diff options
Diffstat (limited to 'libs')
280 files changed, 16196 insertions, 4255 deletions
diff --git a/libs/adbd_auth/Android.bp b/libs/adbd_auth/Android.bp index 16cded8141..8883c0478a 100644 --- a/libs/adbd_auth/Android.bp +++ b/libs/adbd_auth/Android.bp @@ -12,15 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library { name: "libadbd_auth", cflags: [ diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp index b74923cbfc..cdd776480c 100644 --- a/libs/android_runtime_lazy/Android.bp +++ b/libs/android_runtime_lazy/Android.bp @@ -30,15 +30,6 @@ // instead of libandroid_runtime. When they are used by a vendor process, // depending on libandroid_runtime is meaningless. In this case, // they can depend on libandroid_runtime_lazy. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library { name: "libandroid_runtime_lazy", vendor_available: true, diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index bb40f5146e..80aa8916da 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp @@ -12,23 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_applicable_licenses: ["frameworks_native_libs_arect_license"], -} - -// Added automatically by a large-scale-change -// See: http://go/android-license-faq -license { - name: "frameworks_native_libs_arect_license", - visibility: [":__subpackages__"], - license_kinds: [ - "SPDX-license-identifier-Apache-2.0", - ], - license_text: [ - "NOTICE", - ], -} - ndk_headers { name: "libarect_headers_for_ndk", from: "include/android", diff --git a/libs/attestation/Android.bp b/libs/attestation/Android.bp new file mode 100644 index 0000000000..b85aecd16d --- /dev/null +++ b/libs/attestation/Android.bp @@ -0,0 +1,31 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +cc_library_static { + name: "libattestation", + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + srcs: [ + "HmacKeyManager.cpp" + ], + + clang: true, + + shared_libs: [ + "liblog", + "libcrypto", + ], +}
\ No newline at end of file diff --git a/libs/attestation/HmacKeyManager.cpp b/libs/attestation/HmacKeyManager.cpp new file mode 100644 index 0000000000..b15f143014 --- /dev/null +++ b/libs/attestation/HmacKeyManager.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <attestation/HmacKeyManager.h> +#include <log/log.h> +#include <openssl/hmac.h> +#include <openssl/rand.h> + +namespace android { + +static std::array<uint8_t, 128> getRandomKey() { + std::array<uint8_t, 128> key; + if (RAND_bytes(key.data(), key.size()) != 1) { + LOG_ALWAYS_FATAL("Can't generate HMAC key"); + } + return key; +} + +HmacKeyManager::HmacKeyManager() : mHmacKey(getRandomKey()) {} + +std::array<uint8_t, 32> HmacKeyManager::sign(const uint8_t* data, size_t size) const { + // SHA256 always generates 32-bytes result + std::array<uint8_t, 32> hash; + unsigned int hashLen = 0; + uint8_t* result = + HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data, size, hash.data(), &hashLen); + if (result == nullptr) { + ALOGE("Could not sign the data using HMAC"); + return INVALID_HMAC; + } + + if (hashLen != hash.size()) { + ALOGE("HMAC-SHA256 has unexpected length"); + return INVALID_HMAC; + } + + return hash; +} +} // namespace android
\ No newline at end of file diff --git a/libs/attestation/OWNERS b/libs/attestation/OWNERS new file mode 100644 index 0000000000..4dbb0eae5c --- /dev/null +++ b/libs/attestation/OWNERS @@ -0,0 +1,2 @@ +chaviw@google.com +svv@google.com
\ No newline at end of file diff --git a/libs/attestation/TEST_MAPPING b/libs/attestation/TEST_MAPPING new file mode 100644 index 0000000000..43be638d91 --- /dev/null +++ b/libs/attestation/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libattestation_tests" + } + ] +}
\ No newline at end of file diff --git a/libs/attestation/tests/Android.bp b/libs/attestation/tests/Android.bp new file mode 100644 index 0000000000..6ce5ea1b2d --- /dev/null +++ b/libs/attestation/tests/Android.bp @@ -0,0 +1,28 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_test { + name: "libattestation_tests", + test_suites: ["device-tests"], + srcs: [ + "HmacKeyManager_test.cpp", + ], + static_libs: [ + "libattestation", + ], + shared_libs: [ + "liblog", + "libcrypto", + ], +} diff --git a/libs/attestation/tests/HmacKeyManager_test.cpp b/libs/attestation/tests/HmacKeyManager_test.cpp new file mode 100644 index 0000000000..7f7a408886 --- /dev/null +++ b/libs/attestation/tests/HmacKeyManager_test.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <attestation/HmacKeyManager.h> +#include <gtest/gtest.h> + +namespace android { + +class HmacKeyManagerTest : public testing::Test { +protected: + HmacKeyManager mHmacKeyManager; +}; + +/** + * Ensure that separate calls to sign the same data are generating the same key. + * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance + * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky + * tests. + */ +TEST_F(HmacKeyManagerTest, GeneratedHmac_IsConsistent) { + std::array<uint8_t, 10> data = {4, 3, 5, 1, 8, 5, 2, 7, 1, 8}; + + std::array<uint8_t, 32> hmac1 = mHmacKeyManager.sign(data.data(), sizeof(data)); + std::array<uint8_t, 32> hmac2 = mHmacKeyManager.sign(data.data(), sizeof(data)); + ASSERT_EQ(hmac1, hmac2); +} + +/** + * Ensure that changes in the hmac verification data produce a different hmac. + */ +TEST_F(HmacKeyManagerTest, GeneratedHmac_ChangesWhenFieldsChange) { + std::array<uint8_t, 10> data = {4, 3, 5, 1, 8, 5, 2, 7, 1, 8}; + std::array<uint8_t, 32> initialHmac = mHmacKeyManager.sign(data.data(), sizeof(data)); + + data[2] = 2; + ASSERT_NE(initialHmac, mHmacKeyManager.sign(data.data(), sizeof(data))); +} + +} // namespace android
\ No newline at end of file diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp index 5e4c98fc7a..e45a656d29 100644 --- a/libs/binder/ActivityManager.cpp +++ b/libs/binder/ActivityManager.cpp @@ -17,6 +17,7 @@ #include <mutex> #include <unistd.h> +#include <android/permission_manager.h> #include <binder/ActivityManager.h> #include <binder/Binder.h> #include <binder/IServiceManager.h> @@ -61,23 +62,27 @@ int ActivityManager::openContentUri(const String16& stringUri) return service != nullptr ? service->openContentUri(stringUri) : -1; } -void ActivityManager::registerUidObserver(const sp<IUidObserver>& observer, +status_t ActivityManager::registerUidObserver(const sp<IUidObserver>& observer, const int32_t event, const int32_t cutpoint, const String16& callingPackage) { sp<IActivityManager> service = getService(); if (service != nullptr) { - service->registerUidObserver(observer, event, cutpoint, callingPackage); + return service->registerUidObserver(observer, event, cutpoint, callingPackage); } + // ActivityManagerService appears dead. Return usual error code for dead service. + return DEAD_OBJECT; } -void ActivityManager::unregisterUidObserver(const sp<IUidObserver>& observer) +status_t ActivityManager::unregisterUidObserver(const sp<IUidObserver>& observer) { sp<IActivityManager> service = getService(); if (service != nullptr) { - service->unregisterUidObserver(observer); + return service->unregisterUidObserver(observer); } + // ActivityManagerService appears dead. Return usual error code for dead service. + return DEAD_OBJECT; } bool ActivityManager::isUidActive(const uid_t uid, const String16& callingPackage) @@ -98,6 +103,18 @@ int32_t ActivityManager::getUidProcessState(const uid_t uid, const String16& cal return PROCESS_STATE_UNKNOWN; } +status_t ActivityManager::checkPermission(const String16& permission, + const pid_t pid, + const uid_t uid, + int32_t* outResult) { + sp<IActivityManager> service = getService(); + if (service != nullptr) { + return service->checkPermission(permission, pid, uid, outResult); + } + // ActivityManagerService appears dead. Return usual error code for dead service. + return DEAD_OBJECT; +} + status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) { sp<IActivityManager> service = getService(); if (service != nullptr) { diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 44fda7931a..b585ad2c7b 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -12,15 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library_headers { name: "libbinder_headers", export_include_dirs: ["include"], @@ -99,9 +90,6 @@ cc_library { // or dessert updates. Instead, apex users should use libbinder_ndk. apex_available: [ "//apex_available:platform", - // TODO(b/166468760) remove these three - "com.android.media.swcodec", - "test_com.android.media.swcodec", ], srcs: [ @@ -130,6 +118,7 @@ cc_library { "TextOutput.cpp", "Utils.cpp", ":libbinder_aidl", + ":activity_manager_procstate_aidl", ], target: { diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp index 1c6b49135d..de42f36d74 100644 --- a/libs/binder/AppOpsManager.cpp +++ b/libs/binder/AppOpsManager.cpp @@ -22,6 +22,7 @@ #include <utils/SystemClock.h> #include <sys/types.h> +#include <private/android_filesystem_config.h> #ifdef LOG_TAG #undef LOG_TAG @@ -100,7 +101,7 @@ int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPa sp<IAppOpsService> service = getService(); int32_t mode = service != nullptr ? service->noteOperation(op, uid, callingPackage, attributionTag, - shouldCollectNotes(op), message) + shouldCollectNotes(op), message, uid == AID_SYSTEM) : AppOpsManager::MODE_IGNORED; return mode; @@ -118,7 +119,8 @@ int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& c sp<IAppOpsService> service = getService(); int32_t mode = service != nullptr ? service->startOperation(getClientId(), op, uid, callingPackage, - attributionTag, startIfModeDefault, shouldCollectNotes(op), message) + attributionTag, startIfModeDefault, shouldCollectNotes(op), message, + uid == AID_SYSTEM) : AppOpsManager::MODE_IGNORED; return mode; diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp index b81995c6ac..08169f5538 100644 --- a/libs/binder/IActivityManager.cpp +++ b/libs/binder/IActivityManager.cpp @@ -17,9 +17,11 @@ #include <unistd.h> #include <fcntl.h> +#include <android/permission_manager.h> #include <binder/ActivityManager.h> #include <binder/IActivityManager.h> #include <binder/Parcel.h> +#include <utils/Errors.h> namespace android { @@ -57,7 +59,7 @@ public: return fd; } - virtual void registerUidObserver(const sp<IUidObserver>& observer, + virtual status_t registerUidObserver(const sp<IUidObserver>& observer, const int32_t event, const int32_t cutpoint, const String16& callingPackage) @@ -68,15 +70,23 @@ public: data.writeInt32(event); data.writeInt32(cutpoint); data.writeString16(callingPackage); - remote()->transact(REGISTER_UID_OBSERVER_TRANSACTION, data, &reply); + status_t err = remote()->transact(REGISTER_UID_OBSERVER_TRANSACTION, data, &reply); + if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) { + return err; + } + return OK; } - virtual void unregisterUidObserver(const sp<IUidObserver>& observer) + virtual status_t unregisterUidObserver(const sp<IUidObserver>& observer) { Parcel data, reply; data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor()); data.writeStrongBinder(IInterface::asBinder(observer)); - remote()->transact(UNREGISTER_UID_OBSERVER_TRANSACTION, data, &reply); + status_t err = remote()->transact(UNREGISTER_UID_OBSERVER_TRANSACTION, data, &reply); + if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) { + return err; + } + return OK; } virtual bool isUidActive(const uid_t uid, const String16& callingPackage) @@ -104,6 +114,23 @@ public: } return reply.readInt32(); } + + virtual status_t checkPermission(const String16& permission, + const pid_t pid, + const uid_t uid, + int32_t* outResult) { + Parcel data, reply; + data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor()); + data.writeString16(permission); + data.writeInt32(pid); + data.writeInt32(uid); + status_t err = remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply); + if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) { + return err; + } + *outResult = reply.readInt32(); + return NO_ERROR; + } }; // ------------------------------------------------------------------------------------ diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp index 1af5ab8719..18979697f8 100644 --- a/libs/binder/IAppOpsService.cpp +++ b/libs/binder/IAppOpsService.cpp @@ -50,15 +50,16 @@ public: virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName, const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp, - const String16& message) { + const String16& message, bool shouldCollectMessage) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeInt32(code); data.writeInt32(uid); data.writeString16(packageName); data.writeString16(attributionTag); - data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0); + data.writeBool(shouldCollectAsyncNotedOp); data.writeString16(message); + data.writeBool(shouldCollectMessage); remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return MODE_ERRORED; @@ -67,7 +68,8 @@ public: virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid, const String16& packageName, const std::optional<String16>& attributionTag, - bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) { + bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message, + bool shouldCollectMessage) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeStrongBinder(token); @@ -75,9 +77,10 @@ public: data.writeInt32(uid); data.writeString16(packageName); data.writeString16(attributionTag); - data.writeInt32(startIfModeDefault ? 1 : 0); - data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0); + data.writeBool(startIfModeDefault); + data.writeBool(shouldCollectAsyncNotedOp); data.writeString16(message); + data.writeBool(shouldCollectMessage); remote()->transact(START_OPERATION_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return MODE_ERRORED; @@ -186,10 +189,11 @@ status_t BnAppOpsService::onTransact( String16 packageName = data.readString16(); std::optional<String16> attributionTag; data.readString16(&attributionTag); - bool shouldCollectAsyncNotedOp = data.readInt32() == 1; + bool shouldCollectAsyncNotedOp = data.readBool(); String16 message = data.readString16(); + bool shouldCollectMessage = data.readBool(); int32_t res = noteOperation(code, uid, packageName, attributionTag, - shouldCollectAsyncNotedOp, message); + shouldCollectAsyncNotedOp, message, shouldCollectMessage); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; @@ -202,11 +206,12 @@ status_t BnAppOpsService::onTransact( String16 packageName = data.readString16(); std::optional<String16> attributionTag; data.readString16(&attributionTag); - bool startIfModeDefault = data.readInt32() == 1; - bool shouldCollectAsyncNotedOp = data.readInt32() == 1; + bool startIfModeDefault = data.readBool(); + bool shouldCollectAsyncNotedOp = data.readBool(); String16 message = data.readString16(); + bool shouldCollectMessage = data.readBool(); int32_t res = startOperation(token, code, uid, packageName, attributionTag, - startIfModeDefault, shouldCollectAsyncNotedOp, message); + startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl index dc8d74c052..889e15a7e9 100644 --- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl +++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl @@ -101,4 +101,11 @@ interface IPackageManagerNative { * This does nothing if this observer was not already registered. */ void unregisterPackageChangeObserver(in IPackageChangeObserver observer); + + /** + * Returns true if the package has the SHA 256 version of the signing certificate. + * @see PackageManager#hasSigningCertificate(String, byte[], int), where type + * has been set to {@link PackageManager#CERT_INPUT_SHA256}. + */ + boolean hasSha256SigningCertificate(in @utf8InCpp String packageName, in byte[] certificate); } diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h index b90dc862ce..830971b1e6 100644 --- a/libs/binder/include/binder/ActivityManager.h +++ b/libs/binder/include/binder/ActivityManager.h @@ -19,12 +19,16 @@ #ifndef __ANDROID_VNDK__ #include <binder/IActivityManager.h> +#include <android/app/ProcessStateEnum.h> #include <utils/threads.h> // --------------------------------------------------------------------------- namespace android { +#define DECLARE_PROCESS_STATE(name) \ + PROCESS_STATE_##name = (int32_t) app::ProcessStateEnum::name + class ActivityManager { public: @@ -40,45 +44,46 @@ public: UID_OBSERVER_ACTIVE = 1<<3 }; + // PROCESS_STATE_* must come from frameworks/base/core/java/android/app/ProcessStateEnum.aidl. + // This is to make sure that Java side uses the same values as native. enum { - PROCESS_STATE_UNKNOWN = -1, - PROCESS_STATE_PERSISTENT = 0, - PROCESS_STATE_PERSISTENT_UI = 1, - PROCESS_STATE_TOP = 2, - PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3, - PROCESS_STATE_BOUND_TOP = 4, - PROCESS_STATE_FOREGROUND_SERVICE = 5, - PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 6, - PROCESS_STATE_IMPORTANT_FOREGROUND = 7, - PROCESS_STATE_IMPORTANT_BACKGROUND = 8, - PROCESS_STATE_TRANSIENT_BACKGROUND = 9, - PROCESS_STATE_BACKUP = 10, - PROCESS_STATE_SERVICE = 11, - PROCESS_STATE_RECEIVER = 12, - PROCESS_STATE_TOP_SLEEPING = 13, - PROCESS_STATE_HEAVY_WEIGHT = 14, - PROCESS_STATE_HOME = 15, - PROCESS_STATE_LAST_ACTIVITY = 16, - PROCESS_STATE_CACHED_ACTIVITY = 17, - PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 18, - PROCESS_STATE_CACHED_RECENT = 19, - PROCESS_STATE_CACHED_EMPTY = 20, - PROCESS_STATE_NONEXISTENT = 21, + DECLARE_PROCESS_STATE(UNKNOWN), + DECLARE_PROCESS_STATE(PERSISTENT), + DECLARE_PROCESS_STATE(PERSISTENT_UI), + DECLARE_PROCESS_STATE(TOP), + DECLARE_PROCESS_STATE(BOUND_TOP), + DECLARE_PROCESS_STATE(FOREGROUND_SERVICE), + DECLARE_PROCESS_STATE(BOUND_FOREGROUND_SERVICE), + DECLARE_PROCESS_STATE(IMPORTANT_FOREGROUND), + DECLARE_PROCESS_STATE(IMPORTANT_BACKGROUND), + DECLARE_PROCESS_STATE(TRANSIENT_BACKGROUND), + DECLARE_PROCESS_STATE(BACKUP), + DECLARE_PROCESS_STATE(SERVICE), + DECLARE_PROCESS_STATE(RECEIVER), + DECLARE_PROCESS_STATE(TOP_SLEEPING), + DECLARE_PROCESS_STATE(HEAVY_WEIGHT), + DECLARE_PROCESS_STATE(HOME), + DECLARE_PROCESS_STATE(LAST_ACTIVITY), + DECLARE_PROCESS_STATE(CACHED_ACTIVITY), + DECLARE_PROCESS_STATE(CACHED_ACTIVITY_CLIENT), + DECLARE_PROCESS_STATE(CACHED_RECENT), + DECLARE_PROCESS_STATE(CACHED_EMPTY), + DECLARE_PROCESS_STATE(NONEXISTENT), }; ActivityManager(); int openContentUri(const String16& stringUri); - void registerUidObserver(const sp<IUidObserver>& observer, + status_t registerUidObserver(const sp<IUidObserver>& observer, const int32_t event, const int32_t cutpoint, const String16& callingPackage); - void unregisterUidObserver(const sp<IUidObserver>& observer); + status_t unregisterUidObserver(const sp<IUidObserver>& observer); bool isUidActive(const uid_t uid, const String16& callingPackage); int getUidProcessState(const uid_t uid, const String16& callingPackage); + status_t checkPermission(const String16& permission, const pid_t pid, const uid_t uid, int32_t* outResult); - - status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient); + status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient); status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient); private: diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h index 35c697e3d2..f1085cf362 100644 --- a/libs/binder/include/binder/AppOpsManager.h +++ b/libs/binder/include/binder/AppOpsManager.h @@ -136,7 +136,9 @@ public: OP_PHONE_CALL_MICROPHONE = 100, OP_PHONE_CALL_CAMERA = 101, OP_RECORD_AUDIO_HOTWORD = 102, - _NUM_OP = 103 + // Ops 103-105 are currently unused in native, and intentionally omitted + OP_RECORD_AUDIO_OUTPUT = 106, + _NUM_OP = 107 }; AppOpsManager(); diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h index fde56a01a4..2d58c462c2 100644 --- a/libs/binder/include/binder/IActivityManager.h +++ b/libs/binder/include/binder/IActivityManager.h @@ -31,20 +31,25 @@ public: DECLARE_META_INTERFACE(ActivityManager) virtual int openContentUri(const String16& stringUri) = 0; - virtual void registerUidObserver(const sp<IUidObserver>& observer, + virtual status_t registerUidObserver(const sp<IUidObserver>& observer, const int32_t event, const int32_t cutpoint, const String16& callingPackage) = 0; - virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0; + virtual status_t unregisterUidObserver(const sp<IUidObserver>& observer) = 0; virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0; virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0; + virtual status_t checkPermission(const String16& permission, + const pid_t pid, + const uid_t uid, + int32_t* outResult) = 0; enum { OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, REGISTER_UID_OBSERVER_TRANSACTION, UNREGISTER_UID_OBSERVER_TRANSACTION, IS_UID_ACTIVE_TRANSACTION, - GET_UID_PROCESS_STATE_TRANSACTION + GET_UID_PROCESS_STATE_TRANSACTION, + CHECK_PERMISSION_TRANSACTION, }; }; diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h index b0719d4ebc..22f056b235 100644 --- a/libs/binder/include/binder/IAppOpsService.h +++ b/libs/binder/include/binder/IAppOpsService.h @@ -37,10 +37,11 @@ public: 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, const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp, - const String16& message) = 0; + const String16& message, bool shouldCollectMessage) = 0; virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid, const String16& packageName, const std::optional<String16>& attributionTag, - bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0; + bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message, + bool shouldCollectMessage) = 0; virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid, const String16& packageName, const std::optional<String16>& attributionTag) = 0; virtual void startWatchingMode(int32_t op, const String16& packageName, diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index f4a21ddd86..f930d2905e 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -240,23 +240,11 @@ constexpr const char* const kManualInterfaces[] = { "android.hardware.ICameraRecordingProxyListener", "android.hardware.ICrypto", "android.hardware.IOMXObserver", - "android.hardware.ISoundTrigger", - "android.hardware.ISoundTriggerClient", - "android.hardware.ISoundTriggerHwService", "android.hardware.IStreamListener", "android.hardware.IStreamSource", - "android.input.IInputFlinger", - "android.input.ISetInputWindowsListener", - "android.media.IAudioFlinger", - "android.media.IAudioFlingerClient", - "android.media.IAudioPolicyService", - "android.media.IAudioPolicyServiceClient", "android.media.IAudioService", - "android.media.IAudioTrack", "android.media.IDataSource", "android.media.IDrmClient", - "android.media.IEffect", - "android.media.IEffectClient", "android.media.IMediaCodecList", "android.media.IMediaDrmService", "android.media.IMediaExtractor", @@ -280,7 +268,6 @@ constexpr const char* const kManualInterfaces[] = { "android.os.IComplexTypeInterface", "android.os.IPermissionController", "android.os.IPingResponder", - "android.os.IPowerManager", "android.os.IProcessInfoService", "android.os.ISchedulingPolicyService", "android.os.IStringConstants", diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index eb103d3d77..897c72a406 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -15,23 +15,6 @@ */ // TODO(b/31559095): bionic on host should define this -package { - default_applicable_licenses: ["frameworks_native_libs_binder_ndk_license"], -} - -// Added automatically by a large-scale-change -// See: http://go/android-license-faq -license { - name: "frameworks_native_libs_binder_ndk_license", - visibility: [":__subpackages__"], - license_kinds: [ - "SPDX-license-identifier-Apache-2.0", - ], - license_text: [ - "NOTICE", - ], -} - cc_defaults { name: "libbinder_ndk_host_user", target: { diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index 0f59de4309..3c906819ff 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -181,7 +181,7 @@ status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parce binder_status_t status = getClass()->onTransact(this, code, &in, &out); return PruneStatusT(status); - } else if (code == SHELL_COMMAND_TRANSACTION) { + } else if (code == SHELL_COMMAND_TRANSACTION && getClass()->handleShellCommand != nullptr) { int in = data.readFileDescriptor(); int out = data.readFileDescriptor(); int err = data.readFileDescriptor(); diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h index 22cacb4e08..6824306fbf 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -116,13 +116,13 @@ struct AIBinder_Class { const char* getInterfaceDescriptorUtf8() const { return mInterfaceDescriptor.c_str(); } // required to be non-null, implemented for every class - const AIBinder_Class_onCreate onCreate; - const AIBinder_Class_onDestroy onDestroy; - const AIBinder_Class_onTransact onTransact; + const AIBinder_Class_onCreate onCreate = nullptr; + const AIBinder_Class_onDestroy onDestroy = nullptr; + const AIBinder_Class_onTransact onTransact = nullptr; // optional methods for a class - AIBinder_onDump onDump; - AIBinder_handleShellCommand handleShellCommand; + AIBinder_onDump onDump = nullptr; + AIBinder_handleShellCommand handleShellCommand = nullptr; private: // Copy of the raw char string for when we don't have to return UTF-16 diff --git a/libs/binder/ndk/tests/Android.bp b/libs/binder/ndk/tests/Android.bp index bb51bf0b5d..46e6270eb0 100644 --- a/libs/binder/ndk/tests/Android.bp +++ b/libs/binder/ndk/tests/Android.bp @@ -14,15 +14,6 @@ * limitations under the License. */ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_libs_binder_ndk_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_libs_binder_ndk_license"], -} - cc_defaults { name: "test_libbinder_ndk_defaults", shared_libs: [ diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp index 53b5c3c320..2afe5d2058 100644 --- a/libs/binder/ndk/tests/iface.cpp +++ b/libs/binder/ndk/tests/iface.cpp @@ -118,7 +118,7 @@ IFoo::~IFoo() { AIBinder_Weak_delete(mWeakBinder); } -binder_status_t IFoo::addService(const char* instance) { +AIBinder* IFoo::getBinder() { AIBinder* binder = nullptr; if (mWeakBinder != nullptr) { @@ -132,8 +132,18 @@ binder_status_t IFoo::addService(const char* instance) { AIBinder_Weak_delete(mWeakBinder); } mWeakBinder = AIBinder_Weak_new(binder); + + // WARNING: it is important that this class does not implement debug or + // shell functions because it does not use special C++ wrapper + // functions, and so this is how we test those functions. } + return binder; +} + +binder_status_t IFoo::addService(const char* instance) { + AIBinder* binder = getBinder(); + binder_status_t status = AServiceManager_addService(binder, instance); // Strong references we care about kept by remote process AIBinder_decStrong(binder); diff --git a/libs/binder/ndk/tests/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h index 244c9857ac..7408d0c5a9 100644 --- a/libs/binder/ndk/tests/include/iface/iface.h +++ b/libs/binder/ndk/tests/include/iface/iface.h @@ -31,6 +31,9 @@ class IFoo : public virtual ::android::RefBase { static AIBinder_Class* kClass; + // binder representing this interface with one reference count + AIBinder* getBinder(); + // Takes ownership of IFoo binder_status_t addService(const char* instance); static ::android::sp<IFoo> getService(const char* instance, AIBinder** outBinder = nullptr); diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index 7f725e0bfb..de1a48dfd8 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -241,6 +241,26 @@ TEST(NdkBinder, CheckServiceThatDoesExist) { AIBinder_decStrong(binder); } +TEST(NdkBinder, UnimplementedDump) { + sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName); + ASSERT_NE(foo, nullptr); + AIBinder* binder = foo->getBinder(); + EXPECT_EQ(OK, AIBinder_dump(binder, STDOUT_FILENO, nullptr, 0)); + AIBinder_decStrong(binder); +} + +TEST(NdkBinder, UnimplementedShell) { + // libbinder_ndk doesn't support calling shell, so we are calling from the + // libbinder across processes to the NDK service which doesn't implement + // shell + static const sp<android::IServiceManager> sm(android::defaultServiceManager()); + sp<IBinder> testService = sm->getService(String16(IFoo::kSomeInstanceName)); + + Vector<String16> argsVec; + EXPECT_EQ(OK, IBinder::shellCommand(testService, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, + argsVec, nullptr, nullptr)); +} + TEST(NdkBinder, DoubleNumber) { sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName); ASSERT_NE(foo, nullptr); diff --git a/libs/binder/parcel_fuzzer/Android.bp b/libs/binder/parcel_fuzzer/Android.bp index 74b8eb8d93..3e6fe99541 100644 --- a/libs/binder/parcel_fuzzer/Android.bp +++ b/libs/binder/parcel_fuzzer/Android.bp @@ -1,12 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_fuzz { name: "binder_parcel_fuzzer", defaults: ["libbinder_ndk_host_user"], diff --git a/libs/binder/parcel_fuzzer/binder.cpp b/libs/binder/parcel_fuzzer/binder.cpp index 394d222c67..624def1a7a 100644 --- a/libs/binder/parcel_fuzzer/binder.cpp +++ b/libs/binder/parcel_fuzzer/binder.cpp @@ -145,6 +145,13 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { FUZZ_LOG() << "read c-str: " << (str ? str : "<empty string>"); }, PARCEL_READ_OPT_STATUS(android::String8, readString8), + [] (const ::android::Parcel& p, uint8_t /*data*/) { + FUZZ_LOG() << "about to readString8Inplace"; + size_t outLen = 0; + const char* str = p.readString8Inplace(&outLen); + std::string bytes = hexString(str, sizeof(char) * (outLen + 1)); + FUZZ_LOG() << "readString8Inplace: " << bytes << " size: " << outLen; + }, PARCEL_READ_OPT_STATUS(android::String16, readString16), PARCEL_READ_WITH_STATUS(std::unique_ptr<android::String16>, readString16), PARCEL_READ_WITH_STATUS(std::optional<android::String16>, readString16), @@ -152,8 +159,8 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { FUZZ_LOG() << "about to readString16Inplace"; size_t outLen = 0; const char16_t* str = p.readString16Inplace(&outLen); - FUZZ_LOG() << "readString16Inplace: " << hexString(str, sizeof(char16_t) * outLen) - << " size: " << outLen; + std::string bytes = hexString(str, sizeof(char16_t) * (outLen + 1)); + FUZZ_LOG() << "readString16Inplace: " << bytes << " size: " << outLen; }, PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readStrongBinder), PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readNullableStrongBinder), diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index e12a429cf9..f804f1416a 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -1,12 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - rust_library { name: "libbinder_rs", crate_name: "binder", diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp index 0bf76c696a..8810b5dd16 100644 --- a/libs/binder/rust/tests/Android.bp +++ b/libs/binder/rust/tests/Android.bp @@ -1,12 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - rust_test { name: "rustBinderTest", srcs: ["integration.rs"], diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 3bbb0b52bb..259417a655 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -14,15 +14,6 @@ // limitations under the License. // -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_defaults { name: "binder_test_defaults", cflags: [ diff --git a/libs/binder/tests/binderParcelTest.cpp b/libs/binder/tests/binderParcelTest.cpp index 17642281c3..841d47b264 100644 --- a/libs/binder/tests/binderParcelTest.cpp +++ b/libs/binder/tests/binderParcelTest.cpp @@ -25,6 +25,40 @@ using android::String16; using android::String8; using android::status_t; +TEST(Parcel, NonNullTerminatedString8) { + String8 kTestString = String8("test-is-good"); + + // write non-null terminated string + Parcel p; + p.writeString8(kTestString); + p.setDataPosition(0); + // BAD! assumption of wire format for test + // write over length of string + p.writeInt32(kTestString.size() - 2); + + p.setDataPosition(0); + String8 output; + EXPECT_NE(OK, p.readString8(&output)); + EXPECT_EQ(output.size(), 0); +} + +TEST(Parcel, NonNullTerminatedString16) { + String16 kTestString = String16("test-is-good"); + + // write non-null terminated string + Parcel p; + p.writeString16(kTestString); + p.setDataPosition(0); + // BAD! assumption of wire format for test + // write over length of string + p.writeInt32(kTestString.size() - 2); + + p.setDataPosition(0); + String16 output; + EXPECT_NE(OK, p.readString16(&output)); + EXPECT_EQ(output.size(), 0); +} + // Tests a second operation results in a parcel at the same location as it // started. void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) { diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/fuzzers/Android.bp index b1263e8d8e..5531296edb 100644 --- a/libs/binder/tests/fuzzers/Android.bp +++ b/libs/binder/tests/fuzzers/Android.bp @@ -14,15 +14,6 @@ // limitations under the License. // -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_defaults { name: "binder_fuzz_defaults", host_supported: true, diff --git a/libs/binderdebug/Android.bp b/libs/binderdebug/Android.bp index 3eeaf3e4f1..343246a324 100644 --- a/libs/binderdebug/Android.bp +++ b/libs/binderdebug/Android.bp @@ -12,15 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library { name: "libbinderdebug", vendor_available: true, diff --git a/libs/binderdebug/tests/Android.bp b/libs/binderdebug/tests/Android.bp index d141a05ca9..4c06b1d0d9 100644 --- a/libs/binderdebug/tests/Android.bp +++ b/libs/binderdebug/tests/Android.bp @@ -12,15 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_test { name: "libbinderdebug_test", test_suites: ["general-tests"], diff --git a/libs/binderthreadstate/1.0/Android.bp b/libs/binderthreadstate/1.0/Android.bp index 99477d8e26..ebdc932591 100644 --- a/libs/binderthreadstate/1.0/Android.bp +++ b/libs/binderthreadstate/1.0/Android.bp @@ -1,14 +1,5 @@ // This file is autogenerated by hidl-gen -Landroidbp. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - hidl_interface { name: "binderthreadstateutilstest@1.0", root: "binderthreadstateutilstest", diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp index 0a82463feb..08c62df072 100644 --- a/libs/binderthreadstate/Android.bp +++ b/libs/binderthreadstate/Android.bp @@ -14,15 +14,6 @@ // DO NOT ADD NEW USAGES OF THIS // See comments in header file. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library_static { name: "libbinderthreadstateutils", double_loadable: true, diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp index c5d3a3207c..bab267466c 100644 --- a/libs/bufferqueueconverter/Android.bp +++ b/libs/bufferqueueconverter/Android.bp @@ -1,12 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library_headers { name: "libbufferqueueconverter_headers", vendor_available: true, diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp index 570af71d9a..e3cd0859cb 100644 --- a/libs/cputimeinstate/Android.bp +++ b/libs/cputimeinstate/Android.bp @@ -1,12 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library { name: "libtimeinstate", srcs: ["cputimeinstate.cpp"], @@ -44,3 +35,4 @@ cc_test { ], require_root: true, } + diff --git a/libs/diskusage/Android.bp b/libs/diskusage/Android.bp index 86840613e6..a8263069de 100644 --- a/libs/diskusage/Android.bp +++ b/libs/diskusage/Android.bp @@ -12,15 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library_static { name: "libdiskusage", srcs: ["dirsize.c"], diff --git a/libs/dumputils/Android.bp b/libs/dumputils/Android.bp index acda402993..e403d36da1 100644 --- a/libs/dumputils/Android.bp +++ b/libs/dumputils/Android.bp @@ -12,15 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library { name: "libdumputils", diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 8b3c3ad8b5..8a82c2abdf 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -33,6 +33,7 @@ static const char* native_processes_to_dump[] = { "/system/bin/mediaextractor", // media.extractor "/system/bin/mediametrics", // media.metrics "/system/bin/mediaserver", + "/system/bin/mediatranscoding", // media.transcoding "/system/bin/netd", "/system/bin/sdcard", "/apex/com.android.os.statsd/bin/statsd", @@ -86,7 +87,7 @@ static std::set<const std::string> extra_hal_interfaces_to_dump; static void read_extra_hals_to_dump_from_property() { // extra hals to dump are already filled - if (extra_hal_interfaces_to_dump.size() > 0) { + if (!extra_hal_interfaces_to_dump.empty()) { return; } std::string value = android::base::GetProperty("ro.dump.hals.extra", ""); diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp index 47c0657bbd..76518c1286 100644 --- a/libs/fakeservicemanager/Android.bp +++ b/libs/fakeservicemanager/Android.bp @@ -1,12 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_defaults { name: "fakeservicemanager_defaults", host_supported: true, diff --git a/libs/ftl/.clang-format b/libs/ftl/.clang-format new file mode 120000 index 0000000000..86b159339f --- /dev/null +++ b/libs/ftl/.clang-format @@ -0,0 +1 @@ +../../../../build/soong/scripts/system-clang-format-2
\ No newline at end of file diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp new file mode 100644 index 0000000000..5bccaca42d --- /dev/null +++ b/libs/ftl/Android.bp @@ -0,0 +1,19 @@ +cc_test { + name: "ftl_test", + test_suites: ["device-tests"], + sanitize: { + address: true, + }, + srcs: [ + "future_test.cpp", + "small_map_test.cpp", + "small_vector_test.cpp", + "static_vector_test.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + "-Wpedantic", + ], +} diff --git a/libs/ftl/README.md b/libs/ftl/README.md new file mode 100644 index 0000000000..bdd750f40a --- /dev/null +++ b/libs/ftl/README.md @@ -0,0 +1,41 @@ +# FTL + +FTL is a template library shared by SurfaceFlinger and InputFlinger, inspired by +and supplementing the C++ Standard Library. The intent is to fill gaps for areas +not (yet) covered—like cache-efficient data structures and lock-free concurrency +primitives—and implement proposals that are missing or experimental in Android's +libc++ branch. The design takes some liberties with standard compliance, notably +assuming that exceptions are disabled. + +## Tests + + atest ftl_test + +## Style + +- Based on [Google C++ Style](https://google.github.io/styleguide/cppguide.html). +- Informed by [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines). + +Naming conventions are as follows: + +- `PascalCase` + - Types and aliases, except standard interfaces. + - Template parameters, including non-type ones. +- `snake_case` + - Variables, and data members with trailing underscore. + - Functions, free and member alike. + - Type traits, with standard `_t` and `_v` suffixes. +- `kCamelCase` + - Enumerators and `constexpr` constants with static storage duration. +- `MACRO_CASE` + - Macros, with `FTL_` prefix unless `#undef`ed. + +Template parameter packs are named with the following convention: + + typename T, typename... Ts + typename Arg, typename... Args + + std::size_t I, std::size_t... Is + std::size_t Size, std::size_t... Sizes + +The `details` namespace contains implementation details. diff --git a/libs/ftl/future_test.cpp b/libs/ftl/future_test.cpp new file mode 100644 index 0000000000..9b3e93683f --- /dev/null +++ b/libs/ftl/future_test.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ftl/future.h> +#include <gtest/gtest.h> + +#include <algorithm> +#include <future> +#include <string> +#include <thread> +#include <vector> + +namespace android::test { + +// Keep in sync with example usage in header file. +TEST(Future, Example) { + { + auto future = ftl::defer([](int x) { return x + 1; }, 99); + EXPECT_EQ(future.get(), 100); + } + { + auto future = ftl::yield(42); + EXPECT_EQ(future.get(), 42); + } + { + auto ptr = std::make_unique<char>('!'); + auto future = ftl::yield(std::move(ptr)); + EXPECT_EQ(*future.get(), '!'); + } + { + auto future = ftl::yield(123); + std::future<char> futures[] = {ftl::yield('a'), ftl::yield('b')}; + + std::future<char> chain = ftl::chain(std::move(future)) + .then([](int x) { return static_cast<size_t>(x % 2); }) + .then([&futures](size_t i) { return std::move(futures[i]); }); + + EXPECT_EQ(chain.get(), 'b'); + } +} + +namespace { + +using ByteVector = std::vector<uint8_t>; + +ByteVector decrement(ByteVector bytes) { + std::transform(bytes.begin(), bytes.end(), bytes.begin(), [](auto b) { return b - 1; }); + return bytes; +} + +} // namespace + +TEST(Future, Chain) { + std::packaged_task<const char*()> fetch_string([] { return "ifmmp-"; }); + + std::packaged_task<ByteVector(std::string)> append_string([](std::string str) { + str += "!xpsme"; + return ByteVector{str.begin(), str.end()}; + }); + + std::packaged_task<std::future<ByteVector>(ByteVector)> decrement_bytes( + [](ByteVector bytes) { return ftl::defer(decrement, std::move(bytes)); }); + + auto fetch = fetch_string.get_future(); + std::thread fetch_thread(std::move(fetch_string)); + + std::thread append_thread, decrement_thread; + + EXPECT_EQ( + "hello, world", + ftl::chain(std::move(fetch)) + .then([](const char* str) { return std::string(str); }) + .then([&](std::string str) { + auto append = append_string.get_future(); + append_thread = std::thread(std::move(append_string), std::move(str)); + return append; + }) + .then([&](ByteVector bytes) { + auto decrement = decrement_bytes.get_future(); + decrement_thread = std::thread(std::move(decrement_bytes), std::move(bytes)); + return decrement; + }) + .then([](std::future<ByteVector> bytes) { return bytes; }) + .then([](const ByteVector& bytes) { return std::string(bytes.begin(), bytes.end()); }) + .get()); + + fetch_thread.join(); + append_thread.join(); + decrement_thread.join(); +} + +} // namespace android::test diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp new file mode 100644 index 0000000000..323b9f91e7 --- /dev/null +++ b/libs/ftl/small_map_test.cpp @@ -0,0 +1,131 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ftl/small_map.h> +#include <gtest/gtest.h> + +#include <cctype> + +namespace android::test { + +using ftl::SmallMap; + +// Keep in sync with example usage in header file. +TEST(SmallMap, Example) { + ftl::SmallMap<int, std::string, 3> map; + EXPECT_TRUE(map.empty()); + EXPECT_FALSE(map.dynamic()); + + map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?'); + EXPECT_EQ(map.size(), 3u); + EXPECT_FALSE(map.dynamic()); + + EXPECT_TRUE(map.contains(123)); + + EXPECT_EQ(map.find(42, [](const std::string& s) { return s.size(); }), 3u); + + const auto opt = map.find(-1); + ASSERT_TRUE(opt); + + std::string& ref = *opt; + EXPECT_TRUE(ref.empty()); + ref = "xyz"; + + EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc"))); +} + +TEST(SmallMap, Construct) { + { + // Default constructor. + SmallMap<int, std::string, 2> map; + + EXPECT_TRUE(map.empty()); + EXPECT_FALSE(map.dynamic()); + } + { + // In-place constructor with same types. + SmallMap<int, std::string, 5> map = + ftl::init::map<int, std::string>(123, "abc")(456, "def")(789, "ghi"); + + EXPECT_EQ(map.size(), 3u); + EXPECT_EQ(map.max_size(), 5u); + EXPECT_FALSE(map.dynamic()); + + EXPECT_EQ(map, SmallMap(ftl::init::map(123, "abc")(456, "def")(789, "ghi"))); + } + { + // In-place constructor with different types. + SmallMap<int, std::string, 5> map = + ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?'); + + EXPECT_EQ(map.size(), 3u); + EXPECT_EQ(map.max_size(), 5u); + EXPECT_FALSE(map.dynamic()); + + EXPECT_EQ(map, SmallMap(ftl::init::map(42, "???")(123, "abc")(-1, "\0\0\0"))); + } + { + // In-place constructor with implicit size. + SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?'); + + static_assert(std::is_same_v<decltype(map), SmallMap<int, std::string, 3>>); + EXPECT_EQ(map.size(), 3u); + EXPECT_EQ(map.max_size(), 3u); + EXPECT_FALSE(map.dynamic()); + + EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "\0\0\0")(42, "???")(123, "abc"))); + } +} + +TEST(SmallMap, Find) { + { + // Constant reference. + const ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); + + const auto opt = map.find('b'); + EXPECT_EQ(opt, 'B'); + + const char d = 'D'; + const auto ref = map.find('d').value_or(std::cref(d)); + EXPECT_EQ(ref.get(), 'D'); + } + { + // Mutable reference. + ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); + + const auto opt = map.find('c'); + EXPECT_EQ(opt, 'C'); + + char d = 'd'; + const auto ref = map.find('d').value_or(std::ref(d)); + ref.get() = 'D'; + EXPECT_EQ(d, 'D'); + } + { + // Constant unary operation. + const ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); + EXPECT_EQ(map.find('c', [](char c) { return std::toupper(c); }), 'Z'); + } + { + // Mutable unary operation. + ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); + EXPECT_TRUE(map.find('c', [](char& c) { c = std::toupper(c); })); + + EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x'))); + } +} + +} // namespace android::test diff --git a/libs/ftl/small_vector_test.cpp b/libs/ftl/small_vector_test.cpp new file mode 100644 index 0000000000..3a03e696d1 --- /dev/null +++ b/libs/ftl/small_vector_test.cpp @@ -0,0 +1,463 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ftl/small_vector.h> +#include <gtest/gtest.h> + +#include <algorithm> +#include <iterator> +#include <string> +#include <utility> + +using namespace std::string_literals; + +namespace android::test { + +using ftl::SmallVector; + +// Keep in sync with example usage in header file. +TEST(SmallVector, Example) { + ftl::SmallVector<char, 3> vector; + EXPECT_TRUE(vector.empty()); + EXPECT_FALSE(vector.dynamic()); + + vector = {'a', 'b', 'c'}; + EXPECT_EQ(vector.size(), 3u); + EXPECT_FALSE(vector.dynamic()); + + vector.push_back('d'); + EXPECT_TRUE(vector.dynamic()); + + vector.unstable_erase(vector.begin()); + EXPECT_EQ(vector, (ftl::SmallVector{'d', 'b', 'c'})); + + vector.pop_back(); + EXPECT_EQ(vector.back(), 'b'); + EXPECT_TRUE(vector.dynamic()); + + const char array[] = "hi"; + vector = ftl::SmallVector(array); + EXPECT_EQ(vector, (ftl::SmallVector{'h', 'i', '\0'})); + EXPECT_FALSE(vector.dynamic()); + + ftl::SmallVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?'); + ASSERT_EQ(strings.size(), 3u); + EXPECT_FALSE(strings.dynamic()); + + EXPECT_EQ(strings[0], "abc"); + EXPECT_EQ(strings[1], "123"); + EXPECT_EQ(strings[2], "???"); +} + +TEST(SmallVector, Construct) { + { + // Default constructor. + SmallVector<std::string, 2> vector; + + EXPECT_TRUE(vector.empty()); + EXPECT_FALSE(vector.dynamic()); + } + { + // Array constructor. + const float floats[] = {.1f, .2f, .3f}; + SmallVector vector(floats); + + EXPECT_EQ(vector, (SmallVector{.1f, .2f, .3f})); + EXPECT_FALSE(vector.dynamic()); + } + { + // Iterator constructor. + const char chars[] = "abcdef"; + std::string string(chars); + SmallVector<char, sizeof(chars)> vector(string.begin(), string.end()); + + EXPECT_STREQ(vector.begin(), chars); + EXPECT_FALSE(vector.dynamic()); + } + { + // Variadic constructor with same types. + SmallVector vector = {1, 2, 3}; + + static_assert(std::is_same_v<decltype(vector), SmallVector<int, 3>>); + EXPECT_EQ(vector, (SmallVector{1, 2, 3})); + EXPECT_FALSE(vector.dynamic()); + } + { + // Variadic constructor with different types. + const auto copy = "quince"s; + auto move = "tart"s; + SmallVector vector = {copy, std::move(move)}; + + static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 2>>); + EXPECT_EQ(vector, (SmallVector{"quince"s, "tart"s})); + EXPECT_FALSE(vector.dynamic()); + } + { + // In-place constructor with same types. + SmallVector vector = + ftl::init::list<std::string>("redolent", 3u)("velveteen", 6u)("cakewalk", 4u); + + static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>); + EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s})); + EXPECT_FALSE(vector.dynamic()); + } + { + // In-place constructor with different types. + const auto copy = "red"s; + auto move = "velvet"s; + std::initializer_list<char> list = {'c', 'a', 'k', 'e'}; + SmallVector vector = ftl::init::list<std::string>(copy.c_str())(std::move(move))(list); + + static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>); + EXPECT_TRUE(move.empty()); + EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s})); + EXPECT_FALSE(vector.dynamic()); + } + { + // Conversion from StaticVector. + ftl::StaticVector doubles = {.1, .2, .3}; + SmallVector vector = std::move(doubles); + EXPECT_TRUE(doubles.empty()); + + static_assert(std::is_same_v<decltype(vector), SmallVector<double, 3>>); + EXPECT_EQ(vector, (SmallVector{.1, .2, .3})); + EXPECT_FALSE(vector.dynamic()); + } +} + +TEST(SmallVector, String) { + SmallVector<char, 10> chars; + char c = 'a'; + std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; }); + chars.push_back('\0'); + + EXPECT_TRUE(chars.dynamic()); + EXPECT_EQ(chars.size(), 11u); + EXPECT_STREQ(chars.begin(), "abcdefghij"); + + // Constructor takes iterator range. + const char numbers[] = "123456"; + SmallVector<char, 10> string(std::begin(numbers), std::end(numbers)); + + EXPECT_FALSE(string.dynamic()); + EXPECT_STREQ(string.begin(), "123456"); + EXPECT_EQ(string.size(), 7u); + + // Similar to emplace, but replaces rather than inserts. + string.replace(string.begin() + 5, '\0'); + EXPECT_STREQ(string.begin(), "12345"); + + swap(chars, string); + + EXPECT_STREQ(chars.begin(), "12345"); + EXPECT_STREQ(string.begin(), "abcdefghij"); + + EXPECT_FALSE(chars.dynamic()); + EXPECT_TRUE(string.dynamic()); +} + +TEST(SmallVector, CopyableElement) { + struct Pair { + // Needed because std::vector does not use list initialization to emplace. + Pair(int a, int b) : a(a), b(b) {} + + const int a, b; + bool operator==(Pair p) const { return p.a == a && p.b == b; } + }; + + SmallVector<Pair, 5> pairs; + + EXPECT_TRUE(pairs.empty()); + EXPECT_EQ(pairs.max_size(), 5u); + + for (size_t i = 0; i < pairs.max_size(); ++i) { + EXPECT_EQ(pairs.size(), i); + + const int a = static_cast<int>(i) * 2; + EXPECT_EQ(pairs.emplace_back(a, a + 1), Pair(a, a + 1)); + } + + EXPECT_EQ(pairs.size(), 5u); + EXPECT_FALSE(pairs.dynamic()); + + // The vector is promoted when full. + EXPECT_EQ(pairs.emplace_back(10, 11), Pair(10, 11)); + EXPECT_TRUE(pairs.dynamic()); + + EXPECT_EQ(pairs, (SmallVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9}, + Pair{10, 11}})); + + // Constructor takes at most N elements. + SmallVector<int, 6> sums = {0, 0, 0, 0, 0, 0}; + EXPECT_FALSE(sums.dynamic()); + + // Random-access iterators comply with standard. + std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; }); + EXPECT_EQ(sums, (SmallVector{1, 5, 9, 13, 17, 21})); + + sums.pop_back(); + std::reverse(sums.begin(), sums.end()); + + EXPECT_EQ(sums, (SmallVector{17, 13, 9, 5, 1})); +} + +TEST(SmallVector, MovableElement) { + // Construct std::string elements in place from per-element arguments. + SmallVector strings = ftl::init::list<std::string>()()()("cake")("velvet")("red")(); + strings.pop_back(); + + EXPECT_EQ(strings.max_size(), 7u); + EXPECT_EQ(strings.size(), 6u); + + // Erase "cake" and append a substring copy. + { + const auto it = + std::find_if(strings.begin(), strings.end(), [](const auto& s) { return !s.empty(); }); + ASSERT_FALSE(it == strings.end()); + EXPECT_EQ(*it, "cake"); + + // Construct std::string from first 4 characters of string literal. + strings.unstable_erase(it); + EXPECT_EQ(strings.emplace_back("cakewalk", 4u), "cake"s); + } + + strings[1] = "quince"s; + + // Replace last empty string with "tart". + { + const auto rit = std::find(strings.rbegin(), strings.rend(), std::string()); + ASSERT_FALSE(rit == strings.rend()); + + std::initializer_list<char> list = {'t', 'a', 'r', 't'}; + strings.replace(rit.base() - 1, list); + } + + strings.front().assign("pie"); + + EXPECT_EQ(strings, (SmallVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s})); + + strings.push_back("nougat"); + strings.push_back("oreo"); + EXPECT_TRUE(strings.dynamic()); + + std::rotate(strings.begin(), strings.end() - 2, strings.end()); + + EXPECT_EQ(strings, (SmallVector{"nougat"s, "oreo"s, "pie"s, "quince"s, "tart"s, "red"s, "velvet"s, + "cake"s})); +} + +TEST(SmallVector, Replace) { + // Replacing does not require a copy/move assignment operator. + struct Word { + explicit Word(std::string str) : str(std::move(str)) {} + const std::string str; + + bool operator==(const Word& other) const { return other.str == str; } + }; + + SmallVector words = ftl::init::list<Word>("colored")("velour"); + + // The replaced element can be referenced by the replacement. + { + const Word& word = words.replace(words.last(), words.back().str.substr(0, 3) + "vet"); + EXPECT_EQ(word, Word("velvet")); + } + + // The vector is not promoted if replacing while full. + EXPECT_FALSE(words.dynamic()); + + words.emplace_back("cake"); + EXPECT_TRUE(words.dynamic()); + + { + const Word& word = words.replace(words.begin(), words.front().str.substr(4)); + EXPECT_EQ(word, Word("red")); + } + + EXPECT_EQ(words, (SmallVector{Word("red"), Word("velvet"), Word("cake")})); +} + +TEST(SmallVector, ReverseAppend) { + SmallVector strings = {"red"s, "velvet"s, "cake"s}; + EXPECT_FALSE(strings.dynamic()); + + auto rit = strings.rbegin(); + while (rit != strings.rend()) { + // Iterator and reference are invalidated on insertion. + const auto i = std::distance(strings.begin(), rit.base()); + std::string s = *rit; + + strings.push_back(std::move(s)); + rit = std::make_reverse_iterator(strings.begin() + i) + 1; + } + + EXPECT_EQ(strings, (SmallVector{"red"s, "velvet"s, "cake"s, "cake"s, "velvet"s, "red"s})); + EXPECT_TRUE(strings.dynamic()); +} + +TEST(SmallVector, Sort) { + SmallVector strings = ftl::init::list<std::string>("pie")("quince")("tart")("red")("velvet"); + strings.push_back("cake"s); + + auto sorted = std::move(strings); + EXPECT_TRUE(strings.empty()); + + EXPECT_TRUE(sorted.dynamic()); + EXPECT_TRUE(strings.dynamic()); + + std::sort(sorted.begin(), sorted.end()); + EXPECT_EQ(sorted, (SmallVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s})); + + // Constructor takes array reference. + { + const char* array[] = {"cake", "lie"}; + strings = SmallVector(array); + EXPECT_FALSE(strings.dynamic()); + } + + EXPECT_GT(sorted, strings); + swap(sorted, strings); + EXPECT_LT(sorted, strings); + + EXPECT_FALSE(sorted.dynamic()); + EXPECT_TRUE(strings.dynamic()); + + // Append remaining elements, such that "pie" is the only difference. + for (const char* str : {"quince", "red", "tart", "velvet"}) { + sorted.emplace_back(str); + } + EXPECT_TRUE(sorted.dynamic()); + + EXPECT_NE(sorted, strings); + + // Replace second element with "pie". + const auto it = sorted.begin() + 1; + EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie"); + + EXPECT_EQ(sorted, strings); +} + +namespace { + +struct DestroyCounts { + DestroyCounts(int& live, int& dead) : counts{live, dead} {} + DestroyCounts(const DestroyCounts& other) : counts(other.counts) {} + DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; } + ~DestroyCounts() { ++(alive ? counts.live : counts.dead); } + + struct { + int& live; + int& dead; + } counts; + + bool alive = true; +}; + +void swap(DestroyCounts& lhs, DestroyCounts& rhs) { + std::swap(lhs.alive, rhs.alive); +} + +} // namespace + +TEST(SmallVector, Destroy) { + int live = 0; + int dead = 0; + + { SmallVector<DestroyCounts, 3> counts; } + EXPECT_EQ(0, live); + EXPECT_EQ(0, dead); + + { + SmallVector<DestroyCounts, 3> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + + EXPECT_FALSE(counts.dynamic()); + } + EXPECT_EQ(3, live); + EXPECT_EQ(0, dead); + + live = 0; + { + SmallVector<DestroyCounts, 3> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + + EXPECT_TRUE(counts.dynamic()); + } + EXPECT_EQ(4, live); + EXPECT_EQ(3, dead); + + live = dead = 0; + { + SmallVector<DestroyCounts, 2> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + + auto copy = counts; + EXPECT_TRUE(copy.dynamic()); + } + EXPECT_EQ(6, live); + EXPECT_EQ(2, dead); + + live = dead = 0; + { + SmallVector<DestroyCounts, 2> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + + auto move = std::move(counts); + EXPECT_TRUE(move.dynamic()); + } + EXPECT_EQ(3, live); + EXPECT_EQ(2, dead); + + live = dead = 0; + { + SmallVector<DestroyCounts, 2> counts1; + counts1.emplace_back(live, dead); + counts1.emplace_back(live, dead); + counts1.emplace_back(live, dead); + + EXPECT_TRUE(counts1.dynamic()); + EXPECT_EQ(2, dead); + dead = 0; + + SmallVector<DestroyCounts, 2> counts2; + counts2.emplace_back(live, dead); + + EXPECT_FALSE(counts2.dynamic()); + + swap(counts1, counts2); + + EXPECT_FALSE(counts1.dynamic()); + EXPECT_TRUE(counts2.dynamic()); + + EXPECT_EQ(0, live); + EXPECT_EQ(1, dead); + + dead = 0; + } + EXPECT_EQ(4, live); + EXPECT_EQ(0, dead); +} + +} // namespace android::test diff --git a/libs/ftl/static_vector_test.cpp b/libs/ftl/static_vector_test.cpp new file mode 100644 index 0000000000..cbe8dff527 --- /dev/null +++ b/libs/ftl/static_vector_test.cpp @@ -0,0 +1,399 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ftl/static_vector.h> +#include <gtest/gtest.h> + +#include <algorithm> +#include <iterator> +#include <string> +#include <utility> + +using namespace std::string_literals; + +namespace android::test { + +using ftl::StaticVector; + +// Keep in sync with example usage in header file. +TEST(StaticVector, Example) { + ftl::StaticVector<char, 3> vector; + EXPECT_TRUE(vector.empty()); + + vector = {'a', 'b'}; + EXPECT_EQ(vector.size(), 2u); + + vector.push_back('c'); + EXPECT_TRUE(vector.full()); + + EXPECT_FALSE(vector.push_back('d')); + EXPECT_EQ(vector.size(), 3u); + + vector.unstable_erase(vector.begin()); + EXPECT_EQ(vector, (ftl::StaticVector{'c', 'b'})); + + vector.pop_back(); + EXPECT_EQ(vector.back(), 'c'); + + const char array[] = "hi"; + vector = ftl::StaticVector(array); + EXPECT_EQ(vector, (ftl::StaticVector{'h', 'i', '\0'})); + + ftl::StaticVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?'); + ASSERT_EQ(strings.size(), 3u); + + EXPECT_EQ(strings[0], "abc"); + EXPECT_EQ(strings[1], "123"); + EXPECT_EQ(strings[2], "???"); +} + +TEST(StaticVector, Construct) { + { + // Default constructor. + StaticVector<std::string, 2> vector; + EXPECT_TRUE(vector.empty()); + } + { + // Array constructor. + const float floats[] = {.1f, .2f, .3f}; + StaticVector vector(floats); + EXPECT_EQ(vector, (StaticVector{.1f, .2f, .3f})); + } + { + // Iterator constructor. + const char chars[] = "abcdef"; + std::string string(chars); + StaticVector<char, sizeof(chars)> vector(string.begin(), string.end()); + + EXPECT_STREQ(vector.begin(), chars); + } + { + // Variadic constructor with same types. + StaticVector vector = {1, 2, 3}; + + static_assert(std::is_same_v<decltype(vector), StaticVector<int, 3>>); + EXPECT_EQ(vector, (StaticVector{1, 2, 3})); + } + { + // Variadic constructor with different types. + const auto copy = "quince"s; + auto move = "tart"s; + StaticVector vector = {copy, std::move(move)}; + + static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 2>>); + EXPECT_EQ(vector, (StaticVector{"quince"s, "tart"s})); + } + { + // In-place constructor with same types. + StaticVector vector = + ftl::init::list<std::string>("redolent", 3u)("velveteen", 6u)("cakewalk", 4u); + + static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>); + EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s})); + } + { + // In-place constructor with different types. + const auto copy = "red"s; + auto move = "velvet"s; + std::initializer_list<char> list = {'c', 'a', 'k', 'e'}; + StaticVector vector = ftl::init::list<std::string>(copy.c_str())(std::move(move))(list); + + static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>); + EXPECT_TRUE(move.empty()); + EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s})); + } + { + struct String { + explicit String(const char* str) : str(str) {} + explicit String(const char** ptr) : str(*ptr) {} + const char* str; + }; + + const char* strings[] = {"a", "b", "c", "d"}; + + { + // Two iterator-like elements. + StaticVector<String, 3> vector(strings, strings + 3); + ASSERT_EQ(vector.size(), 2u); + + EXPECT_STREQ(vector[0].str, "a"); + EXPECT_STREQ(vector[1].str, "d"); + } + { + // Disambiguating iterator constructor. + StaticVector<String, 3> vector(ftl::kIteratorRange, strings, strings + 3); + ASSERT_EQ(vector.size(), 3u); + + EXPECT_STREQ(vector[0].str, "a"); + EXPECT_STREQ(vector[1].str, "b"); + EXPECT_STREQ(vector[2].str, "c"); + } + } +} + +TEST(StaticVector, String) { + StaticVector<char, 10> chars; + char c = 'a'; + std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; }); + chars.back() = '\0'; + + EXPECT_STREQ(chars.begin(), "abcdefghi"); + + // Constructor takes iterator range. + const char numbers[] = "123456"; + StaticVector<char, 10> string(std::begin(numbers), std::end(numbers)); + + EXPECT_STREQ(string.begin(), "123456"); + EXPECT_EQ(string.size(), 7u); + + // Similar to emplace, but replaces rather than inserts. + string.replace(string.begin() + 5, '\0'); + EXPECT_STREQ(string.begin(), "12345"); + + swap(chars, string); + + EXPECT_STREQ(chars.begin(), "12345"); + EXPECT_STREQ(string.begin(), "abcdefghi"); +} + +TEST(StaticVector, CopyableElement) { + struct Pair { + const int a, b; + bool operator==(Pair p) const { return p.a == a && p.b == b; } + }; + + StaticVector<Pair, 5> pairs; + + EXPECT_TRUE(pairs.empty()); + EXPECT_EQ(pairs.max_size(), 5u); + + for (size_t i = 0; i < pairs.max_size(); ++i) { + EXPECT_EQ(pairs.size(), i); + + const int a = static_cast<int>(i) * 2; + const auto it = pairs.emplace_back(a, a + 1); + ASSERT_NE(it, pairs.end()); + EXPECT_EQ(*it, (Pair{a, a + 1})); + } + + EXPECT_TRUE(pairs.full()); + EXPECT_EQ(pairs.size(), 5u); + + // Insertion fails if the vector is full. + const auto it = pairs.emplace_back(10, 11); + EXPECT_EQ(it, pairs.end()); + + EXPECT_EQ(pairs, (StaticVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9}})); + + // Constructor takes at most N elements. + StaticVector<int, 6> sums = {0, 0, 0, 0, 0, -1}; + EXPECT_TRUE(sums.full()); + + // Random-access iterators comply with standard. + std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; }); + EXPECT_EQ(sums, (StaticVector{1, 5, 9, 13, 17, -1})); + + sums.pop_back(); + std::reverse(sums.begin(), sums.end()); + + EXPECT_EQ(sums, (StaticVector{17, 13, 9, 5, 1})); +} + +TEST(StaticVector, MovableElement) { + // Construct std::string elements in place from per-element arguments. + StaticVector strings = ftl::init::list<std::string>()()()("cake")("velvet")("red")(); + strings.pop_back(); + + EXPECT_EQ(strings.max_size(), 7u); + EXPECT_EQ(strings.size(), 6u); + + // Erase "cake" and append a substring copy. + { + auto it = + std::find_if(strings.begin(), strings.end(), [](const auto& s) { return !s.empty(); }); + ASSERT_FALSE(it == strings.end()); + EXPECT_EQ(*it, "cake"); + + strings.unstable_erase(it); + + // Construct std::string from first 4 characters of string literal. + it = strings.emplace_back("cakewalk", 4u); + ASSERT_NE(it, strings.end()); + EXPECT_EQ(*it, "cake"s); + } + + strings[1] = "quince"s; + + // Replace last empty string with "tart". + { + const auto rit = std::find(strings.rbegin(), strings.rend(), std::string()); + ASSERT_FALSE(rit == strings.rend()); + + std::initializer_list<char> list = {'t', 'a', 'r', 't'}; + strings.replace(rit.base() - 1, list); + } + + strings.front().assign("pie"); + + EXPECT_EQ(strings, (StaticVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s})); +} + +TEST(StaticVector, Replace) { + // Replacing does not require a copy/move assignment operator. + struct Word { + explicit Word(std::string str) : str(std::move(str)) {} + const std::string str; + }; + + StaticVector words = ftl::init::list<Word>("red")("velour")("cake"); + + // The replaced element can be referenced by the replacement. + const auto it = words.begin() + 1; + const Word& word = words.replace(it, it->str.substr(0, 3) + "vet"); + EXPECT_EQ(word.str, "velvet"); +} + +TEST(StaticVector, ReverseTruncate) { + StaticVector<std::string, 10> strings("pie", "quince", "tart", "red", "velvet", "cake"); + EXPECT_FALSE(strings.full()); + + for (auto it = strings.begin(); it != strings.end(); ++it) { + strings.replace(it, strings.back()); + strings.pop_back(); + } + + EXPECT_EQ(strings, (StaticVector{"cake"s, "velvet"s, "red"s})); +} + +TEST(StaticVector, Sort) { + StaticVector<std::string, 7> strings("pie", "quince", "tart", "red", "velvet", "cake"); + EXPECT_FALSE(strings.full()); + + auto sorted = std::move(strings); + EXPECT_TRUE(strings.empty()); + + std::sort(sorted.begin(), sorted.end()); + EXPECT_EQ(sorted, (StaticVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s})); + + // Constructor takes array reference. + { + const char* array[] = {"cake", "lie"}; + strings = StaticVector(array); + } + + EXPECT_GT(sorted, strings); + swap(sorted, strings); + EXPECT_LT(sorted, strings); + + // Append remaining elements, such that "pie" is the only difference. + for (const char* str : {"quince", "red", "tart", "velvet"}) { + sorted.emplace_back(str); + } + + EXPECT_NE(sorted, strings); + + // Replace second element with "pie". + const auto it = sorted.begin() + 1; + EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie"); + + EXPECT_EQ(sorted, strings); +} + +namespace { + +struct DestroyCounts { + DestroyCounts(int& live, int& dead) : counts{live, dead} {} + DestroyCounts(const DestroyCounts& other) : counts(other.counts) {} + DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; } + ~DestroyCounts() { ++(alive ? counts.live : counts.dead); } + + struct { + int& live; + int& dead; + } counts; + + bool alive = true; +}; + +void swap(DestroyCounts& lhs, DestroyCounts& rhs) { + std::swap(lhs.alive, rhs.alive); +} + +} // namespace + +TEST(StaticVector, Destroy) { + int live = 0; + int dead = 0; + + { StaticVector<DestroyCounts, 5> counts; } + EXPECT_EQ(0, live); + EXPECT_EQ(0, dead); + + { + StaticVector<DestroyCounts, 5> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + } + EXPECT_EQ(3, live); + EXPECT_EQ(0, dead); + + live = 0; + { + StaticVector<DestroyCounts, 5> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + + auto copy = counts; + } + EXPECT_EQ(6, live); + EXPECT_EQ(0, dead); + + live = 0; + { + StaticVector<DestroyCounts, 5> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + + auto move = std::move(counts); + } + EXPECT_EQ(3, live); + EXPECT_EQ(3, dead); + + live = dead = 0; + { + StaticVector<DestroyCounts, 5> counts1; + counts1.emplace_back(live, dead); + counts1.emplace_back(live, dead); + counts1.emplace_back(live, dead); + + StaticVector<DestroyCounts, 5> counts2; + counts2.emplace_back(live, dead); + + swap(counts1, counts2); + + EXPECT_EQ(0, live); + EXPECT_EQ(2, dead); + + dead = 0; + } + EXPECT_EQ(4, live); + EXPECT_EQ(0, dead); +} + +} // namespace android::test diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp index a0032aecb9..dd0ae30703 100644 --- a/libs/gralloc/types/Android.bp +++ b/libs/gralloc/types/Android.bp @@ -12,15 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library { name: "libgralloctypes", defaults: ["libbinder_ndk_host_user"], diff --git a/libs/gralloc/types/fuzzer/Android.bp b/libs/gralloc/types/fuzzer/Android.bp index 6689771a24..8933dc323f 100644 --- a/libs/gralloc/types/fuzzer/Android.bp +++ b/libs/gralloc/types/fuzzer/Android.bp @@ -1,12 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_fuzz { name: "libgralloctypes_fuzzer", defaults: ["libbinder_ndk_host_user"], diff --git a/libs/gralloc/types/tests/Android.bp b/libs/gralloc/types/tests/Android.bp index 66eb0aa2fe..b939c1db59 100644 --- a/libs/gralloc/types/tests/Android.bp +++ b/libs/gralloc/types/tests/Android.bp @@ -14,15 +14,6 @@ // limitations under the License. // -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_test { name: "GrallocTypes_test", shared_libs: [ diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp index a96a07a9b8..642c5f2cc0 100644 --- a/libs/graphicsenv/Android.bp +++ b/libs/graphicsenv/Android.bp @@ -12,15 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library_shared { name: "libgraphicsenv", diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index 3d0f8bbb11..d54de4999c 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -29,7 +29,6 @@ #include <android-base/strings.h> #include <android/dlext.h> #include <binder/IServiceManager.h> -#include <cutils/properties.h> #include <graphicsenv/IGpuService.h> #include <log/log.h> #include <nativeloader/dlext_namespaces.h> @@ -74,7 +73,7 @@ static constexpr const char* kNativeLibrariesSystemConfigPath[] = static std::string vndkVersionStr() { #ifdef __BIONIC__ - return android::base::GetProperty("ro.vndk.version", ""); + return base::GetProperty("ro.vndk.version", ""); #endif return ""; } @@ -345,10 +344,8 @@ void* GraphicsEnv::loadLibrary(std::string name) { } bool GraphicsEnv::checkAngleRules(void* so) { - char manufacturer[PROPERTY_VALUE_MAX]; - char model[PROPERTY_VALUE_MAX]; - property_get("ro.product.manufacturer", manufacturer, "UNSET"); - property_get("ro.product.model", model, "UNSET"); + auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET"); + auto model = base::GetProperty("ro.product.model", "UNSET"); auto ANGLEGetFeatureSupportUtilAPIVersion = (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so, @@ -401,7 +398,8 @@ bool GraphicsEnv::checkAngleRules(void* so) { ALOGW("ANGLE feature-support library cannot obtain SystemInfo"); break; } - if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, systemInfoHandle)) { + if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(), + systemInfoHandle)) { ALOGW("ANGLE feature-support library cannot add device info to SystemInfo"); break; } @@ -468,7 +466,8 @@ void GraphicsEnv::updateUseAngle() { } void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName, - const std::string developerOptIn, const int rulesFd, + const std::string developerOptIn, + const std::vector<std::string> eglFeatures, const int rulesFd, const long rulesOffset, const long rulesLength) { if (mUseAngle != UNKNOWN) { // We've already figured out an answer for this app, so just return. @@ -477,6 +476,8 @@ void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName return; } + mAngleEglFeatures = std::move(eglFeatures); + ALOGV("setting ANGLE path to '%s'", path.c_str()); mAnglePath = path; ALOGV("setting ANGLE app name to '%s'", appName.c_str()); @@ -522,6 +523,10 @@ std::string& GraphicsEnv::getAngleAppName() { return mAngleAppName; } +const std::vector<std::string>& GraphicsEnv::getAngleEglFeatures() { + return mAngleEglFeatures; +} + const std::string& GraphicsEnv::getLayerPaths() { return mLayerPaths; } @@ -543,7 +548,7 @@ void GraphicsEnv::setDebugLayersGLES(const std::string layers) { } // Return true if all the required libraries from vndk and sphal namespace are -// linked to the Game Driver namespace correctly. +// linked to the updatable gfx driver namespace correctly. bool GraphicsEnv::linkDriverNamespaceLocked(android_namespace_t* vndkNamespace) { const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK); if (llndkLibraries.empty()) { @@ -653,8 +658,7 @@ android_namespace_t* GraphicsEnv::getAngleNamespace() { mAngleNamespace = android_create_namespace("ANGLE", nullptr, // ld_library_path mAnglePath.c_str(), // default_library_path - ANDROID_NAMESPACE_TYPE_SHARED | - ANDROID_NAMESPACE_TYPE_ISOLATED, + ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED, nullptr, // permitted_when_isolated_path nullptr); diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 22a2332589..900fc49b59 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -97,12 +97,15 @@ public: // in the search path must have a '!' after the zip filename, e.g. // /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn, - const int rulesFd, const long rulesOffset, const long rulesLength); + const std::vector<std::string> eglFeatures, const int rulesFd, + const long rulesOffset, const long rulesLength); // Get the ANGLE driver namespace. android_namespace_t* getAngleNamespace(); // Get the app name for ANGLE debug message. std::string& getAngleAppName(); + const std::vector<std::string>& getAngleEglFeatures(); + /* * Apis for debug layer */ @@ -154,6 +157,8 @@ private: std::string mAngleAppName; // ANGLE developer opt in status. std::string mAngleDeveloperOptIn; + // ANGLE EGL features; + std::vector<std::string> mAngleEglFeatures; // ANGLE rules. std::vector<char> mRulesBuffer; // Use ANGLE flag. diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 904cc6db5a..fa5044cc16 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -11,15 +11,6 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library_headers { name: "libgui_headers", vendor_available: true, @@ -39,6 +30,12 @@ cc_library_headers { min_sdk_version: "29", } +filegroup { + name: "libgui_aidl", + srcs: ["aidl/**/*.aidl"], + path: "aidl/", +} + cc_library_shared { name: "libgui", vendor_available: true, @@ -52,6 +49,8 @@ cc_library_shared { srcs: [ ":framework_native_aidl", + ":inputconstants_aidl", + ":libgui_aidl", ":libgui_bufferqueue_sources", "BitTube.cpp", @@ -64,8 +63,8 @@ cc_library_shared { "DebugEGLImageTracker.cpp", "DisplayEventDispatcher.cpp", "DisplayEventReceiver.cpp", + "FrameTimelineInfo.cpp", "GLConsumer.cpp", - "GuiConfig.cpp", "IConsumerListener.cpp", "IDisplayEventConnection.cpp", "IGraphicBufferConsumer.cpp", @@ -80,10 +79,12 @@ cc_library_shared { "LayerState.cpp", "OccupancyTracker.cpp", "StreamSplitter.cpp", + "ScreenCaptureResults.cpp", "Surface.cpp", "SurfaceControl.cpp", "SurfaceComposerClient.cpp", "SyncFeatures.cpp", + "TransactionTracing.cpp", "view/Surface.cpp", "bufferqueue/1.0/B2HProducerListener.cpp", "bufferqueue/1.0/H2BGraphicBufferProducer.cpp", @@ -102,6 +103,7 @@ cc_library_shared { export_shared_lib_headers: [ "libbinder", + "libinput", ], // bufferhub is not used when building libgui for vendors @@ -154,6 +156,8 @@ cc_library_static { defaults: ["libgui_bufferqueue-defaults"], srcs: [ + ":inputconstants_aidl", + ":libgui_aidl", ":libgui_bufferqueue_sources", ], } @@ -161,6 +165,7 @@ cc_library_static { filegroup { name: "libgui_bufferqueue_sources", srcs: [ + "BatchBufferOps.cpp", "BufferItem.cpp", "BufferQueue.cpp", "BufferQueueConsumer.cpp", @@ -171,7 +176,7 @@ filegroup { "FrameTimestamps.cpp", "GLConsumerUtils.cpp", "HdrMetadata.cpp", - "QueueBufferInputOutput.cpp", + "IGraphicBufferProducerFlattenables.cpp", "bufferqueue/1.0/Conversion.cpp", "bufferqueue/1.0/H2BProducerListener.cpp", "bufferqueue/1.0/WProducerListener.cpp", @@ -237,6 +242,10 @@ cc_defaults { "libnativebase_headers", ], + include_dirs: [ + "frameworks/native/include", + ], + export_shared_lib_headers: [ "libEGL", "libnativewindow", diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 56591bdc63..1e6fc2b217 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -18,10 +18,17 @@ #define LOG_TAG "BLASTBufferQueue" #define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 #include <gui/BLASTBufferQueue.h> #include <gui/BufferItemConsumer.h> +#include <gui/BufferQueueConsumer.h> +#include <gui/BufferQueueCore.h> +#include <gui/BufferQueueProducer.h> #include <gui/GLConsumer.h> +#include <gui/IProducerListener.h> +#include <gui/Surface.h> +#include <utils/Singleton.h> #include <utils/Trace.h> @@ -29,8 +36,24 @@ using namespace std::chrono_literals; +namespace { +inline const char* toString(bool b) { + return b ? "true" : "false"; +} +} // namespace + namespace android { +// Macros to include adapter info in log messages +#define BQA_LOGV(x, ...) \ + ALOGV("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__) +// enable logs for a single layer +//#define BQA_LOGV(x, ...) \ +// ALOGV_IF((strstr(mName.c_str(), "SurfaceView") != nullptr), "[%s](f:%u,a:%u) " x, \ +// mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__) +#define BQA_LOGE(x, ...) \ + ALOGE("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__) + void BLASTBufferItemConsumer::onDisconnect() { Mutex::Autolock lock(mFrameEventHistoryMutex); mPreviouslyConnected = mCurrentlyConnected; @@ -93,13 +116,16 @@ void BLASTBufferItemConsumer::getConnectionEvents(uint64_t frameNumber, bool* ne if (needsDisconnect != nullptr) *needsDisconnect = disconnect; } -BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height, +BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, + int width, int height, int32_t format, bool enableTripleBuffering) - : mSurfaceControl(surface), - mWidth(width), - mHeight(height), + : mName(name), + mSurfaceControl(surface), + mSize(width, height), + mRequestedSize(mSize), + mFormat(format), mNextTransaction(nullptr) { - BufferQueue::createBufferQueue(&mProducer, &mConsumer); + createBufferQueue(&mProducer, &mConsumer); // since the adapter is in the client process, set dequeue timeout // explicitly so that dequeueBuffer will block mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max()); @@ -107,19 +133,25 @@ BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, if (enableTripleBuffering) { mProducer->setMaxDequeuedBufferCount(2); } - mBufferItemConsumer = - new BLASTBufferItemConsumer(mConsumer, GraphicBuffer::USAGE_HW_COMPOSER, 1, true); + mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer, + GraphicBuffer::USAGE_HW_COMPOSER | + GraphicBuffer::USAGE_HW_TEXTURE, + 1, false); static int32_t id = 0; - auto name = std::string("BLAST Consumer") + std::to_string(id); + auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id); id++; - mBufferItemConsumer->setName(String8(name.c_str())); + mBufferItemConsumer->setName(String8(consumerName.c_str())); mBufferItemConsumer->setFrameAvailableListener(this); mBufferItemConsumer->setBufferFreedListener(this); - mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight); - mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888); + mBufferItemConsumer->setDefaultBufferSize(mSize.width, mSize.height); + mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format)); mTransformHint = mSurfaceControl->getTransformHint(); mBufferItemConsumer->setTransformHint(mTransformHint); + SurfaceComposerClient::Transaction() + .setFlags(surface, layer_state_t::eEnableBackpressure, + layer_state_t::eEnableBackpressure) + .apply(); mNumAcquired = 0; mNumFrameAvailable = 0; @@ -127,14 +159,52 @@ BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, mPendingReleaseItem.releaseFence = nullptr; } -void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) { +BLASTBufferQueue::~BLASTBufferQueue() { + if (mPendingTransactions.empty()) { + return; + } + BQA_LOGE("Applying pending transactions on dtor %d", + static_cast<uint32_t>(mPendingTransactions.size())); + SurfaceComposerClient::Transaction t; + for (auto& [targetFrameNumber, transaction] : mPendingTransactions) { + t.merge(std::move(transaction)); + } + t.apply(); +} + +void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, + int32_t format) { std::unique_lock _lock{mMutex}; - mSurfaceControl = surface; + if (mFormat != format) { + mFormat = format; + mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format)); + } + + SurfaceComposerClient::Transaction t; + bool applyTransaction = false; + if (!SurfaceControl::isSameSurface(mSurfaceControl, surface)) { + mSurfaceControl = surface; + t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, + layer_state_t::eEnableBackpressure); + applyTransaction = true; + } - if (mWidth != width || mHeight != height) { - mWidth = width; - mHeight = height; - mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight); + ui::Size newSize(width, height); + if (mRequestedSize != newSize) { + mRequestedSize.set(newSize); + mBufferItemConsumer->setDefaultBufferSize(mRequestedSize.width, mRequestedSize.height); + if (mLastBufferScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) { + // If the buffer supports scaling, update the frame immediately since the client may + // want to scale the existing buffer to the new size. + mSize = mRequestedSize; + t.setFrame(mSurfaceControl, + {0, 0, static_cast<int32_t>(mSize.width), + static_cast<int32_t>(mSize.height)}); + applyTransaction = true; + } + } + if (applyTransaction) { + t.apply(); } } @@ -144,63 +214,87 @@ static void transactionCallbackThunk(void* context, nsecs_t latchTime, if (context == nullptr) { return; } - BLASTBufferQueue* bq = static_cast<BLASTBufferQueue*>(context); + sp<BLASTBufferQueue> bq = static_cast<BLASTBufferQueue*>(context); bq->transactionCallback(latchTime, presentFence, stats); } void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/, const std::vector<SurfaceControlStats>& stats) { - std::unique_lock _lock{mMutex}; - ATRACE_CALL(); + std::function<void(int64_t)> transactionCompleteCallback = nullptr; + uint64_t currFrameNumber = 0; + + { + std::unique_lock _lock{mMutex}; + ATRACE_CALL(); + BQA_LOGV("transactionCallback"); + mInitialCallbackReceived = true; - if (!stats.empty()) { - mTransformHint = stats[0].transformHint; - mBufferItemConsumer->setTransformHint(mTransformHint); - mBufferItemConsumer->updateFrameTimestamps(stats[0].frameEventStats.frameNumber, - stats[0].frameEventStats.refreshStartTime, - stats[0].frameEventStats.gpuCompositionDoneFence, - stats[0].presentFence, - stats[0].previousReleaseFence, - stats[0].frameEventStats.compositorTiming, - stats[0].latchTime, - stats[0].frameEventStats.dequeueReadyTime); - } - if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) { if (!stats.empty()) { - mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence; - } else { - ALOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback"); + mTransformHint = stats[0].transformHint; + mBufferItemConsumer->setTransformHint(mTransformHint); + mBufferItemConsumer + ->updateFrameTimestamps(stats[0].frameEventStats.frameNumber, + stats[0].frameEventStats.refreshStartTime, + stats[0].frameEventStats.gpuCompositionDoneFence, + stats[0].presentFence, stats[0].previousReleaseFence, + stats[0].frameEventStats.compositorTiming, + stats[0].latchTime, + stats[0].frameEventStats.dequeueReadyTime); + } + if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) { + if (!stats.empty()) { + mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence; + } else { + BQA_LOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback"); + mPendingReleaseItem.releaseFence = nullptr; + } + mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item, + mPendingReleaseItem.releaseFence + ? mPendingReleaseItem.releaseFence + : Fence::NO_FENCE); + mNumAcquired--; + mPendingReleaseItem.item = BufferItem(); mPendingReleaseItem.releaseFence = nullptr; } - mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item, - mPendingReleaseItem.releaseFence - ? mPendingReleaseItem.releaseFence - : Fence::NO_FENCE); - mNumAcquired--; - mPendingReleaseItem.item = BufferItem(); - mPendingReleaseItem.releaseFence = nullptr; - } - if (mSubmitted.empty()) { - ALOGE("ERROR: callback with no corresponding submitted buffer item"); - } - mPendingReleaseItem.item = std::move(mSubmitted.front()); - mSubmitted.pop(); + if (mSubmitted.empty()) { + BQA_LOGE("ERROR: callback with no corresponding submitted buffer item"); + } + mPendingReleaseItem.item = std::move(mSubmitted.front()); + mSubmitted.pop(); - processNextBufferLocked(false); + processNextBufferLocked(false /* useNextTransaction */); - mCallbackCV.notify_all(); - decStrong((void*)transactionCallbackThunk); + currFrameNumber = mPendingReleaseItem.item.mFrameNumber; + if (mTransactionCompleteCallback && mTransactionCompleteFrameNumber == currFrameNumber) { + transactionCompleteCallback = std::move(mTransactionCompleteCallback); + mTransactionCompleteFrameNumber = 0; + } + + mCallbackCV.notify_all(); + decStrong((void*)transactionCallbackThunk); + } + + if (transactionCompleteCallback) { + transactionCompleteCallback(currFrameNumber); + } } void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { ATRACE_CALL(); - if (mNumFrameAvailable == 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) { + BQA_LOGV("processNextBufferLocked useNextTransaction=%s", toString(useNextTransaction)); + + // If the next transaction is set, we want to guarantee the our acquire will not fail, so don't + // include the extra buffer when checking if we can acquire the next buffer. + const bool includeExtraAcquire = !useNextTransaction; + if (mNumFrameAvailable == 0 || maxBuffersAcquired(includeExtraAcquire)) { + BQA_LOGV("processNextBufferLocked waiting for frame available or callback"); + mCallbackCV.notify_all(); return; } if (mSurfaceControl == nullptr) { - ALOGE("ERROR : surface control is null"); + BQA_LOGE("ERROR : surface control is null"); return; } @@ -215,8 +309,13 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { BufferItem bufferItem; - status_t status = mBufferItemConsumer->acquireBuffer(&bufferItem, -1, false); - if (status != OK) { + status_t status = + mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false); + if (status == BufferQueue::NO_BUFFER_AVAILABLE) { + BQA_LOGV("Failed to acquire a buffer, err=NO_BUFFER_AVAILABLE"); + return; + } else if (status != OK) { + BQA_LOGE("Failed to acquire a buffer, err=%s", statusToString(status).c_str()); return; } auto buffer = bufferItem.mGraphicBuffer; @@ -224,6 +323,17 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { if (buffer == nullptr) { mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE); + BQA_LOGE("Buffer was empty"); + return; + } + + if (rejectBuffer(bufferItem)) { + BQA_LOGE("rejecting buffer:active_size=%dx%d, requested_size=%dx%d" + "buffer{size=%dx%d transform=%d}", + mSize.width, mSize.height, mRequestedSize.width, mRequestedSize.height, + buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform); + mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE); + processNextBufferLocked(useNextTransaction); return; } @@ -241,46 +351,352 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback. incStrong((void*)transactionCallbackThunk); + mLastBufferScalingMode = bufferItem.mScalingMode; + mLastAcquiredFrameNumber = bufferItem.mFrameNumber; + t->setBuffer(mSurfaceControl, buffer); + t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace)); + t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata); + t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage); t->setAcquireFence(mSurfaceControl, bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE); t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this)); - t->setFrame(mSurfaceControl, {0, 0, mWidth, mHeight}); + t->setFrame(mSurfaceControl, + {0, 0, static_cast<int32_t>(mSize.width), static_cast<int32_t>(mSize.height)}); t->setCrop(mSurfaceControl, computeCrop(bufferItem)); t->setTransform(mSurfaceControl, bufferItem.mTransform); t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse); - t->setDesiredPresentTime(bufferItem.mTimestamp); + if (!bufferItem.mIsAutoTimestamp) { + t->setDesiredPresentTime(bufferItem.mTimestamp); + } + t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber); + + if (!mNextFrameTimelineInfoQueue.empty()) { + t->setFrameTimelineInfo(mSurfaceControl, mNextFrameTimelineInfoQueue.front()); + mNextFrameTimelineInfoQueue.pop(); + } + + if (mAutoRefresh != bufferItem.mAutoRefresh) { + t->setAutoRefresh(mSurfaceControl, bufferItem.mAutoRefresh); + mAutoRefresh = bufferItem.mAutoRefresh; + } + { + std::unique_lock _lock{mTimestampMutex}; + auto dequeueTime = mDequeueTimestamps.find(buffer->getId()); + if (dequeueTime != mDequeueTimestamps.end()) { + Parcel p; + p.writeInt64(dequeueTime->second); + t->setMetadata(mSurfaceControl, METADATA_DEQUEUE_TIME, p); + mDequeueTimestamps.erase(dequeueTime); + } + } + + auto mergeTransaction = + [&t, currentFrameNumber = bufferItem.mFrameNumber]( + std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) { + auto& [targetFrameNumber, transaction] = pendingTransaction; + if (currentFrameNumber < targetFrameNumber) { + return false; + } + t->merge(std::move(transaction)); + return true; + }; + + mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(), + mPendingTransactions.end(), mergeTransaction), + mPendingTransactions.end()); if (applyTransaction) { - t->apply(); + t->setApplyToken(mApplyToken).apply(); } + + BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64 + " applyTransaction=%s mTimestamp=%" PRId64 " mPendingTransactions.size=%d", + mSize.width, mSize.height, bufferItem.mFrameNumber, toString(applyTransaction), + bufferItem.mTimestamp, static_cast<uint32_t>(mPendingTransactions.size())); } Rect BLASTBufferQueue::computeCrop(const BufferItem& item) { if (item.mScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) { - return GLConsumer::scaleDownCrop(item.mCrop, mWidth, mHeight); + return GLConsumer::scaleDownCrop(item.mCrop, mSize.width, mSize.height); } return item.mCrop; } -void BLASTBufferQueue::onFrameAvailable(const BufferItem& /*item*/) { +void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { ATRACE_CALL(); std::unique_lock _lock{mMutex}; - if (mNextTransaction != nullptr) { - while (mNumFrameAvailable > 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) { + const bool nextTransactionSet = mNextTransaction != nullptr; + BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s mFlushShadowQueue=%s", + item.mFrameNumber, toString(nextTransactionSet), toString(mFlushShadowQueue)); + + if (nextTransactionSet || mFlushShadowQueue) { + while (mNumFrameAvailable > 0 || maxBuffersAcquired(false /* includeExtraAcquire */)) { + BQA_LOGV("waiting in onFrameAvailable..."); mCallbackCV.wait(_lock); } } + mFlushShadowQueue = false; // add to shadow queue mNumFrameAvailable++; - processNextBufferLocked(true); + processNextBufferLocked(nextTransactionSet /* useNextTransaction */); +} + +void BLASTBufferQueue::onFrameReplaced(const BufferItem& item) { + BQA_LOGV("onFrameReplaced framenumber=%" PRIu64, item.mFrameNumber); + // Do nothing since we are not storing unacquired buffer items locally. } +void BLASTBufferQueue::onFrameDequeued(const uint64_t bufferId) { + std::unique_lock _lock{mTimestampMutex}; + mDequeueTimestamps[bufferId] = systemTime(); +}; + +void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) { + std::unique_lock _lock{mTimestampMutex}; + mDequeueTimestamps.erase(bufferId); +}; + void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) { std::lock_guard _lock{mMutex}; mNextTransaction = t; } +bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) { + if (item.mScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) { + // Only reject buffers if scaling mode is freeze. + return false; + } + + uint32_t bufWidth = item.mGraphicBuffer->getWidth(); + uint32_t bufHeight = item.mGraphicBuffer->getHeight(); + + // Take the buffer's orientation into account + if (item.mTransform & ui::Transform::ROT_90) { + std::swap(bufWidth, bufHeight); + } + ui::Size bufferSize(bufWidth, bufHeight); + if (mRequestedSize != mSize && mRequestedSize == bufferSize) { + mSize = mRequestedSize; + return false; + } + + // reject buffers if the buffer size doesn't match. + return mSize != bufferSize; +} + +void BLASTBufferQueue::setTransactionCompleteCallback( + uint64_t frameNumber, std::function<void(int64_t)>&& transactionCompleteCallback) { + std::lock_guard _lock{mMutex}; + if (transactionCompleteCallback == nullptr) { + mTransactionCompleteCallback = nullptr; + } else { + mTransactionCompleteCallback = std::move(transactionCompleteCallback); + mTransactionCompleteFrameNumber = frameNumber; + } +} + +// Check if we have acquired the maximum number of buffers. +// As a special case, we wait for the first callback before acquiring the second buffer so we +// can ensure the first buffer is presented if multiple buffers are queued in succession. +// Consumer can acquire an additional buffer if that buffer is not droppable. Set +// includeExtraAcquire is true to include this buffer to the count. Since this depends on the state +// of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE. +bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const { + int maxAcquiredBuffers = MAX_ACQUIRED_BUFFERS + (includeExtraAcquire ? 2 : 1); + return mNumAcquired == maxAcquiredBuffers || (!mInitialCallbackReceived && mNumAcquired == 1); +} + +class BBQSurface : public Surface { +private: + sp<BLASTBufferQueue> mBbq; +public: + BBQSurface(const sp<IGraphicBufferProducer>& igbp, bool controlledByApp, + const sp<IBinder>& scHandle, const sp<BLASTBufferQueue>& bbq) + : Surface(igbp, controlledByApp, scHandle), mBbq(bbq) {} + + void allocateBuffers() override { + uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth; + uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight; + auto gbp = getIGraphicBufferProducer(); + std::thread ([reqWidth, reqHeight, gbp=getIGraphicBufferProducer(), + reqFormat=mReqFormat, reqUsage=mReqUsage] () { + gbp->allocateBuffers(reqWidth, reqHeight, + reqFormat, reqUsage); + + }).detach(); + } + + status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) override { + if (!ValidateFrameRate(frameRate, compatibility, "BBQSurface::setFrameRate")) { + return BAD_VALUE; + } + return mBbq->setFrameRate(frameRate, compatibility, shouldBeSeamless); + } + + status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override { + return mBbq->setFrameTimelineInfo(frameTimelineInfo); + } +}; + +// TODO: Can we coalesce this with frame updates? Need to confirm +// no timing issues. +status_t BLASTBufferQueue::setFrameRate(float frameRate, int8_t compatibility, + bool shouldBeSeamless) { + std::unique_lock _lock{mMutex}; + SurfaceComposerClient::Transaction t; + + return t.setFrameRate(mSurfaceControl, frameRate, compatibility, shouldBeSeamless).apply(); +} + +status_t BLASTBufferQueue::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) { + std::unique_lock _lock{mMutex}; + mNextFrameTimelineInfoQueue.push(frameTimelineInfo); + return OK; +} + +sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) { + std::unique_lock _lock{mMutex}; + sp<IBinder> scHandle = nullptr; + if (includeSurfaceControlHandle && mSurfaceControl) { + scHandle = mSurfaceControl->getHandle(); + } + return new BBQSurface(mProducer, true, scHandle, this); +} + +void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, + uint64_t frameNumber) { + std::lock_guard _lock{mMutex}; + if (mLastAcquiredFrameNumber >= frameNumber) { + // Apply the transaction since we have already acquired the desired frame. + t->apply(); + } else { + mPendingTransactions.emplace_back(frameNumber, std::move(*t)); + } +} + +// Maintains a single worker thread per process that services a list of runnables. +class AsyncWorker : public Singleton<AsyncWorker> { +private: + std::thread mThread; + bool mDone = false; + std::deque<std::function<void()>> mRunnables; + std::mutex mMutex; + std::condition_variable mCv; + void run() { + std::unique_lock<std::mutex> lock(mMutex); + while (!mDone) { + mCv.wait(lock); + while (!mRunnables.empty()) { + std::function<void()> runnable = mRunnables.front(); + mRunnables.pop_front(); + runnable(); + } + } + } + +public: + AsyncWorker() : Singleton<AsyncWorker>() { mThread = std::thread(&AsyncWorker::run, this); } + + ~AsyncWorker() { + mDone = true; + mCv.notify_all(); + if (mThread.joinable()) { + mThread.join(); + } + } + + void post(std::function<void()> runnable) { + std::unique_lock<std::mutex> lock(mMutex); + mRunnables.emplace_back(std::move(runnable)); + mCv.notify_one(); + } +}; +ANDROID_SINGLETON_STATIC_INSTANCE(AsyncWorker); + +// Asynchronously calls ProducerListener functions so we can emulate one way binder calls. +class AsyncProducerListener : public BnProducerListener { +private: + const sp<IProducerListener> mListener; + +public: + AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {} + + void onBufferReleased() override { + AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferReleased(); }); + } + + void onBuffersDiscarded(const std::vector<int32_t>& slots) override { + AsyncWorker::getInstance().post( + [listener = mListener, slots = slots]() { listener->onBuffersDiscarded(slots); }); + } +}; + +// Extends the BufferQueueProducer to create a wrapper around the listener so the listener calls +// can be non-blocking when the producer is in the client process. +class BBQBufferQueueProducer : public BufferQueueProducer { +public: + BBQBufferQueueProducer(const sp<BufferQueueCore>& core) + : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/) {} + + status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp, + QueueBufferOutput* output) override { + if (!listener) { + return BufferQueueProducer::connect(listener, api, producerControlledByApp, output); + } + + return BufferQueueProducer::connect(new AsyncProducerListener(listener), api, + producerControlledByApp, output); + } + + int query(int what, int* value) override { + if (what == NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER) { + *value = 1; + return NO_ERROR; + } + return BufferQueueProducer::query(what, value); + } +}; + +// Similar to BufferQueue::createBufferQueue but creates an adapter specific bufferqueue producer. +// This BQP allows invoking client specified ProducerListeners and invoke them asynchronously, +// emulating one way binder call behavior. Without this, if the listener calls back into the queue, +// we can deadlock. +void BLASTBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, + sp<IGraphicBufferConsumer>* outConsumer) { + LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BLASTBufferQueue: outProducer must not be NULL"); + LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BLASTBufferQueue: outConsumer must not be NULL"); + + sp<BufferQueueCore> core(new BufferQueueCore()); + LOG_ALWAYS_FATAL_IF(core == nullptr, "BLASTBufferQueue: failed to create BufferQueueCore"); + + sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core)); + LOG_ALWAYS_FATAL_IF(producer == nullptr, + "BLASTBufferQueue: failed to create BBQBufferQueueProducer"); + + sp<BufferQueueConsumer> consumer(new BufferQueueConsumer(core)); + consumer->setAllowExtraAcquire(true); + LOG_ALWAYS_FATAL_IF(consumer == nullptr, + "BLASTBufferQueue: failed to create BufferQueueConsumer"); + + *outProducer = producer; + *outConsumer = consumer; +} + +PixelFormat BLASTBufferQueue::convertBufferFormat(PixelFormat& format) { + PixelFormat convertedFormat = format; + switch (format) { + case PIXEL_FORMAT_TRANSPARENT: + case PIXEL_FORMAT_TRANSLUCENT: + convertedFormat = PIXEL_FORMAT_RGBA_8888; + break; + case PIXEL_FORMAT_OPAQUE: + convertedFormat = PIXEL_FORMAT_RGBX_8888; + break; + } + return convertedFormat; +} + } // namespace android diff --git a/libs/gui/BatchBufferOps.cpp b/libs/gui/BatchBufferOps.cpp new file mode 100644 index 0000000000..60aceb1bbd --- /dev/null +++ b/libs/gui/BatchBufferOps.cpp @@ -0,0 +1,125 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <inttypes.h> + +#define LOG_TAG "IGBPBatchOps" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 + +#include <gui/IGraphicBufferProducer.h> + +namespace android { + +/** + * Default implementation of batched buffer operations. These default + * implementations call into the non-batched version of the same operation. + */ + +status_t IGraphicBufferProducer::requestBuffers( + const std::vector<int32_t>& slots, + std::vector<RequestBufferOutput>* outputs) { + outputs->clear(); + outputs->reserve(slots.size()); + for (int32_t slot : slots) { + RequestBufferOutput& output = outputs->emplace_back(); + output.result = requestBuffer(static_cast<int>(slot), + &output.buffer); + } + return NO_ERROR; +} + +status_t IGraphicBufferProducer::dequeueBuffers( + const std::vector<DequeueBufferInput>& inputs, + std::vector<DequeueBufferOutput>* outputs) { + outputs->clear(); + outputs->reserve(inputs.size()); + for (const DequeueBufferInput& input : inputs) { + DequeueBufferOutput& output = outputs->emplace_back(); + output.result = dequeueBuffer( + &output.slot, + &output.fence, + input.width, + input.height, + input.format, + input.usage, + &output.bufferAge, + input.getTimestamps ? &output.timestamps.emplace() : nullptr); + } + return NO_ERROR; +} + +status_t IGraphicBufferProducer::detachBuffers( + const std::vector<int32_t>& slots, + std::vector<status_t>* results) { + results->clear(); + results->reserve(slots.size()); + for (int32_t slot : slots) { + results->emplace_back(detachBuffer(slot)); + } + return NO_ERROR; +} + +status_t IGraphicBufferProducer::attachBuffers( + const std::vector<sp<GraphicBuffer>>& buffers, + std::vector<AttachBufferOutput>* outputs) { + outputs->clear(); + outputs->reserve(buffers.size()); + for (const sp<GraphicBuffer>& buffer : buffers) { + AttachBufferOutput& output = outputs->emplace_back(); + output.result = attachBuffer(&output.slot, buffer); + } + return NO_ERROR; +} + +status_t IGraphicBufferProducer::queueBuffers( + const std::vector<QueueBufferInput>& inputs, + std::vector<QueueBufferOutput>* outputs) { + outputs->clear(); + outputs->reserve(inputs.size()); + for (const QueueBufferInput& input : inputs) { + QueueBufferOutput& output = outputs->emplace_back(); + output.result = queueBuffer(input.slot, input, &output); + } + return NO_ERROR; +} + +status_t IGraphicBufferProducer::cancelBuffers( + const std::vector<CancelBufferInput>& inputs, + std::vector<status_t>* results) { + results->clear(); + results->reserve(inputs.size()); + for (const CancelBufferInput& input : inputs) { + results->emplace_back() = cancelBuffer(input.slot, input.fence); + } + return NO_ERROR; +} + +status_t IGraphicBufferProducer::query(const std::vector<int32_t> inputs, + std::vector<QueryOutput>* outputs) { + outputs->clear(); + outputs->reserve(inputs.size()); + for (int32_t input : inputs) { + QueryOutput& output = outputs->emplace_back(); + int value{}; + output.result = static_cast<status_t>( + query(static_cast<int>(input), &value)); + output.value = static_cast<int64_t>(value); + } + return NO_ERROR; +} + +} // namespace android diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index da6143c59f..7f7a0437f1 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -94,7 +94,10 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, ++numAcquiredBuffers; } } - if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) { + const bool acquireNonDroppableBuffer = mCore->mAllowExtraAcquire && + numAcquiredBuffers == mCore->mMaxAcquiredBufferCount + 1; + if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1 && + !acquireNonDroppableBuffer) { BQ_LOGE("acquireBuffer: max acquired buffer count reached: %d (max %d)", numAcquiredBuffers, mCore->mMaxAcquiredBufferCount); return INVALID_OPERATION; @@ -254,6 +257,9 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, outBuffer->mIsStale = false; outBuffer->mAutoRefresh = mCore->mSharedBufferMode && mCore->mAutoRefresh; + } else if (acquireNonDroppableBuffer && front->mIsDroppable) { + BQ_LOGV("acquireBuffer: front buffer is not droppable"); + return NO_BUFFER_AVAILABLE; } else { slot = front->mSlot; *outBuffer = *front; @@ -824,4 +830,9 @@ status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResul return NO_ERROR; } +void BufferQueueConsumer::setAllowExtraAcquire(bool allow) { + std::lock_guard<std::mutex> lock(mCore->mMutex); + mCore->mAllowExtraAcquire = allow; +} + } // namespace android diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index 682fe911de..9cd3f631c8 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -33,10 +33,10 @@ namespace android { // using just a few large reads. static const size_t EVENT_BUFFER_SIZE = 100; -DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper, - ISurfaceComposer::VsyncSource vsyncSource, - ISurfaceComposer::ConfigChanged configChanged) - : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) { +DisplayEventDispatcher::DisplayEventDispatcher( + const sp<Looper>& looper, ISurfaceComposer::VsyncSource vsyncSource, + ISurfaceComposer::EventRegistrationFlags eventRegistration) + : mLooper(looper), mReceiver(vsyncSource, eventRegistration), mWaitingForVsync(false) { ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this); } @@ -73,7 +73,8 @@ status_t DisplayEventDispatcher::scheduleVsync() { nsecs_t vsyncTimestamp; PhysicalDisplayId vsyncDisplayId; uint32_t vsyncCount; - if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { + VsyncEventData vsyncEventData; + if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) { ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", this, ns2ms(static_cast<nsecs_t>(vsyncTimestamp))); } @@ -116,12 +117,14 @@ int DisplayEventDispatcher::handleEvent(int, int events, void*) { nsecs_t vsyncTimestamp; PhysicalDisplayId vsyncDisplayId; uint32_t vsyncCount; - if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { + VsyncEventData vsyncEventData; + if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) { ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 - ", displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d", - this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount); + ", displayId=%s, count=%d, vsyncId=%" PRId64, + this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount, + vsyncEventData.id); mWaitingForVsync = false; - dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount); + dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData); } return 1; // keep the callback @@ -129,12 +132,14 @@ int DisplayEventDispatcher::handleEvent(int, int events, void*) { bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, - uint32_t* outCount) { + uint32_t* outCount, + VsyncEventData* outVsyncEventData) { bool gotVsync = false; DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; ssize_t n; while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { ALOGV("dispatcher %p ~ Read %d events.", this, int(n)); + mFrameRateOverrides.reserve(n); for (ssize_t i = 0; i < n; i++) { const DisplayEventReceiver::Event& ev = buf[i]; switch (ev.header.type) { @@ -145,17 +150,26 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, *outTimestamp = ev.header.timestamp; *outDisplayId = ev.header.displayId; *outCount = ev.vsync.count; + outVsyncEventData->id = ev.vsync.vsyncId; + outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp; break; case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected); break; - case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: - dispatchConfigChanged(ev.header.timestamp, ev.header.displayId, - ev.config.configId, ev.config.vsyncPeriod); + case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: + dispatchModeChanged(ev.header.timestamp, ev.header.displayId, + ev.modeChange.modeId, ev.modeChange.vsyncPeriod); break; case DisplayEventReceiver::DISPLAY_EVENT_NULL: dispatchNullEvent(ev.header.timestamp, ev.header.displayId); break; + case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE: + mFrameRateOverrides.emplace_back(ev.frameRateOverride); + break; + case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH: + dispatchFrameRateOverrides(ev.header.timestamp, ev.header.displayId, + std::move(mFrameRateOverrides)); + break; default: ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type); break; diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp index f2b0962eb7..03b33c7330 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -32,11 +32,12 @@ namespace android { // --------------------------------------------------------------------------- -DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource, - ISurfaceComposer::ConfigChanged configChanged) { +DisplayEventReceiver::DisplayEventReceiver( + ISurfaceComposer::VsyncSource vsyncSource, + ISurfaceComposer::EventRegistrationFlags eventRegistration) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); if (sf != nullptr) { - mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged); + mEventConnection = sf->createDisplayEventConnection(vsyncSource, eventRegistration); if (mEventConnection != nullptr) { mDataChannel = std::make_unique<gui::BitTube>(); mEventConnection->stealReceiveChannel(mDataChannel.get()); diff --git a/libs/gui/FrameTimelineInfo.cpp b/libs/gui/FrameTimelineInfo.cpp new file mode 100644 index 0000000000..f40077403a --- /dev/null +++ b/libs/gui/FrameTimelineInfo.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "FrameTimelineInfo" + +#include <inttypes.h> + +#include <android/os/IInputConstants.h> +#include <gui/FrameTimelineInfo.h> +#include <gui/LayerState.h> +#include <utils/Errors.h> + +#include <cmath> + +using android::os::IInputConstants; + +namespace android { + +status_t FrameTimelineInfo::write(Parcel& output) const { + SAFE_PARCEL(output.writeInt64, vsyncId); + SAFE_PARCEL(output.writeInt32, inputEventId); + return NO_ERROR; +} + +status_t FrameTimelineInfo::read(const Parcel& input) { + SAFE_PARCEL(input.readInt64, &vsyncId); + SAFE_PARCEL(input.readInt32, &inputEventId); + return NO_ERROR; +} + +void FrameTimelineInfo::merge(const FrameTimelineInfo& other) { + // When merging vsync Ids we take the oldest valid one + if (vsyncId != INVALID_VSYNC_ID && other.vsyncId != INVALID_VSYNC_ID) { + if (other.vsyncId > vsyncId) { + vsyncId = other.vsyncId; + inputEventId = other.inputEventId; + } + } else if (vsyncId == INVALID_VSYNC_ID) { + vsyncId = other.vsyncId; + inputEventId = other.inputEventId; + } +} + +void FrameTimelineInfo::clear() { + vsyncId = INVALID_VSYNC_ID; + inputEventId = IInputConstants::INVALID_INPUT_EVENT_ID; +} + +}; // namespace android diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index ad00939976..c1f9b85229 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -74,6 +74,13 @@ enum { GET_CONSUMER_USAGE, SET_LEGACY_BUFFER_DROP, SET_AUTO_PREROTATION, + REQUEST_BUFFERS, + DEQUEUE_BUFFERS, + DETACH_BUFFERS, + ATTACH_BUFFERS, + QUEUE_BUFFERS, + CANCEL_BUFFERS, + QUERY_MULTIPLE, }; class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> @@ -90,7 +97,7 @@ public: Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(bufferIdx); - status_t result =remote()->transact(REQUEST_BUFFER, data, &reply); + status_t result = remote()->transact(REQUEST_BUFFER, data, &reply); if (result != NO_ERROR) { return result; } @@ -107,6 +114,27 @@ public: return result; } + virtual status_t requestBuffers( + const std::vector<int32_t>& slots, + std::vector<RequestBufferOutput>* outputs) override { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32Vector(slots); + status_t result = remote()->transact(REQUEST_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.resizeOutVector(outputs); + for (RequestBufferOutput& output : *outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply.read(output); + } + + return result; + } + virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) { Parcel data, reply; data.writeInterfaceToken( @@ -183,6 +211,29 @@ public: return result; } + virtual status_t dequeueBuffers( + const std::vector<DequeueBufferInput>& inputs, + std::vector<DequeueBufferOutput>* outputs) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeVectorSize(inputs); + for (const auto& input : inputs) { + data.write(input); + } + status_t result = remote()->transact(DEQUEUE_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.resizeOutVector(outputs); + for (auto& output : *outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply.read(output); + } + return result; + } + virtual status_t detachBuffer(int slot) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); @@ -195,6 +246,19 @@ public: return result; } + virtual status_t detachBuffers(const std::vector<int32_t>& slots, + std::vector<status_t>* results) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32Vector(slots); + status_t result = remote()->transact(DETACH_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.readInt32Vector(results); + return result; + } + virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { if (outBuffer == nullptr) { @@ -256,6 +320,39 @@ public: return result; } + virtual status_t attachBuffers( + const std::vector<sp<GraphicBuffer>>& buffers, + std::vector<AttachBufferOutput>* outputs) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeVectorSize(buffers); + for (const sp<GraphicBuffer>& buffer : buffers) { + data.write(*buffer.get()); + } + status_t result = remote()->transact(ATTACH_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.resizeOutVector(outputs); + for (AttachBufferOutput& output : *outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply.read(output); + } + if (result == NO_ERROR) { + for (AttachBufferOutput& output : *outputs) { + if (output.result == NO_ERROR && output.slot < 0) { + ALOGE("attachBuffers returned invalid slot %d", + output.slot); + android_errorWriteLog(0x534e4554, "37478824"); + output.result = UNKNOWN_ERROR; + } + } + } + return result; + } + virtual status_t queueBuffer(int buf, const QueueBufferInput& input, QueueBufferOutput* output) { Parcel data, reply; @@ -278,6 +375,28 @@ public: return result; } + virtual status_t queueBuffers(const std::vector<QueueBufferInput>& inputs, + std::vector<QueueBufferOutput>* outputs) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeVectorSize(inputs); + for (const QueueBufferInput& input : inputs) { + data.write(input); + } + status_t result = remote()->transact(QUEUE_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.resizeOutVector(outputs); + for (QueueBufferOutput& output : *outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply.read(output); + } + return result; + } + virtual status_t cancelBuffer(int buf, const sp<Fence>& fence) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); @@ -291,6 +410,23 @@ public: return result; } + virtual status_t cancelBuffers( + const std::vector<CancelBufferInput>& inputs, + std::vector<status_t>* results) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeVectorSize(inputs); + for (const CancelBufferInput& input : inputs) { + data.write(input); + } + status_t result = remote()->transact(CANCEL_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.readInt32Vector(results); + return result; + } + virtual int query(int what, int* value) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); @@ -304,6 +440,25 @@ public: return result; } + virtual status_t query(const std::vector<int32_t> inputs, + std::vector<QueryOutput>* outputs) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32Vector(inputs); + status_t result = remote()->transact(QUERY_MULTIPLE, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.resizeOutVector(outputs); + for (QueryOutput& output : *outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply.read(output); + } + return result; + } + virtual status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) { Parcel data, reply; @@ -576,6 +731,12 @@ public: return mBase->requestBuffer(slot, buf); } + status_t requestBuffers( + const std::vector<int32_t>& slots, + std::vector<RequestBufferOutput>* outputs) override { + return mBase->requestBuffers(slots, outputs); + } + status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override { return mBase->setMaxDequeuedBufferCount(maxDequeuedBuffers); } @@ -590,10 +751,21 @@ public: return mBase->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps); } + status_t dequeueBuffers( + const std::vector<DequeueBufferInput>& inputs, + std::vector<DequeueBufferOutput>* outputs) override { + return mBase->dequeueBuffers(inputs, outputs); + } + status_t detachBuffer(int slot) override { return mBase->detachBuffer(slot); } + status_t detachBuffers(const std::vector<int32_t>& slots, + std::vector<status_t>* results) override { + return mBase->detachBuffers(slots, results); + } + status_t detachNextBuffer( sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override { return mBase->detachNextBuffer(outBuffer, outFence); @@ -604,6 +776,12 @@ public: return mBase->attachBuffer(outSlot, buffer); } + status_t attachBuffers( + const std::vector<sp<GraphicBuffer>>& buffers, + std::vector<AttachBufferOutput>* outputs) override { + return mBase->attachBuffers(buffers, outputs); + } + status_t queueBuffer( int slot, const QueueBufferInput& input, @@ -611,14 +789,30 @@ public: return mBase->queueBuffer(slot, input, output); } + status_t queueBuffers(const std::vector<QueueBufferInput>& inputs, + std::vector<QueueBufferOutput>* outputs) override { + return mBase->queueBuffers(inputs, outputs); + } + status_t cancelBuffer(int slot, const sp<Fence>& fence) override { return mBase->cancelBuffer(slot, fence); } + status_t cancelBuffers( + const std::vector<CancelBufferInput>& inputs, + std::vector<status_t>* results) override { + return mBase->cancelBuffers(inputs, results); + } + int query(int what, int* value) override { return mBase->query(what, value); } + status_t query(const std::vector<int32_t> inputs, + std::vector<QueryOutput>* outputs) override { + return mBase->query(inputs, outputs); + } + status_t connect( const sp<IProducerListener>& listener, int api, bool producerControlledByApp, @@ -789,7 +983,7 @@ status_t BnGraphicBufferProducer::onTransact( switch(code) { case REQUEST_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); - int bufferIdx = data.readInt32(); + int bufferIdx = data.readInt32(); sp<GraphicBuffer> buffer; int result = requestBuffer(bufferIdx, &buffer); reply->writeInt32(buffer != nullptr); @@ -799,6 +993,24 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } + case REQUEST_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + std::vector<int32_t> slots; + std::vector<RequestBufferOutput> outputs; + status_t result = data.readInt32Vector(&slots); + if (result != NO_ERROR) { + return result; + } + (void)requestBuffers(slots, &outputs); + result = reply->writeVectorSize(outputs); + for (const RequestBufferOutput& output : outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply->write(output); + } + return result; + } case SET_MAX_DEQUEUED_BUFFER_COUNT: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int maxDequeuedBuffers = data.readInt32(); @@ -841,6 +1053,30 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } + case DEQUEUE_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + std::vector<DequeueBufferInput> inputs; + std::vector<DequeueBufferOutput> outputs; + status_t result = data.resizeOutVector(&inputs); + if (result != NO_ERROR) { + return result; + } + for (DequeueBufferInput& input : inputs) { + result = data.read(input); + if (result != NO_ERROR) { + return result; + } + } + (void)dequeueBuffers(inputs, &outputs); + result = reply->writeVectorSize(outputs); + for (const DequeueBufferOutput& output : outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply->write(output); + } + return result; + } case DETACH_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int slot = data.readInt32(); @@ -848,6 +1084,17 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } + case DETACH_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + std::vector<int32_t> slots; + std::vector<status_t> results; + status_t result = data.readInt32Vector(&slots); + if (result != NO_ERROR) { + return result; + } + (void)detachBuffers(slots, &results); + return reply->writeInt32Vector(results); + } case DETACH_NEXT_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); sp<GraphicBuffer> buffer; @@ -878,6 +1125,31 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } + case ATTACH_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + std::vector<sp<GraphicBuffer>> buffers; + status_t result = data.resizeOutVector(&buffers); + if (result != NO_ERROR) { + return result; + } + for (sp<GraphicBuffer>& buffer : buffers) { + buffer = new GraphicBuffer(); + result = data.read(*buffer.get()); + if (result != NO_ERROR) { + return result; + } + } + std::vector<AttachBufferOutput> outputs; + (void)attachBuffers(buffers, &outputs); + result = reply->writeVectorSize(outputs); + for (const AttachBufferOutput& output : outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply->write(output); + } + return result; + } case QUEUE_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); @@ -890,6 +1162,30 @@ status_t BnGraphicBufferProducer::onTransact( return NO_ERROR; } + case QUEUE_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + std::vector<QueueBufferInput> inputs; + status_t result = data.resizeOutVector(&inputs); + if (result != NO_ERROR) { + return result; + } + for (QueueBufferInput& input : inputs) { + result = data.read(input); + if (result != NO_ERROR) { + return result; + } + } + std::vector<QueueBufferOutput> outputs; + (void)queueBuffers(inputs, &outputs); + result = reply->writeVectorSize(outputs); + for (const QueueBufferOutput& output : outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply->write(output); + } + return result; + } case CANCEL_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int buf = data.readInt32(); @@ -901,6 +1197,26 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } + case CANCEL_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + std::vector<CancelBufferInput> inputs; + status_t result = data.resizeOutVector(&inputs); + for (CancelBufferInput& input : inputs) { + if (result != NO_ERROR) { + return result; + } + result = data.read(input); + } + if (result != NO_ERROR) { + return result; + } + std::vector<status_t> results; + result = cancelBuffers(inputs, &results); + if (result != NO_ERROR) { + return result; + } + return reply->writeInt32Vector(results); + } case QUERY: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int value = 0; @@ -910,6 +1226,27 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(res); return NO_ERROR; } + case QUERY_MULTIPLE: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + std::vector<int32_t> inputs; + status_t result = data.readInt32Vector(&inputs); + if (result != NO_ERROR) { + return result; + } + std::vector<QueryOutput> outputs; + result = query(inputs, &outputs); + if (result != NO_ERROR) { + return result; + } + result = reply->writeVectorSize(outputs); + for (const QueryOutput& output : outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply->write(output); + } + return result; + } case CONNECT: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); sp<IProducerListener> listener; @@ -1083,11 +1420,4 @@ status_t BnGraphicBufferProducer::onTransact( return BBinder::onTransact(code, data, reply, flags); } -// ---------------------------------------------------------------------------- - -IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) { - parcel.read(*this); -} - - }; // namespace android diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp new file mode 100644 index 0000000000..c8b9b6751d --- /dev/null +++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp @@ -0,0 +1,413 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <inttypes.h> +#include <gui/IGraphicBufferProducer.h> + +namespace android { + +constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() { + return sizeof(timestamp) + + sizeof(isAutoTimestamp) + + sizeof(dataSpace) + + sizeof(crop) + + sizeof(scalingMode) + + sizeof(transform) + + sizeof(stickyTransform) + + sizeof(getFrameTimestamps) + + sizeof(slot); +} + +size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const { + return minFlattenedSize() + + fence->getFlattenedSize() + + surfaceDamage.getFlattenedSize() + + hdrMetadata.getFlattenedSize(); +} + +size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const { + return fence->getFdCount(); +} + +status_t IGraphicBufferProducer::QueueBufferInput::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const +{ + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, timestamp); + FlattenableUtils::write(buffer, size, isAutoTimestamp); + FlattenableUtils::write(buffer, size, dataSpace); + FlattenableUtils::write(buffer, size, crop); + FlattenableUtils::write(buffer, size, scalingMode); + FlattenableUtils::write(buffer, size, transform); + FlattenableUtils::write(buffer, size, stickyTransform); + FlattenableUtils::write(buffer, size, getFrameTimestamps); + + status_t result = fence->flatten(buffer, size, fds, count); + if (result != NO_ERROR) { + return result; + } + result = surfaceDamage.flatten(buffer, size); + if (result != NO_ERROR) { + return result; + } + FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize()); + result = hdrMetadata.flatten(buffer, size); + if (result != NO_ERROR) { + return result; + } + FlattenableUtils::advance(buffer, size, hdrMetadata.getFlattenedSize()); + FlattenableUtils::write(buffer, size, slot); + return NO_ERROR; +} + +status_t IGraphicBufferProducer::QueueBufferInput::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) +{ + if (size < minFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, timestamp); + FlattenableUtils::read(buffer, size, isAutoTimestamp); + FlattenableUtils::read(buffer, size, dataSpace); + FlattenableUtils::read(buffer, size, crop); + FlattenableUtils::read(buffer, size, scalingMode); + FlattenableUtils::read(buffer, size, transform); + FlattenableUtils::read(buffer, size, stickyTransform); + FlattenableUtils::read(buffer, size, getFrameTimestamps); + + fence = new Fence(); + status_t result = fence->unflatten(buffer, size, fds, count); + if (result != NO_ERROR) { + return result; + } + result = surfaceDamage.unflatten(buffer, size); + if (result != NO_ERROR) { + return result; + } + FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize()); + result = hdrMetadata.unflatten(buffer, size); + if (result != NO_ERROR) { + return result; + } + FlattenableUtils::advance(buffer, size, hdrMetadata.getFlattenedSize()); + FlattenableUtils::read(buffer, size, slot); + return NO_ERROR; +} + +//////////////////////////////////////////////////////////////////////// +constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() { + return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) + + sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount) + + sizeof(result); +} +size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const { + return minFlattenedSize() + frameTimestamps.getFlattenedSize(); +} + +size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const { + return frameTimestamps.getFdCount(); +} + +status_t IGraphicBufferProducer::QueueBufferOutput::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const +{ + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, width); + FlattenableUtils::write(buffer, size, height); + FlattenableUtils::write(buffer, size, transformHint); + FlattenableUtils::write(buffer, size, numPendingBuffers); + FlattenableUtils::write(buffer, size, nextFrameNumber); + FlattenableUtils::write(buffer, size, bufferReplaced); + FlattenableUtils::write(buffer, size, maxBufferCount); + + status_t result = frameTimestamps.flatten(buffer, size, fds, count); + if (result != NO_ERROR) { + return result; + } + FlattenableUtils::write(buffer, size, result); + return NO_ERROR; +} + +status_t IGraphicBufferProducer::QueueBufferOutput::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) +{ + if (size < minFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, width); + FlattenableUtils::read(buffer, size, height); + FlattenableUtils::read(buffer, size, transformHint); + FlattenableUtils::read(buffer, size, numPendingBuffers); + FlattenableUtils::read(buffer, size, nextFrameNumber); + FlattenableUtils::read(buffer, size, bufferReplaced); + FlattenableUtils::read(buffer, size, maxBufferCount); + + status_t result = frameTimestamps.unflatten(buffer, size, fds, count); + if (result != NO_ERROR) { + return result; + } + FlattenableUtils::read(buffer, size, result); + return NO_ERROR; +} + +//////////////////////////////////////////////////////////////////////// +constexpr size_t IGraphicBufferProducer::RequestBufferOutput::minFlattenedSize() { + return sizeof(result) + + sizeof(int32_t); // IsBufferNull +} + +size_t IGraphicBufferProducer::RequestBufferOutput::getFlattenedSize() const { + return minFlattenedSize() + (buffer == nullptr ? 0 : buffer->getFlattenedSize()); +} + +size_t IGraphicBufferProducer::RequestBufferOutput::getFdCount() const { + return (buffer == nullptr ? 0 : buffer->getFdCount()); +} + +status_t IGraphicBufferProducer::RequestBufferOutput::flatten( + void*& fBuffer, size_t& size, int*& fds, size_t& count) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(fBuffer, size, result); + const int32_t isBufferNull = (buffer == nullptr ? 1 : 0); + FlattenableUtils::write(fBuffer, size, isBufferNull); + + if (!isBufferNull) { + status_t status = buffer->flatten(fBuffer, size, fds, count); + if (status != NO_ERROR) { + return status; + } + } + return NO_ERROR; +} + +status_t IGraphicBufferProducer::RequestBufferOutput::unflatten( + void const*& fBuffer, size_t& size, int const*& fds, size_t& count) { + if (size < minFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(fBuffer, size, result); + int32_t isBufferNull = 0; + FlattenableUtils::read(fBuffer, size, isBufferNull); + buffer = new GraphicBuffer(); + if (!isBufferNull) { + status_t status = buffer->unflatten(fBuffer, size, fds, count); + if (status != NO_ERROR) { + return status; + } + } + return NO_ERROR; +} + +//////////////////////////////////////////////////////////////////////// + +size_t IGraphicBufferProducer::DequeueBufferInput::getFlattenedSize() const { + return sizeof(width) + sizeof(height) + sizeof(format) + sizeof(usage) + + sizeof(int32_t/*getTimestamps*/); +} + +status_t IGraphicBufferProducer::DequeueBufferInput::flatten(void* buffer, size_t size) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + FlattenableUtils::write(buffer, size, width); + FlattenableUtils::write(buffer, size, height); + FlattenableUtils::write(buffer, size, format); + FlattenableUtils::write(buffer, size, usage); + const int32_t getTimestampsInt = (getTimestamps ? 1 : 0); + FlattenableUtils::write(buffer, size, getTimestampsInt); + + return NO_ERROR; +} + +status_t IGraphicBufferProducer::DequeueBufferInput::unflatten(void const* buffer, size_t size) { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, width); + FlattenableUtils::read(buffer, size, height); + FlattenableUtils::read(buffer, size, format); + FlattenableUtils::read(buffer, size, usage); + int32_t getTimestampsInt = 0; + FlattenableUtils::read(buffer, size, getTimestampsInt); + getTimestamps = (getTimestampsInt == 1); + + return NO_ERROR; +} + +//////////////////////////////////////////////////////////////////////// + +constexpr size_t IGraphicBufferProducer::DequeueBufferOutput::minFlattenedSize() { + return sizeof(result) + sizeof(slot) + sizeof(bufferAge) + sizeof(int32_t/*hasTimestamps*/); +} + +size_t IGraphicBufferProducer::DequeueBufferOutput::getFlattenedSize() const { + return minFlattenedSize() + + fence->getFlattenedSize() + + (timestamps.has_value() ? timestamps->getFlattenedSize() : 0); +} + +size_t IGraphicBufferProducer::DequeueBufferOutput::getFdCount() const { + return fence->getFdCount() + + (timestamps.has_value() ? timestamps->getFdCount() : 0); +} + +status_t IGraphicBufferProducer::DequeueBufferOutput::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, result); + FlattenableUtils::write(buffer, size, slot); + FlattenableUtils::write(buffer, size, bufferAge); + status_t status = fence->flatten(buffer, size, fds, count); + if (status != NO_ERROR) { + return result; + } + const int32_t hasTimestamps = timestamps.has_value() ? 1 : 0; + FlattenableUtils::write(buffer, size, hasTimestamps); + if (timestamps.has_value()) { + status = timestamps->flatten(buffer, size, fds, count); + } + return status; +} + +status_t IGraphicBufferProducer::DequeueBufferOutput::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { + if (size < minFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, result); + FlattenableUtils::read(buffer, size, slot); + FlattenableUtils::read(buffer, size, bufferAge); + + fence = new Fence(); + status_t status = fence->unflatten(buffer, size, fds, count); + if (status != NO_ERROR) { + return status; + } + int32_t hasTimestamps = 0; + FlattenableUtils::read(buffer, size, hasTimestamps); + if (hasTimestamps) { + timestamps.emplace(); + status = timestamps->unflatten(buffer, size, fds, count); + } + return status; +} + +//////////////////////////////////////////////////////////////////////// + +size_t IGraphicBufferProducer::AttachBufferOutput::getFlattenedSize() const { + return sizeof(result) + sizeof(slot); +} + +status_t IGraphicBufferProducer::AttachBufferOutput::flatten(void* buffer, size_t size) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + FlattenableUtils::write(buffer, size, result); + FlattenableUtils::write(buffer, size, slot); + + return NO_ERROR; +} + +status_t IGraphicBufferProducer::AttachBufferOutput::unflatten(void const* buffer, size_t size) { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + FlattenableUtils::read(buffer, size, result); + FlattenableUtils::read(buffer, size, slot); + + return NO_ERROR; +} + +//////////////////////////////////////////////////////////////////////// + +constexpr size_t IGraphicBufferProducer::CancelBufferInput::minFlattenedSize() { + return sizeof(slot); +} + +size_t IGraphicBufferProducer::CancelBufferInput::getFlattenedSize() const { + return minFlattenedSize() + fence->getFlattenedSize(); +} + +size_t IGraphicBufferProducer::CancelBufferInput::getFdCount() const { + return fence->getFdCount(); +} + +status_t IGraphicBufferProducer::CancelBufferInput::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, slot); + return fence->flatten(buffer, size, fds, count); +} + +status_t IGraphicBufferProducer::CancelBufferInput::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { + if (size < minFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, slot); + + fence = new Fence(); + return fence->unflatten(buffer, size, fds, count); +} + +//////////////////////////////////////////////////////////////////////// + +size_t IGraphicBufferProducer::QueryOutput::getFlattenedSize() const { + return sizeof(result) + sizeof(value); +} + +status_t IGraphicBufferProducer::QueryOutput::flatten(void* buffer, size_t size) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + FlattenableUtils::write(buffer, size, result); + FlattenableUtils::write(buffer, size, value); + + return NO_ERROR; +} + +status_t IGraphicBufferProducer::QueryOutput::unflatten(void const* buffer, size_t size) { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + FlattenableUtils::read(buffer, size, result); + FlattenableUtils::read(buffer, size, value); + + return NO_ERROR; +} + +} // namespace android diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index e62a61fc55..762746c0ce 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -20,6 +20,8 @@ #include <stdint.h> #include <sys/types.h> +#include <android/gui/ITransactionTraceListener.h> + #include <binder/Parcel.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> @@ -34,8 +36,8 @@ #include <system/graphics.h> -#include <ui/DisplayConfig.h> #include <ui/DisplayInfo.h> +#include <ui/DisplayMode.h> #include <ui/DisplayStatInfo.h> #include <ui/DisplayState.h> #include <ui/HdrCapabilities.h> @@ -66,145 +68,87 @@ public: return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); } - virtual void setTransactionState(const Vector<ComposerState>& state, - const Vector<DisplayState>& displays, uint32_t flags, - const sp<IBinder>& applyToken, - const InputWindowCommands& commands, - int64_t desiredPresentTime, - const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, - const std::vector<ListenerCallbacks>& listenerCallbacks) { + status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo, + const Vector<ComposerState>& state, + const Vector<DisplayState>& displays, uint32_t flags, + const sp<IBinder>& applyToken, const InputWindowCommands& commands, + int64_t desiredPresentTime, bool isAutoTimestamp, + const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, + const std::vector<ListenerCallbacks>& listenerCallbacks, + uint64_t transactionId) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeUint32(static_cast<uint32_t>(state.size())); + SAFE_PARCEL(frameTimelineInfo.write, data); + + SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(state.size())); for (const auto& s : state) { - s.write(data); + SAFE_PARCEL(s.write, data); } - data.writeUint32(static_cast<uint32_t>(displays.size())); + SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(displays.size())); for (const auto& d : displays) { - d.write(data); + SAFE_PARCEL(d.write, data); } - data.writeUint32(flags); - data.writeStrongBinder(applyToken); - commands.write(data); - data.writeInt64(desiredPresentTime); - data.writeStrongBinder(uncacheBuffer.token.promote()); - data.writeUint64(uncacheBuffer.id); - data.writeBool(hasListenerCallbacks); + SAFE_PARCEL(data.writeUint32, flags); + SAFE_PARCEL(data.writeStrongBinder, applyToken); + SAFE_PARCEL(commands.write, data); + SAFE_PARCEL(data.writeInt64, desiredPresentTime); + SAFE_PARCEL(data.writeBool, isAutoTimestamp); + SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote()); + SAFE_PARCEL(data.writeUint64, uncacheBuffer.id); + SAFE_PARCEL(data.writeBool, hasListenerCallbacks); - if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) { - for (const auto& [listener, callbackIds] : listenerCallbacks) { - data.writeStrongBinder(listener); - data.writeInt64Vector(callbackIds); - } + SAFE_PARCEL(data.writeVectorSize, listenerCallbacks); + for (const auto& [listener, callbackIds] : listenerCallbacks) { + SAFE_PARCEL(data.writeStrongBinder, listener); + SAFE_PARCEL(data.writeInt64Vector, callbackIds); } - remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply); + SAFE_PARCEL(data.writeUint64, transactionId); + + return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply); } - virtual void bootFinished() - { + void bootFinished() override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply); } - virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - bool& outCapturedSecureLayers, ui::Dataspace reqDataspace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ui::Rotation rotation, bool captureSecureLayers) { + status_t captureDisplay(const DisplayCaptureArgs& args, + const sp<IScreenCaptureListener>& captureListener) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(display); - data.writeInt32(static_cast<int32_t>(reqDataspace)); - data.writeInt32(static_cast<int32_t>(reqPixelFormat)); - data.write(sourceCrop); - data.writeUint32(reqWidth); - data.writeUint32(reqHeight); - data.writeInt32(static_cast<int32_t>(useIdentityTransform)); - data.writeInt32(static_cast<int32_t>(rotation)); - data.writeInt32(static_cast<int32_t>(captureSecureLayers)); - status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); - if (result != NO_ERROR) { - ALOGE("captureScreen failed to transact: %d", result); - return result; - } - result = reply.readInt32(); - if (result != NO_ERROR) { - ALOGE("captureScreen failed to readInt32: %d", result); - return result; - } - - *outBuffer = new GraphicBuffer(); - reply.read(**outBuffer); - outCapturedSecureLayers = reply.readBool(); + SAFE_PARCEL(args.write, data); + SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener)); - return result; + return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply); } - virtual status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, - sp<GraphicBuffer>* outBuffer) { + status_t captureDisplay(uint64_t displayOrLayerStack, + const sp<IScreenCaptureListener>& captureListener) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeUint64(displayOrLayerStack); - status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN_BY_ID, data, &reply); - if (result != NO_ERROR) { - ALOGE("captureScreen failed to transact: %d", result); - return result; - } - result = reply.readInt32(); - if (result != NO_ERROR) { - ALOGE("captureScreen failed to readInt32: %d", result); - return result; - } + SAFE_PARCEL(data.writeUint64, displayOrLayerStack); + SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener)); - *outDataspace = static_cast<ui::Dataspace>(reply.readInt32()); - *outBuffer = new GraphicBuffer(); - reply.read(**outBuffer); - return result; + return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply); } - virtual status_t captureLayers( - const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer, - const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, - const Rect& sourceCrop, - const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeLayers, float frameScale, - bool childrenOnly) { + status_t captureLayers(const LayerCaptureArgs& args, + const sp<IScreenCaptureListener>& captureListener) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(layerHandleBinder); - data.writeInt32(static_cast<int32_t>(reqDataspace)); - data.writeInt32(static_cast<int32_t>(reqPixelFormat)); - data.write(sourceCrop); - data.writeInt32(excludeLayers.size()); - for (auto el : excludeLayers) { - data.writeStrongBinder(el); - } - data.writeFloat(frameScale); - data.writeBool(childrenOnly); - status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply); - if (result != NO_ERROR) { - ALOGE("captureLayers failed to transact: %d", result); - return result; - } - result = reply.readInt32(); - if (result != NO_ERROR) { - ALOGE("captureLayers failed to readInt32: %d", result); - return result; - } - - *outBuffer = new GraphicBuffer(); - reply.read(**outBuffer); + SAFE_PARCEL(args.write, data); + SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener)); - return result; + return remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply); } - virtual bool authenticateSurfaceTexture( - const sp<IGraphicBufferProducer>& bufferProducer) const - { + bool authenticateSurfaceTexture( + const sp<IGraphicBufferProducer>& bufferProducer) const override { Parcel data, reply; int err = NO_ERROR; err = data.writeInterfaceToken( @@ -237,8 +181,7 @@ public: return result != 0; } - virtual status_t getSupportedFrameTimestamps( - std::vector<FrameEvent>* outSupported) const { + status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const override { if (!outSupported) { return UNEXPECTED_NULL; } @@ -281,8 +224,8 @@ public: return NO_ERROR; } - virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource, - ConfigChanged configChanged) { + sp<IDisplayEventConnection> createDisplayEventConnection( + VsyncSource vsyncSource, EventRegistrationFlags eventRegistration) override { Parcel data, reply; sp<IDisplayEventConnection> result; int err = data.writeInterfaceToken( @@ -291,7 +234,7 @@ public: return result; } data.writeInt32(static_cast<int32_t>(vsyncSource)); - data.writeInt32(static_cast<int32_t>(configChanged)); + data.writeUint32(eventRegistration.get()); err = remote()->transact( BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION, data, &reply); @@ -304,31 +247,47 @@ public: return result; } - virtual sp<IBinder> createDisplay(const String8& displayName, bool secure) - { + sp<IBinder> createDisplay(const String8& displayName, bool secure) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeString8(displayName); - data.writeInt32(secure ? 1 : 0); - remote()->transact(BnSurfaceComposer::CREATE_DISPLAY, data, &reply); - return reply.readStrongBinder(); + status_t status = data.writeString8(displayName); + if (status) { + return nullptr; + } + status = data.writeBool(secure); + if (status) { + return nullptr; + } + + status = remote()->transact(BnSurfaceComposer::CREATE_DISPLAY, data, &reply); + if (status) { + return nullptr; + } + sp<IBinder> display; + status = reply.readNullableStrongBinder(&display); + if (status) { + return nullptr; + } + return display; } - virtual void destroyDisplay(const sp<IBinder>& display) - { + void destroyDisplay(const sp<IBinder>& display) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); remote()->transact(BnSurfaceComposer::DESTROY_DISPLAY, data, &reply); } - virtual std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const { + std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS, data, &reply) == NO_ERROR) { - std::vector<PhysicalDisplayId> displayIds; - if (reply.readUint64Vector(&displayIds) == NO_ERROR) { + std::vector<uint64_t> rawIds; + if (reply.readUint64Vector(&rawIds) == NO_ERROR) { + std::vector<PhysicalDisplayId> displayIds(rawIds.size()); + std::transform(rawIds.begin(), rawIds.end(), displayIds.begin(), + [](uint64_t rawId) { return PhysicalDisplayId(rawId); }); return displayIds; } } @@ -336,16 +295,15 @@ public: return {}; } - virtual sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const { + sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeUint64(displayId); + data.writeUint64(displayId.value); remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN, data, &reply); return reply.readStrongBinder(); } - virtual void setPowerMode(const sp<IBinder>& display, int mode) - { + void setPowerMode(const sp<IBinder>& display, int mode) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); @@ -353,7 +311,7 @@ public: remote()->transact(BnSurfaceComposer::SET_POWER_MODE, data, &reply); } - virtual status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState* state) { + status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState* state) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); @@ -365,39 +323,35 @@ public: return result; } - virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) { + status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); remote()->transact(BnSurfaceComposer::GET_DISPLAY_INFO, data, &reply); const status_t result = reply.readInt32(); - if (result == NO_ERROR) { - memcpy(info, reply.readInplace(sizeof(DisplayInfo)), sizeof(DisplayInfo)); - } - return result; + if (result != NO_ERROR) return result; + return reply.read(*info); } - virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>* configs) { + status_t getDisplayModes(const sp<IBinder>& display, Vector<ui::DisplayMode>* modes) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); - remote()->transact(BnSurfaceComposer::GET_DISPLAY_CONFIGS, data, &reply); + remote()->transact(BnSurfaceComposer::GET_DISPLAY_MODES, data, &reply); const status_t result = reply.readInt32(); if (result == NO_ERROR) { - const size_t numConfigs = reply.readUint32(); - configs->clear(); - configs->resize(numConfigs); - for (size_t c = 0; c < numConfigs; ++c) { - memcpy(&(configs->editItemAt(c)), reply.readInplace(sizeof(DisplayConfig)), - sizeof(DisplayConfig)); + const size_t numModes = reply.readUint32(); + modes->clear(); + modes->resize(numModes); + for (size_t i = 0; i < numModes; i++) { + memcpy(&(modes->editItemAt(i)), reply.readInplace(sizeof(ui::DisplayMode)), + sizeof(ui::DisplayMode)); } } return result; } - virtual status_t getDisplayStats(const sp<IBinder>& display, - DisplayStatInfo* stats) - { + status_t getDisplayStats(const sp<IBinder>& display, DisplayStatInfo* stats) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); @@ -411,17 +365,16 @@ public: return result; } - virtual int getActiveConfig(const sp<IBinder>& display) - { + int getActiveDisplayModeId(const sp<IBinder>& display) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); - remote()->transact(BnSurfaceComposer::GET_ACTIVE_CONFIG, data, &reply); + remote()->transact(BnSurfaceComposer::GET_ACTIVE_DISPLAY_MODE, data, &reply); return reply.readInt32(); } - virtual status_t getDisplayColorModes(const sp<IBinder>& display, - Vector<ColorMode>* outColorModes) { + status_t getDisplayColorModes(const sp<IBinder>& display, + Vector<ColorMode>* outColorModes) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { @@ -450,8 +403,8 @@ public: return result; } - virtual status_t getDisplayNativePrimaries(const sp<IBinder>& display, - ui::DisplayPrimaries& primaries) { + status_t getDisplayNativePrimaries(const sp<IBinder>& display, + ui::DisplayPrimaries& primaries) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { @@ -476,7 +429,7 @@ public: return result; } - virtual ColorMode getActiveColorMode(const sp<IBinder>& display) { + ColorMode getActiveColorMode(const sp<IBinder>& display) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { @@ -496,8 +449,7 @@ public: return static_cast<ColorMode>(reply.readInt32()); } - virtual status_t setActiveColorMode(const sp<IBinder>& display, - ColorMode colorMode) { + status_t setActiveColorMode(const sp<IBinder>& display, ColorMode colorMode) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { @@ -522,8 +474,8 @@ public: return static_cast<status_t>(reply.readInt32()); } - virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display, - bool* outSupport) const { + status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display, + bool* outSupport) const override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); status_t result = data.writeStrongBinder(display); @@ -540,7 +492,7 @@ public: return reply.readBool(outSupport); } - virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) { + void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { @@ -565,7 +517,8 @@ public: } } - virtual status_t getGameContentTypeSupport(const sp<IBinder>& display, bool* outSupport) const { + status_t getGameContentTypeSupport(const sp<IBinder>& display, + bool* outSupport) const override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); status_t result = data.writeStrongBinder(display); @@ -581,7 +534,7 @@ public: return reply.readBool(outSupport); } - virtual void setGameContentType(const sp<IBinder>& display, bool on) { + void setGameContentType(const sp<IBinder>& display, bool on) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { @@ -604,7 +557,7 @@ public: } } - virtual status_t clearAnimationFrameStats() { + status_t clearAnimationFrameStats() override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { @@ -619,7 +572,7 @@ public: return reply.readInt32(); } - virtual status_t getAnimationFrameStats(FrameStats* outStats) const { + status_t getAnimationFrameStats(FrameStats* outStats) const override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); remote()->transact(BnSurfaceComposer::GET_ANIMATION_FRAME_STATS, data, &reply); @@ -627,8 +580,8 @@ public: return reply.readInt32(); } - virtual status_t getHdrCapabilities(const sp<IBinder>& display, - HdrCapabilities* outCapabilities) const { + status_t getHdrCapabilities(const sp<IBinder>& display, + HdrCapabilities* outCapabilities) const override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); status_t result = data.writeStrongBinder(display); @@ -649,7 +602,7 @@ public: return result; } - virtual status_t enableVSyncInjections(bool enable) { + status_t enableVSyncInjections(bool enable) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { @@ -670,7 +623,7 @@ public: return result; } - virtual status_t injectVSync(nsecs_t when) { + status_t injectVSync(nsecs_t when) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { @@ -691,7 +644,7 @@ public: return result; } - virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) { + status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override { if (!outLayers) { return UNEXPECTED_NULL; } @@ -721,10 +674,10 @@ public: return reply.readParcelableVector(outLayers); } - virtual status_t getCompositionPreference(ui::Dataspace* defaultDataspace, - ui::PixelFormat* defaultPixelFormat, - ui::Dataspace* wideColorGamutDataspace, - ui::PixelFormat* wideColorGamutPixelFormat) const { + status_t getCompositionPreference(ui::Dataspace* defaultDataspace, + ui::PixelFormat* defaultPixelFormat, + ui::Dataspace* wideColorGamutDataspace, + ui::PixelFormat* wideColorGamutPixelFormat) const override { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { @@ -744,7 +697,7 @@ public: return error; } - virtual status_t getColorManagement(bool* outGetColorManagement) const { + status_t getColorManagement(bool* outGetColorManagement) const override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); remote()->transact(BnSurfaceComposer::GET_COLOR_MANAGEMENT, data, &reply); @@ -756,10 +709,10 @@ public: return err; } - virtual status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display, - ui::PixelFormat* outFormat, - ui::Dataspace* outDataspace, - uint8_t* outComponentMask) const { + status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display, + ui::PixelFormat* outFormat, + ui::Dataspace* outDataspace, + uint8_t* outComponentMask) const override { if (!outFormat || !outDataspace || !outComponentMask) return BAD_VALUE; Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -793,8 +746,8 @@ public: return error; } - virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable, - uint8_t componentMask, uint64_t maxFrames) { + status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable, + uint8_t componentMask, uint64_t maxFrames) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); @@ -807,9 +760,9 @@ public: return result; } - virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames, - uint64_t timestamp, - DisplayedFrameStats* outStats) const { + status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames, + uint64_t timestamp, + DisplayedFrameStats* outStats) const override { if (!outStats) return BAD_VALUE; Parcel data, reply; @@ -846,7 +799,7 @@ public: return result; } - virtual status_t getProtectedContentSupport(bool* outSupported) const { + status_t getProtectedContentSupport(bool* outSupported) const override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); status_t error = @@ -858,8 +811,8 @@ public: return error; } - virtual status_t isWideColorDisplay(const sp<IBinder>& token, - bool* outIsWideColorDisplay) const { + status_t isWideColorDisplay(const sp<IBinder>& token, + bool* outIsWideColorDisplay) const override { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { @@ -878,9 +831,8 @@ public: return error; } - virtual status_t addRegionSamplingListener(const Rect& samplingArea, - const sp<IBinder>& stopLayerHandle, - const sp<IRegionSamplingListener>& listener) { + status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle, + const sp<IRegionSamplingListener>& listener) override { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { @@ -909,7 +861,7 @@ public: return error; } - virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) { + status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { @@ -929,119 +881,133 @@ public: return error; } - virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t defaultConfig, - float primaryRefreshRateMin, - float primaryRefreshRateMax, - float appRequestRefreshRateMin, - float appRequestRefreshRateMax) { + status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t defaultMode, + bool allowGroupSwitching, float primaryRefreshRateMin, + float primaryRefreshRateMax, float appRequestRefreshRateMin, + float appRequestRefreshRateMax) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { - ALOGE("setDesiredDisplayConfigSpecs: failed to writeInterfaceToken: %d", result); + ALOGE("setDesiredDisplayModeSpecs: failed to writeInterfaceToken: %d", result); return result; } result = data.writeStrongBinder(displayToken); if (result != NO_ERROR) { - ALOGE("setDesiredDisplayConfigSpecs: failed to write display token: %d", result); + ALOGE("setDesiredDisplayModeSpecs: failed to write display token: %d", result); return result; } - result = data.writeInt32(defaultConfig); + result = data.writeInt32(defaultMode); if (result != NO_ERROR) { - ALOGE("setDesiredDisplayConfigSpecs failed to write defaultConfig: %d", result); + ALOGE("setDesiredDisplayModeSpecs failed to write defaultMode: %d", result); + return result; + } + result = data.writeBool(allowGroupSwitching); + if (result != NO_ERROR) { + ALOGE("setDesiredDisplayModeSpecs failed to write allowGroupSwitching: %d", result); return result; } result = data.writeFloat(primaryRefreshRateMin); if (result != NO_ERROR) { - ALOGE("setDesiredDisplayConfigSpecs failed to write primaryRefreshRateMin: %d", result); + ALOGE("setDesiredDisplayModeSpecs failed to write primaryRefreshRateMin: %d", result); return result; } result = data.writeFloat(primaryRefreshRateMax); if (result != NO_ERROR) { - ALOGE("setDesiredDisplayConfigSpecs failed to write primaryRefreshRateMax: %d", result); + ALOGE("setDesiredDisplayModeSpecs failed to write primaryRefreshRateMax: %d", result); return result; } result = data.writeFloat(appRequestRefreshRateMin); if (result != NO_ERROR) { - ALOGE("setDesiredDisplayConfigSpecs failed to write appRequestRefreshRateMin: %d", + ALOGE("setDesiredDisplayModeSpecs failed to write appRequestRefreshRateMin: %d", result); return result; } result = data.writeFloat(appRequestRefreshRateMax); if (result != NO_ERROR) { - ALOGE("setDesiredDisplayConfigSpecs failed to write appRequestRefreshRateMax: %d", + ALOGE("setDesiredDisplayModeSpecs failed to write appRequestRefreshRateMax: %d", result); return result; } - result = remote()->transact(BnSurfaceComposer::SET_DESIRED_DISPLAY_CONFIG_SPECS, data, - &reply); + result = + remote()->transact(BnSurfaceComposer::SET_DESIRED_DISPLAY_MODE_SPECS, data, &reply); if (result != NO_ERROR) { - ALOGE("setDesiredDisplayConfigSpecs failed to transact: %d", result); + ALOGE("setDesiredDisplayModeSpecs failed to transact: %d", result); return result; } return reply.readInt32(); } - virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t* outDefaultConfig, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax) { - if (!outDefaultConfig || !outPrimaryRefreshRateMin || !outPrimaryRefreshRateMax || - !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) { + status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t* outDefaultMode, + bool* outAllowGroupSwitching, + float* outPrimaryRefreshRateMin, + float* outPrimaryRefreshRateMax, + float* outAppRequestRefreshRateMin, + float* outAppRequestRefreshRateMax) override { + if (!outDefaultMode || !outAllowGroupSwitching || !outPrimaryRefreshRateMin || + !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin || + !outAppRequestRefreshRateMax) { return BAD_VALUE; } Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { - ALOGE("getDesiredDisplayConfigSpecs failed to writeInterfaceToken: %d", result); + ALOGE("getDesiredDisplayModeSpecs failed to writeInterfaceToken: %d", result); return result; } result = data.writeStrongBinder(displayToken); if (result != NO_ERROR) { - ALOGE("getDesiredDisplayConfigSpecs failed to writeStrongBinder: %d", result); + ALOGE("getDesiredDisplayModeSpecs failed to writeStrongBinder: %d", result); return result; } - result = remote()->transact(BnSurfaceComposer::GET_DESIRED_DISPLAY_CONFIG_SPECS, data, - &reply); + result = + remote()->transact(BnSurfaceComposer::GET_DESIRED_DISPLAY_MODE_SPECS, data, &reply); if (result != NO_ERROR) { - ALOGE("getDesiredDisplayConfigSpecs failed to transact: %d", result); + ALOGE("getDesiredDisplayModeSpecs failed to transact: %d", result); return result; } - result = reply.readInt32(outDefaultConfig); + int32_t defaultMode; + result = reply.readInt32(&defaultMode); if (result != NO_ERROR) { - ALOGE("getDesiredDisplayConfigSpecs failed to read defaultConfig: %d", result); + ALOGE("getDesiredDisplayModeSpecs failed to read defaultMode: %d", result); + return result; + } + if (defaultMode < 0) { + ALOGE("%s: defaultMode must be non-negative but it was %d", __func__, defaultMode); + return BAD_VALUE; + } + *outDefaultMode = static_cast<size_t>(defaultMode); + + result = reply.readBool(outAllowGroupSwitching); + if (result != NO_ERROR) { + ALOGE("getDesiredDisplayModeSpecs failed to read allowGroupSwitching: %d", result); return result; } result = reply.readFloat(outPrimaryRefreshRateMin); if (result != NO_ERROR) { - ALOGE("getDesiredDisplayConfigSpecs failed to read primaryRefreshRateMin: %d", result); + ALOGE("getDesiredDisplayModeSpecs failed to read primaryRefreshRateMin: %d", result); return result; } result = reply.readFloat(outPrimaryRefreshRateMax); if (result != NO_ERROR) { - ALOGE("getDesiredDisplayConfigSpecs failed to read primaryRefreshRateMax: %d", result); + ALOGE("getDesiredDisplayModeSpecs failed to read primaryRefreshRateMax: %d", result); return result; } result = reply.readFloat(outAppRequestRefreshRateMin); if (result != NO_ERROR) { - ALOGE("getDesiredDisplayConfigSpecs failed to read appRequestRefreshRateMin: %d", - result); + ALOGE("getDesiredDisplayModeSpecs failed to read appRequestRefreshRateMin: %d", result); return result; } result = reply.readFloat(outAppRequestRefreshRateMax); if (result != NO_ERROR) { - ALOGE("getDesiredDisplayConfigSpecs failed to read appRequestRefreshRateMax: %d", - result); + ALOGE("getDesiredDisplayModeSpecs failed to read appRequestRefreshRateMax: %d", result); return result; } return reply.readInt32(); } - virtual status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken, - bool* outSupport) const { + status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken, + bool* outSupport) const override { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { @@ -1068,7 +1034,7 @@ public: return NO_ERROR; } - virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) { + status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { @@ -1093,29 +1059,29 @@ public: return NO_ERROR; } - virtual status_t notifyPowerHint(int32_t hintId) { + status_t notifyPowerBoost(int32_t boostId) override { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { - ALOGE("notifyPowerHint: failed to write interface token: %d", error); + ALOGE("notifyPowerBoost: failed to write interface token: %d", error); return error; } - error = data.writeInt32(hintId); + error = data.writeInt32(boostId); if (error != NO_ERROR) { - ALOGE("notifyPowerHint: failed to write hintId: %d", error); + ALOGE("notifyPowerBoost: failed to write boostId: %d", error); return error; } - error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_HINT, data, &reply, + error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_BOOST, data, &reply, IBinder::FLAG_ONEWAY); if (error != NO_ERROR) { - ALOGE("notifyPowerHint: failed to transact: %d", error); + ALOGE("notifyPowerBoost: failed to transact: %d", error); return error; } return NO_ERROR; } - virtual status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor, - float lightPosY, float lightPosZ, float lightRadius) { + status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor, + float lightPosY, float lightPosZ, float lightRadius) override { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { @@ -1143,8 +1109,8 @@ public: return NO_ERROR; } - virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate, - int8_t compatibility) { + status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate, + int8_t compatibility, bool shouldBeSeamless) override { Parcel data, reply; status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (err != NO_ERROR) { @@ -1170,6 +1136,12 @@ public: return err; } + err = data.writeBool(shouldBeSeamless); + if (err != NO_ERROR) { + ALOGE("setFrameRate: failed writing bool: %s (%d)", strerror(-err), -err); + return err; + } + err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply); if (err != NO_ERROR) { ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err); @@ -1179,7 +1151,7 @@ public: return reply.readInt32(); } - virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) { + status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override { if (!outToken) return BAD_VALUE; Parcel data, reply; @@ -1213,6 +1185,68 @@ public: return NO_ERROR; } + + status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface, + const FrameTimelineInfo& frameTimelineInfo) override { + Parcel data, reply; + status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (err != NO_ERROR) { + ALOGE("%s: failed writing interface token: %s (%d)", __func__, strerror(-err), -err); + return err; + } + + err = data.writeStrongBinder(IInterface::asBinder(surface)); + if (err != NO_ERROR) { + ALOGE("%s: failed writing strong binder: %s (%d)", __func__, strerror(-err), -err); + return err; + } + + SAFE_PARCEL(frameTimelineInfo.write, data); + + err = remote()->transact(BnSurfaceComposer::SET_FRAME_TIMELINE_INFO, data, &reply); + if (err != NO_ERROR) { + ALOGE("%s: failed to transact: %s (%d)", __func__, strerror(-err), err); + return err; + } + + return reply.readInt32(); + } + + status_t addTransactionTraceListener( + const sp<gui::ITransactionTraceListener>& listener) override { + Parcel data, reply; + SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); + SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener)); + + return remote()->transact(BnSurfaceComposer::ADD_TRANSACTION_TRACE_LISTENER, data, &reply); + } + + /** + * Get priority of the RenderEngine in surface flinger. + */ + int getGPUContextPriority() override { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + status_t err = + remote()->transact(BnSurfaceComposer::GET_GPU_CONTEXT_PRIORITY, data, &reply); + if (err != NO_ERROR) { + ALOGE("getGPUContextPriority failed to read data: %s (%d)", strerror(-err), err); + return 0; + } + return reply.readInt32(); + } + + status_t getExtraBufferCount(int* extraBuffers) const override { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + status_t err = remote()->transact(BnSurfaceComposer::GET_EXTRA_BUFFER_COUNT, data, &reply); + if (err != NO_ERROR) { + ALOGE("getExtraBufferCount failed to read data: %s (%d)", strerror(-err), err); + return err; + } + + return reply.readInt32(extraBuffers); + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -1236,135 +1270,98 @@ status_t BnSurfaceComposer::onTransact( case SET_TRANSACTION_STATE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - size_t count = data.readUint32(); - if (count > data.dataSize()) { - return BAD_VALUE; - } + FrameTimelineInfo frameTimelineInfo; + SAFE_PARCEL(frameTimelineInfo.read, data); + + uint32_t count = 0; + SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize()); Vector<ComposerState> state; state.setCapacity(count); for (size_t i = 0; i < count; i++) { ComposerState s; - if (s.read(data) == BAD_VALUE) { - return BAD_VALUE; - } + SAFE_PARCEL(s.read, data); state.add(s); } - count = data.readUint32(); - if (count > data.dataSize()) { - return BAD_VALUE; - } + SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize()); DisplayState d; Vector<DisplayState> displays; displays.setCapacity(count); for (size_t i = 0; i < count; i++) { - if (d.read(data) == BAD_VALUE) { - return BAD_VALUE; - } + SAFE_PARCEL(d.read, data); displays.add(d); } - uint32_t stateFlags = data.readUint32(); - sp<IBinder> applyToken = data.readStrongBinder(); + uint32_t stateFlags = 0; + SAFE_PARCEL(data.readUint32, &stateFlags); + sp<IBinder> applyToken; + SAFE_PARCEL(data.readStrongBinder, &applyToken); InputWindowCommands inputWindowCommands; - inputWindowCommands.read(data); + SAFE_PARCEL(inputWindowCommands.read, data); - int64_t desiredPresentTime = data.readInt64(); + int64_t desiredPresentTime = 0; + bool isAutoTimestamp = true; + SAFE_PARCEL(data.readInt64, &desiredPresentTime); + SAFE_PARCEL(data.readBool, &isAutoTimestamp); client_cache_t uncachedBuffer; - uncachedBuffer.token = data.readStrongBinder(); - uncachedBuffer.id = data.readUint64(); + sp<IBinder> tmpBinder; + SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder); + uncachedBuffer.token = tmpBinder; + SAFE_PARCEL(data.readUint64, &uncachedBuffer.id); - bool hasListenerCallbacks = data.readBool(); + bool hasListenerCallbacks = false; + SAFE_PARCEL(data.readBool, &hasListenerCallbacks); std::vector<ListenerCallbacks> listenerCallbacks; - int32_t listenersSize = data.readInt32(); + int32_t listenersSize = 0; + SAFE_PARCEL_READ_SIZE(data.readInt32, &listenersSize, data.dataSize()); for (int32_t i = 0; i < listenersSize; i++) { - auto listener = data.readStrongBinder(); + SAFE_PARCEL(data.readStrongBinder, &tmpBinder); std::vector<CallbackId> callbackIds; - data.readInt64Vector(&callbackIds); - listenerCallbacks.emplace_back(listener, callbackIds); + SAFE_PARCEL(data.readInt64Vector, &callbackIds); + listenerCallbacks.emplace_back(tmpBinder, callbackIds); } - setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands, - desiredPresentTime, uncachedBuffer, hasListenerCallbacks, - listenerCallbacks); - return NO_ERROR; + + uint64_t transactionId = -1; + SAFE_PARCEL(data.readUint64, &transactionId); + + return setTransactionState(frameTimelineInfo, state, displays, stateFlags, applyToken, + inputWindowCommands, desiredPresentTime, isAutoTimestamp, + uncachedBuffer, hasListenerCallbacks, listenerCallbacks, + transactionId); } case BOOT_FINISHED: { CHECK_INTERFACE(ISurfaceComposer, data, reply); bootFinished(); return NO_ERROR; } - case CAPTURE_SCREEN: { + case CAPTURE_DISPLAY: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> display = data.readStrongBinder(); - ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32()); - ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32()); - sp<GraphicBuffer> outBuffer; - Rect sourceCrop(Rect::EMPTY_RECT); - data.read(sourceCrop); - uint32_t reqWidth = data.readUint32(); - uint32_t reqHeight = data.readUint32(); - bool useIdentityTransform = static_cast<bool>(data.readInt32()); - int32_t rotation = data.readInt32(); - bool captureSecureLayers = static_cast<bool>(data.readInt32()); - - bool capturedSecureLayers = false; - status_t res = captureScreen(display, &outBuffer, capturedSecureLayers, reqDataspace, - reqPixelFormat, sourceCrop, reqWidth, reqHeight, - useIdentityTransform, ui::toRotation(rotation), - captureSecureLayers); - - reply->writeInt32(res); - if (res == NO_ERROR) { - reply->write(*outBuffer); - reply->writeBool(capturedSecureLayers); - } - return NO_ERROR; + DisplayCaptureArgs args; + sp<IScreenCaptureListener> captureListener; + SAFE_PARCEL(args.read, data); + SAFE_PARCEL(data.readStrongBinder, &captureListener); + + return captureDisplay(args, captureListener); } - case CAPTURE_SCREEN_BY_ID: { + case CAPTURE_DISPLAY_BY_ID: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - uint64_t displayOrLayerStack = data.readUint64(); - ui::Dataspace outDataspace = ui::Dataspace::V0_SRGB; - sp<GraphicBuffer> outBuffer; - status_t res = captureScreen(displayOrLayerStack, &outDataspace, &outBuffer); - reply->writeInt32(res); - if (res == NO_ERROR) { - reply->writeInt32(static_cast<int32_t>(outDataspace)); - reply->write(*outBuffer); - } - return NO_ERROR; + uint64_t displayOrLayerStack = 0; + sp<IScreenCaptureListener> captureListener; + SAFE_PARCEL(data.readUint64, &displayOrLayerStack); + SAFE_PARCEL(data.readStrongBinder, &captureListener); + + return captureDisplay(displayOrLayerStack, captureListener); } case CAPTURE_LAYERS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> layerHandleBinder = data.readStrongBinder(); - ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32()); - ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32()); - sp<GraphicBuffer> outBuffer; - Rect sourceCrop(Rect::EMPTY_RECT); - data.read(sourceCrop); - - std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles; - int numExcludeHandles = data.readInt32(); - if (numExcludeHandles >= static_cast<int>(MAX_LAYERS)) { - return BAD_VALUE; - } - excludeHandles.reserve(numExcludeHandles); - for (int i = 0; i < numExcludeHandles; i++) { - excludeHandles.emplace(data.readStrongBinder()); - } + LayerCaptureArgs args; + sp<IScreenCaptureListener> captureListener; + SAFE_PARCEL(args.read, data); + SAFE_PARCEL(data.readStrongBinder, &captureListener); - float frameScale = data.readFloat(); - bool childrenOnly = data.readBool(); - - status_t res = - captureLayers(layerHandleBinder, &outBuffer, reqDataspace, reqPixelFormat, - sourceCrop, excludeHandles, frameScale, childrenOnly); - reply->writeInt32(res); - if (res == NO_ERROR) { - reply->write(*outBuffer); - } - return NO_ERROR; + return captureLayers(args, captureListener); } case AUTHENTICATE_SURFACE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); @@ -1396,19 +1393,22 @@ status_t BnSurfaceComposer::onTransact( case CREATE_DISPLAY_EVENT_CONNECTION: { CHECK_INTERFACE(ISurfaceComposer, data, reply); auto vsyncSource = static_cast<ISurfaceComposer::VsyncSource>(data.readInt32()); - auto configChanged = static_cast<ISurfaceComposer::ConfigChanged>(data.readInt32()); + EventRegistrationFlags eventRegistration = + static_cast<EventRegistration>(data.readUint32()); sp<IDisplayEventConnection> connection( - createDisplayEventConnection(vsyncSource, configChanged)); + createDisplayEventConnection(vsyncSource, eventRegistration)); reply->writeStrongBinder(IInterface::asBinder(connection)); return NO_ERROR; } case CREATE_DISPLAY: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - String8 displayName = data.readString8(); - bool secure = bool(data.readInt32()); - sp<IBinder> display(createDisplay(displayName, secure)); - reply->writeStrongBinder(display); + String8 displayName; + SAFE_PARCEL(data.readString8, &displayName); + bool secure = false; + SAFE_PARCEL(data.readBool, &secure); + sp<IBinder> display = createDisplay(displayName, secure); + SAFE_PARCEL(reply->writeStrongBinder, display); return NO_ERROR; } case DESTROY_DISPLAY: { @@ -1419,7 +1419,7 @@ status_t BnSurfaceComposer::onTransact( } case GET_PHYSICAL_DISPLAY_TOKEN: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - PhysicalDisplayId displayId = data.readUint64(); + PhysicalDisplayId displayId(data.readUint64()); sp<IBinder> display = getPhysicalDisplayToken(displayId); reply->writeStrongBinder(display); return NO_ERROR; @@ -1442,22 +1442,20 @@ status_t BnSurfaceComposer::onTransact( const sp<IBinder> display = data.readStrongBinder(); const status_t result = getDisplayInfo(display, &info); reply->writeInt32(result); - if (result == NO_ERROR) { - memcpy(reply->writeInplace(sizeof(DisplayInfo)), &info, sizeof(DisplayInfo)); - } - return NO_ERROR; + if (result != NO_ERROR) return result; + return reply->write(info); } - case GET_DISPLAY_CONFIGS: { + case GET_DISPLAY_MODES: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - Vector<DisplayConfig> configs; + Vector<ui::DisplayMode> modes; const sp<IBinder> display = data.readStrongBinder(); - const status_t result = getDisplayConfigs(display, &configs); + const status_t result = getDisplayModes(display, &modes); reply->writeInt32(result); if (result == NO_ERROR) { - reply->writeUint32(static_cast<uint32_t>(configs.size())); - for (size_t c = 0; c < configs.size(); ++c) { - memcpy(reply->writeInplace(sizeof(DisplayConfig)), &configs[c], - sizeof(DisplayConfig)); + reply->writeUint32(static_cast<uint32_t>(modes.size())); + for (size_t i = 0; i < modes.size(); i++) { + memcpy(reply->writeInplace(sizeof(ui::DisplayMode)), &modes[i], + sizeof(ui::DisplayMode)); } } return NO_ERROR; @@ -1474,10 +1472,10 @@ status_t BnSurfaceComposer::onTransact( } return NO_ERROR; } - case GET_ACTIVE_CONFIG: { + case GET_ACTIVE_DISPLAY_MODE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = data.readStrongBinder(); - int id = getActiveConfig(display); + int id = getActiveDisplayModeId(display); reply->writeInt32(id); return NO_ERROR; } @@ -1823,7 +1821,11 @@ status_t BnSurfaceComposer::onTransact( } case GET_PHYSICAL_DISPLAY_IDS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - return reply->writeUint64Vector(getPhysicalDisplayIds()); + std::vector<PhysicalDisplayId> ids = getPhysicalDisplayIds(); + std::vector<uint64_t> rawIds(ids.size()); + std::transform(ids.begin(), ids.end(), rawIds.begin(), + [](PhysicalDisplayId id) { return id.value; }); + return reply->writeUint64Vector(rawIds); } case ADD_REGION_SAMPLING_LISTENER: { CHECK_INTERFACE(ISurfaceComposer, data, reply); @@ -1857,49 +1859,59 @@ status_t BnSurfaceComposer::onTransact( } return removeRegionSamplingListener(listener); } - case SET_DESIRED_DISPLAY_CONFIG_SPECS: { + case SET_DESIRED_DISPLAY_MODE_SPECS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> displayToken = data.readStrongBinder(); - int32_t defaultConfig; - status_t result = data.readInt32(&defaultConfig); + int32_t defaultMode; + status_t result = data.readInt32(&defaultMode); if (result != NO_ERROR) { - ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultConfig: %d", result); + ALOGE("setDesiredDisplayModeSpecs: failed to read defaultMode: %d", result); + return result; + } + if (defaultMode < 0) { + ALOGE("%s: defaultMode must be non-negative but it was %d", __func__, defaultMode); + return BAD_VALUE; + } + bool allowGroupSwitching; + result = data.readBool(&allowGroupSwitching); + if (result != NO_ERROR) { + ALOGE("setDesiredDisplayModeSpecs: failed to read allowGroupSwitching: %d", result); return result; } float primaryRefreshRateMin; result = data.readFloat(&primaryRefreshRateMin); if (result != NO_ERROR) { - ALOGE("setDesiredDisplayConfigSpecs: failed to read primaryRefreshRateMin: %d", + ALOGE("setDesiredDisplayModeSpecs: failed to read primaryRefreshRateMin: %d", result); return result; } float primaryRefreshRateMax; result = data.readFloat(&primaryRefreshRateMax); if (result != NO_ERROR) { - ALOGE("setDesiredDisplayConfigSpecs: failed to read primaryRefreshRateMax: %d", + ALOGE("setDesiredDisplayModeSpecs: failed to read primaryRefreshRateMax: %d", result); return result; } float appRequestRefreshRateMin; result = data.readFloat(&appRequestRefreshRateMin); if (result != NO_ERROR) { - ALOGE("setDesiredDisplayConfigSpecs: failed to read appRequestRefreshRateMin: %d", + ALOGE("setDesiredDisplayModeSpecs: failed to read appRequestRefreshRateMin: %d", result); return result; } float appRequestRefreshRateMax; result = data.readFloat(&appRequestRefreshRateMax); if (result != NO_ERROR) { - ALOGE("setDesiredDisplayConfigSpecs: failed to read appRequestRefreshRateMax: %d", + ALOGE("setDesiredDisplayModeSpecs: failed to read appRequestRefreshRateMax: %d", result); return result; } - result = - setDesiredDisplayConfigSpecs(displayToken, defaultConfig, primaryRefreshRateMin, - primaryRefreshRateMax, appRequestRefreshRateMin, - appRequestRefreshRateMax); + result = setDesiredDisplayModeSpecs(displayToken, static_cast<size_t>(defaultMode), + allowGroupSwitching, primaryRefreshRateMin, + primaryRefreshRateMax, appRequestRefreshRateMin, + appRequestRefreshRateMax); if (result != NO_ERROR) { - ALOGE("setDesiredDisplayConfigSpecs: failed to call setDesiredDisplayConfigSpecs: " + ALOGE("setDesiredDisplayModeSpecs: failed to call setDesiredDisplayModeSpecs: " "%d", result); return result; @@ -1907,53 +1919,60 @@ status_t BnSurfaceComposer::onTransact( reply->writeInt32(result); return result; } - case GET_DESIRED_DISPLAY_CONFIG_SPECS: { + case GET_DESIRED_DISPLAY_MODE_SPECS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> displayToken = data.readStrongBinder(); - int32_t defaultConfig; + size_t defaultMode; + bool allowGroupSwitching; float primaryRefreshRateMin; float primaryRefreshRateMax; float appRequestRefreshRateMin; float appRequestRefreshRateMax; status_t result = - getDesiredDisplayConfigSpecs(displayToken, &defaultConfig, - &primaryRefreshRateMin, &primaryRefreshRateMax, - &appRequestRefreshRateMin, - &appRequestRefreshRateMax); + getDesiredDisplayModeSpecs(displayToken, &defaultMode, &allowGroupSwitching, + &primaryRefreshRateMin, &primaryRefreshRateMax, + &appRequestRefreshRateMin, + &appRequestRefreshRateMax); if (result != NO_ERROR) { - ALOGE("getDesiredDisplayConfigSpecs: failed to get getDesiredDisplayConfigSpecs: " + ALOGE("getDesiredDisplayModeSpecs: failed to get getDesiredDisplayModeSpecs: " "%d", result); return result; } - result = reply->writeInt32(defaultConfig); + result = reply->writeInt32(static_cast<int32_t>(defaultMode)); if (result != NO_ERROR) { - ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultConfig: %d", result); + ALOGE("getDesiredDisplayModeSpecs: failed to write defaultMode: %d", result); + return result; + } + result = reply->writeBool(allowGroupSwitching); + if (result != NO_ERROR) { + ALOGE("getDesiredDisplayModeSpecs: failed to write allowGroupSwitching: %d", + result); return result; } result = reply->writeFloat(primaryRefreshRateMin); if (result != NO_ERROR) { - ALOGE("getDesiredDisplayConfigSpecs: failed to write primaryRefreshRateMin: %d", + ALOGE("getDesiredDisplayModeSpecs: failed to write primaryRefreshRateMin: %d", result); return result; } result = reply->writeFloat(primaryRefreshRateMax); if (result != NO_ERROR) { - ALOGE("getDesiredDisplayConfigSpecs: failed to write primaryRefreshRateMax: %d", + ALOGE("getDesiredDisplayModeSpecs: failed to write primaryRefreshRateMax: %d", result); return result; } result = reply->writeFloat(appRequestRefreshRateMin); if (result != NO_ERROR) { - ALOGE("getDesiredDisplayConfigSpecs: failed to write appRequestRefreshRateMin: %d", + ALOGE("getDesiredDisplayModeSpecs: failed to write appRequestRefreshRateMin: %d", result); return result; } result = reply->writeFloat(appRequestRefreshRateMax); if (result != NO_ERROR) { - ALOGE("getDesiredDisplayConfigSpecs: failed to write appRequestRefreshRateMax: %d", + ALOGE("getDesiredDisplayModeSpecs: failed to write appRequestRefreshRateMax: %d", result); return result; } @@ -1989,15 +2008,15 @@ status_t BnSurfaceComposer::onTransact( } return setDisplayBrightness(displayToken, brightness); } - case NOTIFY_POWER_HINT: { + case NOTIFY_POWER_BOOST: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - int32_t hintId; - status_t error = data.readInt32(&hintId); + int32_t boostId; + status_t error = data.readInt32(&boostId); if (error != NO_ERROR) { - ALOGE("notifyPowerHint: failed to read hintId: %d", error); + ALOGE("notifyPowerBoost: failed to read boostId: %d", error); return error; } - return notifyPowerHint(hintId); + return notifyPowerBoost(boostId); } case SET_GLOBAL_SHADOW_SETTINGS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); @@ -2044,7 +2063,13 @@ status_t BnSurfaceComposer::onTransact( ALOGE("setFrameRate: failed to read byte: %s (%d)", strerror(-err), -err); return err; } - status_t result = setFrameRate(surface, frameRate, compatibility); + bool shouldBeSeamless; + err = data.readBool(&shouldBeSeamless); + if (err != NO_ERROR) { + ALOGE("setFrameRate: failed to read bool: %s (%d)", strerror(-err), -err); + return err; + } + status_t result = setFrameRate(surface, frameRate, compatibility, shouldBeSeamless); reply->writeInt32(result); return NO_ERROR; } @@ -2058,6 +2083,52 @@ status_t BnSurfaceComposer::onTransact( } return NO_ERROR; } + case SET_FRAME_TIMELINE_INFO: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> binder; + status_t err = data.readStrongBinder(&binder); + if (err != NO_ERROR) { + ALOGE("setFrameTimelineInfo: failed to read strong binder: %s (%d)", strerror(-err), + -err); + return err; + } + sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(binder); + if (!surface) { + ALOGE("setFrameTimelineInfo: failed to cast to IGraphicBufferProducer: %s (%d)", + strerror(-err), -err); + return err; + } + + FrameTimelineInfo frameTimelineInfo; + SAFE_PARCEL(frameTimelineInfo.read, data); + + status_t result = setFrameTimelineInfo(surface, frameTimelineInfo); + reply->writeInt32(result); + return NO_ERROR; + } + case ADD_TRANSACTION_TRACE_LISTENER: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<gui::ITransactionTraceListener> listener; + SAFE_PARCEL(data.readStrongBinder, &listener); + + return addTransactionTraceListener(listener); + } + case GET_GPU_CONTEXT_PRIORITY: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + int priority = getGPUContextPriority(); + SAFE_PARCEL(reply->writeInt32, priority); + return NO_ERROR; + } + case GET_EXTRA_BUFFER_COUNT: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + int extraBuffers = 0; + int err = getExtraBufferCount(&extraBuffers); + if (err != NO_ERROR) { + return err; + } + SAFE_PARCEL(reply->writeInt32, extraBuffers); + return NO_ERROR; + } default: { return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp index 621cf5950b..5e7a7ec67b 100644 --- a/libs/gui/ISurfaceComposerClient.cpp +++ b/libs/gui/ISurfaceComposerClient.cpp @@ -50,12 +50,12 @@ 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, - uint32_t* outTransformHint) override { + int32_t* outLayerId, 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, outLayerId, outTransformHint); } @@ -63,14 +63,14 @@ public: PixelFormat format, uint32_t flags, const sp<IGraphicBufferProducer>& parent, LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, + sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, 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, - outTransformHint); + outLayerId, outTransformHint); } status_t clearLayerFrameStats(const sp<IBinder>& handle) const override { @@ -85,10 +85,11 @@ public: outStats); } - status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) override { + status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle, + int32_t* outLayerId) override { return callRemote<decltype(&ISurfaceComposerClient::mirrorSurface)>(Tag::MIRROR_SURFACE, mirrorFromHandle, - outHandle); + outHandle, outLayerId); } }; diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp index 69f7894d07..0ded9361bf 100644 --- a/libs/gui/ITransactionCompletedListener.cpp +++ b/libs/gui/ITransactionCompletedListener.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "ITransactionCompletedListener" //#define LOG_NDEBUG 0 +#include <gui/LayerState.h> +#include <gui/ISurfaceComposer.h> #include <gui/ITransactionCompletedListener.h> namespace android { @@ -90,61 +92,59 @@ status_t FrameEventHistoryStats::readFromParcel(const Parcel* input) { return err; } +JankData::JankData() + : frameVsyncId(FrameTimelineInfo::INVALID_VSYNC_ID), jankType(JankType::None) {} + +status_t JankData::writeToParcel(Parcel* output) const { + SAFE_PARCEL(output->writeInt64, frameVsyncId); + SAFE_PARCEL(output->writeInt32, jankType); + return NO_ERROR; +} + +status_t JankData::readFromParcel(const Parcel* input) { + SAFE_PARCEL(input->readInt64, &frameVsyncId); + SAFE_PARCEL(input->readInt32, &jankType); + return NO_ERROR; +} + status_t SurfaceStats::writeToParcel(Parcel* output) const { - status_t err = output->writeStrongBinder(surfaceControl); - if (err != NO_ERROR) { - return err; - } - err = output->writeInt64(acquireTime); - if (err != NO_ERROR) { - return err; - } + SAFE_PARCEL(output->writeStrongBinder, surfaceControl); + SAFE_PARCEL(output->writeInt64, acquireTime); if (previousReleaseFence) { - err = output->writeBool(true); - if (err != NO_ERROR) { - return err; - } - err = output->write(*previousReleaseFence); + SAFE_PARCEL(output->writeBool, true); + SAFE_PARCEL(output->write, *previousReleaseFence); } else { - err = output->writeBool(false); + SAFE_PARCEL(output->writeBool, false); } - err = output->writeUint32(transformHint); - if (err != NO_ERROR) { - return err; + SAFE_PARCEL(output->writeUint32, transformHint); + SAFE_PARCEL(output->writeParcelable, eventStats); + SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankData.size())); + for (const auto& data : jankData) { + SAFE_PARCEL(output->writeParcelable, data); } - - err = output->writeParcelable(eventStats); - return err; + return NO_ERROR; } status_t SurfaceStats::readFromParcel(const Parcel* input) { - status_t err = input->readStrongBinder(&surfaceControl); - if (err != NO_ERROR) { - return err; - } - err = input->readInt64(&acquireTime); - if (err != NO_ERROR) { - return err; - } + SAFE_PARCEL(input->readStrongBinder, &surfaceControl); + SAFE_PARCEL(input->readInt64, &acquireTime); bool hasFence = false; - err = input->readBool(&hasFence); - if (err != NO_ERROR) { - return err; - } + SAFE_PARCEL(input->readBool, &hasFence); if (hasFence) { previousReleaseFence = new Fence(); - err = input->read(*previousReleaseFence); - if (err != NO_ERROR) { - return err; - } + SAFE_PARCEL(input->read, *previousReleaseFence); } - err = input->readUint32(&transformHint); - if (err != NO_ERROR) { - return err; + SAFE_PARCEL(input->readUint32, &transformHint); + SAFE_PARCEL(input->readParcelable, &eventStats); + + int32_t jankData_size = 0; + SAFE_PARCEL_READ_SIZE(input->readInt32, &jankData_size, input->dataSize()); + for (int i = 0; i < jankData_size; i++) { + JankData data; + SAFE_PARCEL(input->readParcelable, &data); + jankData.push_back(data); } - - err = input->readParcelable(&eventStats); - return err; + return NO_ERROR; } status_t TransactionStats::writeToParcel(Parcel* output) const { diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp index b3eb9940b2..634d8b7df0 100644 --- a/libs/gui/LayerMetadata.cpp +++ b/libs/gui/LayerMetadata.cpp @@ -17,6 +17,7 @@ #include <android-base/stringprintf.h> #include <binder/Parcel.h> #include <gui/LayerMetadata.h> +#include <inttypes.h> #include "android/view/LayerMetadataKey.h" @@ -113,6 +114,15 @@ void LayerMetadata::setInt32(uint32_t key, int32_t value) { memcpy(data.data(), p.data(), p.dataSize()); } +std::optional<int64_t> LayerMetadata::getInt64(uint32_t key) const { + if (!has(key)) return std::nullopt; + const std::vector<uint8_t>& data = mMap.at(key); + if (data.size() < sizeof(int64_t)) return std::nullopt; + Parcel p; + p.setData(data.data(), data.size()); + return p.readInt64(); +} + std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const { if (!has(key)) return std::string(); switch (static_cast<view::LayerMetadataKey>(key)) { @@ -122,6 +132,10 @@ std::string LayerMetadata::itemToString(uint32_t key, const char* separator) con return StringPrintf("windowType%s%d", separator, getInt32(key, 0)); case view::LayerMetadataKey::METADATA_TASK_ID: return StringPrintf("taskId%s%d", separator, getInt32(key, 0)); + case view::LayerMetadataKey::METADATA_OWNER_PID: + return StringPrintf("ownerPID%s%d", separator, getInt32(key, 0)); + case view::LayerMetadataKey::METADATA_DEQUEUE_TIME: + return StringPrintf("dequeueTime%s%" PRId64, separator, *getInt64(key)); default: return StringPrintf("%d%s%dbytes", key, separator, static_cast<int>(mMap.at(key).size())); diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 0281279d69..f053372cb0 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -16,195 +16,286 @@ #define LOG_TAG "LayerState" +#include <apex/window.h> #include <inttypes.h> -#include <utils/Errors.h> #include <binder/Parcel.h> -#include <gui/ISurfaceComposerClient.h> #include <gui/IGraphicBufferProducer.h> +#include <gui/ISurfaceComposerClient.h> #include <gui/LayerState.h> +#include <utils/Errors.h> #include <cmath> namespace android { +layer_state_t::layer_state_t() + : what(0), + x(0), + y(0), + z(0), + w(0), + h(0), + layerStack(0), + alpha(0), + flags(0), + mask(0), + reserved(0), + crop_legacy(Rect::INVALID_RECT), + cornerRadius(0.0f), + backgroundBlurRadius(0), + barrierFrameNumber(0), + transform(0), + transformToDisplayInverse(false), + crop(Rect::INVALID_RECT), + orientedDisplaySpaceRect(Rect::INVALID_RECT), + dataspace(ui::Dataspace::UNKNOWN), + surfaceDamageRegion(), + api(-1), + colorTransform(mat4()), + bgColorAlpha(0), + bgColorDataspace(ui::Dataspace::UNKNOWN), + colorSpaceAgnostic(false), + shadowRadius(0.0f), + frameRateSelectionPriority(-1), + frameRate(0.0f), + frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT), + shouldBeSeamless(true), + fixedTransformHint(ui::Transform::ROT_INVALID), + frameNumber(0), + frameTimelineInfo(), + autoRefresh(false) { + matrix.dsdx = matrix.dtdy = 1.0f; + matrix.dsdy = matrix.dtdx = 0.0f; + hdrMetadata.validTypes = 0; +} + status_t layer_state_t::write(Parcel& output) const { - output.writeStrongBinder(surface); - output.writeUint64(what); - output.writeFloat(x); - output.writeFloat(y); - output.writeInt32(z); - output.writeUint32(w); - output.writeUint32(h); - output.writeUint32(layerStack); - output.writeFloat(alpha); - output.writeUint32(flags); - output.writeUint32(mask); - *reinterpret_cast<layer_state_t::matrix22_t *>( - output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix; - output.write(crop_legacy); - output.writeStrongBinder(barrierHandle_legacy); - output.writeStrongBinder(reparentHandle); - output.writeUint64(frameNumber_legacy); - output.writeInt32(overrideScalingMode); - output.writeStrongBinder(IInterface::asBinder(barrierGbp_legacy)); - output.writeStrongBinder(relativeLayerHandle); - output.writeStrongBinder(parentHandleForChild); - output.writeFloat(color.r); - output.writeFloat(color.g); - output.writeFloat(color.b); + SAFE_PARCEL(output.writeStrongBinder, surface); + SAFE_PARCEL(output.writeInt32, layerId); + SAFE_PARCEL(output.writeUint64, what); + SAFE_PARCEL(output.writeFloat, x); + SAFE_PARCEL(output.writeFloat, y); + SAFE_PARCEL(output.writeInt32, z); + SAFE_PARCEL(output.writeUint32, w); + SAFE_PARCEL(output.writeUint32, h); + SAFE_PARCEL(output.writeUint32, layerStack); + SAFE_PARCEL(output.writeFloat, alpha); + SAFE_PARCEL(output.writeUint32, flags); + SAFE_PARCEL(output.writeUint32, mask); + SAFE_PARCEL(matrix.write, output); + SAFE_PARCEL(output.write, crop_legacy); + SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, barrierSurfaceControl_legacy); + SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, reparentSurfaceControl); + SAFE_PARCEL(output.writeUint64, barrierFrameNumber); + SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl); + SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild); + SAFE_PARCEL(output.writeFloat, color.r); + SAFE_PARCEL(output.writeFloat, color.g); + SAFE_PARCEL(output.writeFloat, color.b); #ifndef NO_INPUT - inputInfo.write(output); + SAFE_PARCEL(inputHandle->writeToParcel, &output); #endif - output.write(transparentRegion); - output.writeUint32(transform); - output.writeBool(transformToDisplayInverse); - output.write(crop); - output.write(frame); + SAFE_PARCEL(output.write, transparentRegion); + SAFE_PARCEL(output.writeUint32, transform); + SAFE_PARCEL(output.writeBool, transformToDisplayInverse); + SAFE_PARCEL(output.write, crop); + SAFE_PARCEL(output.write, orientedDisplaySpaceRect); + if (buffer) { - output.writeBool(true); - output.write(*buffer); + SAFE_PARCEL(output.writeBool, true); + SAFE_PARCEL(output.write, *buffer); } else { - output.writeBool(false); + SAFE_PARCEL(output.writeBool, false); } + if (acquireFence) { - output.writeBool(true); - output.write(*acquireFence); + SAFE_PARCEL(output.writeBool, true); + SAFE_PARCEL(output.write, *acquireFence); } else { - output.writeBool(false); + SAFE_PARCEL(output.writeBool, false); } - output.writeUint32(static_cast<uint32_t>(dataspace)); - output.write(hdrMetadata); - output.write(surfaceDamageRegion); - output.writeInt32(api); + + SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace)); + SAFE_PARCEL(output.write, hdrMetadata); + SAFE_PARCEL(output.write, surfaceDamageRegion); + SAFE_PARCEL(output.writeInt32, api); + if (sidebandStream) { - output.writeBool(true); - output.writeNativeHandle(sidebandStream->handle()); + SAFE_PARCEL(output.writeBool, true); + SAFE_PARCEL(output.writeNativeHandle, sidebandStream->handle()); } else { - output.writeBool(false); + SAFE_PARCEL(output.writeBool, false); } - memcpy(output.writeInplace(16 * sizeof(float)), - colorTransform.asArray(), 16 * sizeof(float)); - output.writeFloat(cornerRadius); - output.writeUint32(backgroundBlurRadius); - output.writeStrongBinder(cachedBuffer.token.promote()); - output.writeUint64(cachedBuffer.id); - output.writeParcelable(metadata); - - output.writeFloat(bgColorAlpha); - output.writeUint32(static_cast<uint32_t>(bgColorDataspace)); - output.writeBool(colorSpaceAgnostic); - - auto err = output.writeVectorSize(listeners); - if (err) { - return err; - } + SAFE_PARCEL(output.write, colorTransform.asArray(), 16 * sizeof(float)); + SAFE_PARCEL(output.writeFloat, cornerRadius); + SAFE_PARCEL(output.writeUint32, backgroundBlurRadius); + SAFE_PARCEL(output.writeStrongBinder, cachedBuffer.token.promote()); + SAFE_PARCEL(output.writeUint64, cachedBuffer.id); + SAFE_PARCEL(output.writeParcelable, metadata); + SAFE_PARCEL(output.writeFloat, bgColorAlpha); + SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(bgColorDataspace)); + SAFE_PARCEL(output.writeBool, colorSpaceAgnostic); + SAFE_PARCEL(output.writeVectorSize, listeners); for (auto listener : listeners) { - err = output.writeStrongBinder(listener.transactionCompletedListener); - if (err) { - return err; - } - err = output.writeInt64Vector(listener.callbackIds); - if (err) { - return err; - } - } - output.writeFloat(shadowRadius); - output.writeInt32(frameRateSelectionPriority); - output.writeFloat(frameRate); - output.writeByte(frameRateCompatibility); - output.writeUint32(fixedTransformHint); + SAFE_PARCEL(output.writeStrongBinder, listener.transactionCompletedListener); + SAFE_PARCEL(output.writeInt64Vector, listener.callbackIds); + } + SAFE_PARCEL(output.writeFloat, shadowRadius); + SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority); + SAFE_PARCEL(output.writeFloat, frameRate); + SAFE_PARCEL(output.writeByte, frameRateCompatibility); + SAFE_PARCEL(output.writeBool, shouldBeSeamless); + SAFE_PARCEL(output.writeUint32, fixedTransformHint); + SAFE_PARCEL(output.writeUint64, frameNumber); + SAFE_PARCEL(frameTimelineInfo.write, output); + SAFE_PARCEL(output.writeBool, autoRefresh); + + SAFE_PARCEL(output.writeUint32, blurRegions.size()); + for (auto region : blurRegions) { + SAFE_PARCEL(output.writeUint32, region.blurRadius); + SAFE_PARCEL(output.writeFloat, region.cornerRadiusTL); + SAFE_PARCEL(output.writeFloat, region.cornerRadiusTR); + SAFE_PARCEL(output.writeFloat, region.cornerRadiusBL); + SAFE_PARCEL(output.writeFloat, region.cornerRadiusBR); + SAFE_PARCEL(output.writeFloat, region.alpha); + SAFE_PARCEL(output.writeInt32, region.left); + SAFE_PARCEL(output.writeInt32, region.top); + SAFE_PARCEL(output.writeInt32, region.right); + SAFE_PARCEL(output.writeInt32, region.bottom); + } + + SAFE_PARCEL(output.write, stretchEffect); + return NO_ERROR; } status_t layer_state_t::read(const Parcel& input) { - surface = input.readStrongBinder(); - what = input.readUint64(); - x = input.readFloat(); - y = input.readFloat(); - z = input.readInt32(); - w = input.readUint32(); - h = input.readUint32(); - layerStack = input.readUint32(); - alpha = input.readFloat(); - flags = static_cast<uint8_t>(input.readUint32()); - mask = static_cast<uint8_t>(input.readUint32()); - const void* matrix_data = input.readInplace(sizeof(layer_state_t::matrix22_t)); - if (matrix_data) { - matrix = *reinterpret_cast<layer_state_t::matrix22_t const *>(matrix_data); - } else { - return BAD_VALUE; - } - input.read(crop_legacy); - barrierHandle_legacy = input.readStrongBinder(); - reparentHandle = input.readStrongBinder(); - frameNumber_legacy = input.readUint64(); - overrideScalingMode = input.readInt32(); - barrierGbp_legacy = interface_cast<IGraphicBufferProducer>(input.readStrongBinder()); - relativeLayerHandle = input.readStrongBinder(); - parentHandleForChild = input.readStrongBinder(); - color.r = input.readFloat(); - color.g = input.readFloat(); - color.b = input.readFloat(); - + SAFE_PARCEL(input.readNullableStrongBinder, &surface); + SAFE_PARCEL(input.readInt32, &layerId); + SAFE_PARCEL(input.readUint64, &what); + SAFE_PARCEL(input.readFloat, &x); + SAFE_PARCEL(input.readFloat, &y); + SAFE_PARCEL(input.readInt32, &z); + SAFE_PARCEL(input.readUint32, &w); + SAFE_PARCEL(input.readUint32, &h); + SAFE_PARCEL(input.readUint32, &layerStack); + SAFE_PARCEL(input.readFloat, &alpha); + + SAFE_PARCEL(input.readUint32, &flags); + + SAFE_PARCEL(input.readUint32, &mask); + + SAFE_PARCEL(matrix.read, input); + SAFE_PARCEL(input.read, crop_legacy); + SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &barrierSurfaceControl_legacy); + SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &reparentSurfaceControl); + SAFE_PARCEL(input.readUint64, &barrierFrameNumber); + + SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl); + SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild); + + float tmpFloat = 0; + SAFE_PARCEL(input.readFloat, &tmpFloat); + color.r = tmpFloat; + SAFE_PARCEL(input.readFloat, &tmpFloat); + color.g = tmpFloat; + SAFE_PARCEL(input.readFloat, &tmpFloat); + color.b = tmpFloat; #ifndef NO_INPUT - inputInfo = InputWindowInfo::read(input); + SAFE_PARCEL(inputHandle->readFromParcel, &input); #endif - input.read(transparentRegion); - transform = input.readUint32(); - transformToDisplayInverse = input.readBool(); - input.read(crop); - input.read(frame); - buffer = new GraphicBuffer(); - if (input.readBool()) { - input.read(*buffer); - } - acquireFence = new Fence(); - if (input.readBool()) { - input.read(*acquireFence); - } - dataspace = static_cast<ui::Dataspace>(input.readUint32()); - input.read(hdrMetadata); - input.read(surfaceDamageRegion); - api = input.readInt32(); - if (input.readBool()) { - sidebandStream = NativeHandle::create(input.readNativeHandle(), true); + SAFE_PARCEL(input.read, transparentRegion); + SAFE_PARCEL(input.readUint32, &transform); + SAFE_PARCEL(input.readBool, &transformToDisplayInverse); + SAFE_PARCEL(input.read, crop); + SAFE_PARCEL(input.read, orientedDisplaySpaceRect); + + bool tmpBool = false; + SAFE_PARCEL(input.readBool, &tmpBool); + if (tmpBool) { + buffer = new GraphicBuffer(); + SAFE_PARCEL(input.read, *buffer); } - const void* color_transform_data = input.readInplace(16 * sizeof(float)); - if (color_transform_data) { - colorTransform = mat4(static_cast<const float*>(color_transform_data)); - } else { - return BAD_VALUE; + SAFE_PARCEL(input.readBool, &tmpBool); + if (tmpBool) { + acquireFence = new Fence(); + SAFE_PARCEL(input.read, *acquireFence); + } + + uint32_t tmpUint32 = 0; + SAFE_PARCEL(input.readUint32, &tmpUint32); + dataspace = static_cast<ui::Dataspace>(tmpUint32); + + SAFE_PARCEL(input.read, hdrMetadata); + SAFE_PARCEL(input.read, surfaceDamageRegion); + SAFE_PARCEL(input.readInt32, &api); + SAFE_PARCEL(input.readBool, &tmpBool); + if (tmpBool) { + sidebandStream = NativeHandle::create(input.readNativeHandle(), true); } - cornerRadius = input.readFloat(); - backgroundBlurRadius = input.readUint32(); - cachedBuffer.token = input.readStrongBinder(); - cachedBuffer.id = input.readUint64(); - input.readParcelable(&metadata); - bgColorAlpha = input.readFloat(); - bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32()); - colorSpaceAgnostic = input.readBool(); + SAFE_PARCEL(input.read, &colorTransform, 16 * sizeof(float)); + SAFE_PARCEL(input.readFloat, &cornerRadius); + SAFE_PARCEL(input.readUint32, &backgroundBlurRadius); + sp<IBinder> tmpBinder; + SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder); + cachedBuffer.token = tmpBinder; + SAFE_PARCEL(input.readUint64, &cachedBuffer.id); + SAFE_PARCEL(input.readParcelable, &metadata); - int32_t numListeners = input.readInt32(); + SAFE_PARCEL(input.readFloat, &bgColorAlpha); + SAFE_PARCEL(input.readUint32, &tmpUint32); + bgColorDataspace = static_cast<ui::Dataspace>(tmpUint32); + SAFE_PARCEL(input.readBool, &colorSpaceAgnostic); + + int32_t numListeners = 0; + SAFE_PARCEL_READ_SIZE(input.readInt32, &numListeners, input.dataSize()); listeners.clear(); for (int i = 0; i < numListeners; i++) { - auto listener = input.readStrongBinder(); + sp<IBinder> listener; std::vector<CallbackId> callbackIds; - input.readInt64Vector(&callbackIds); + SAFE_PARCEL(input.readNullableStrongBinder, &listener); + SAFE_PARCEL(input.readInt64Vector, &callbackIds); listeners.emplace_back(listener, callbackIds); } - shadowRadius = input.readFloat(); - frameRateSelectionPriority = input.readInt32(); - frameRate = input.readFloat(); - frameRateCompatibility = input.readByte(); - fixedTransformHint = static_cast<ui::Transform::RotationFlags>(input.readUint32()); + SAFE_PARCEL(input.readFloat, &shadowRadius); + SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority); + SAFE_PARCEL(input.readFloat, &frameRate); + SAFE_PARCEL(input.readByte, &frameRateCompatibility); + SAFE_PARCEL(input.readBool, &shouldBeSeamless); + SAFE_PARCEL(input.readUint32, &tmpUint32); + fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32); + SAFE_PARCEL(input.readUint64, &frameNumber); + SAFE_PARCEL(frameTimelineInfo.read, input); + SAFE_PARCEL(input.readBool, &autoRefresh); + + uint32_t numRegions = 0; + SAFE_PARCEL(input.readUint32, &numRegions); + blurRegions.clear(); + for (uint32_t i = 0; i < numRegions; i++) { + BlurRegion region; + SAFE_PARCEL(input.readUint32, ®ion.blurRadius); + SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusTL); + SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusTR); + SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusBL); + SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusBR); + SAFE_PARCEL(input.readFloat, ®ion.alpha); + SAFE_PARCEL(input.readInt32, ®ion.left); + SAFE_PARCEL(input.readInt32, ®ion.top); + SAFE_PARCEL(input.readInt32, ®ion.right); + SAFE_PARCEL(input.readInt32, ®ion.bottom); + blurRegions.push_back(region); + } + + SAFE_PARCEL(input.read, stretchEffect); + return NO_ERROR; } @@ -216,39 +307,43 @@ status_t ComposerState::read(const Parcel& input) { return state.read(input); } - -DisplayState::DisplayState() : - what(0), - layerStack(0), - viewport(Rect::EMPTY_RECT), - frame(Rect::EMPTY_RECT), - width(0), - height(0) { -} +DisplayState::DisplayState() + : what(0), + layerStack(0), + layerStackSpaceRect(Rect::EMPTY_RECT), + orientedDisplaySpaceRect(Rect::EMPTY_RECT), + width(0), + height(0) {} status_t DisplayState::write(Parcel& output) const { - output.writeStrongBinder(token); - output.writeStrongBinder(IInterface::asBinder(surface)); - output.writeUint32(what); - output.writeUint32(layerStack); - output.writeUint32(toRotationInt(orientation)); - output.write(viewport); - output.write(frame); - output.writeUint32(width); - output.writeUint32(height); + SAFE_PARCEL(output.writeStrongBinder, token); + SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(surface)); + SAFE_PARCEL(output.writeUint32, what); + SAFE_PARCEL(output.writeUint32, layerStack); + SAFE_PARCEL(output.writeUint32, toRotationInt(orientation)); + SAFE_PARCEL(output.write, layerStackSpaceRect); + SAFE_PARCEL(output.write, orientedDisplaySpaceRect); + SAFE_PARCEL(output.writeUint32, width); + SAFE_PARCEL(output.writeUint32, height); return NO_ERROR; } status_t DisplayState::read(const Parcel& input) { - token = input.readStrongBinder(); - surface = interface_cast<IGraphicBufferProducer>(input.readStrongBinder()); - what = input.readUint32(); - layerStack = input.readUint32(); - orientation = ui::toRotation(input.readUint32()); - input.read(viewport); - input.read(frame); - width = input.readUint32(); - height = input.readUint32(); + SAFE_PARCEL(input.readStrongBinder, &token); + sp<IBinder> tmpBinder; + SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder); + surface = interface_cast<IGraphicBufferProducer>(tmpBinder); + + SAFE_PARCEL(input.readUint32, &what); + SAFE_PARCEL(input.readUint32, &layerStack); + uint32_t tmpUint = 0; + SAFE_PARCEL(input.readUint32, &tmpUint); + orientation = ui::toRotation(tmpUint); + + SAFE_PARCEL(input.read, layerStackSpaceRect); + SAFE_PARCEL(input.read, orientedDisplaySpaceRect); + SAFE_PARCEL(input.readUint32, &width); + SAFE_PARCEL(input.readUint32, &height); return NO_ERROR; } @@ -264,8 +359,8 @@ void DisplayState::merge(const DisplayState& other) { if (other.what & eDisplayProjectionChanged) { what |= eDisplayProjectionChanged; orientation = other.orientation; - viewport = other.viewport; - frame = other.frame; + layerStackSpaceRect = other.layerStackSpaceRect; + orientedDisplaySpaceRect = other.orientedDisplaySpaceRect; } if (other.what & eDisplaySizeChanged) { what |= eDisplaySizeChanged; @@ -324,32 +419,28 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eBackgroundBlurRadiusChanged; backgroundBlurRadius = other.backgroundBlurRadius; } + if (other.what & eBlurRegionsChanged) { + what |= eBlurRegionsChanged; + blurRegions = other.blurRegions; + } if (other.what & eDeferTransaction_legacy) { what |= eDeferTransaction_legacy; - barrierHandle_legacy = other.barrierHandle_legacy; - barrierGbp_legacy = other.barrierGbp_legacy; - frameNumber_legacy = other.frameNumber_legacy; - } - if (other.what & eOverrideScalingModeChanged) { - what |= eOverrideScalingModeChanged; - overrideScalingMode = other.overrideScalingMode; + barrierSurfaceControl_legacy = other.barrierSurfaceControl_legacy; + barrierFrameNumber = other.barrierFrameNumber; } if (other.what & eReparentChildren) { what |= eReparentChildren; - reparentHandle = other.reparentHandle; - } - if (other.what & eDetachChildren) { - what |= eDetachChildren; + reparentSurfaceControl = other.reparentSurfaceControl; } if (other.what & eRelativeLayerChanged) { what |= eRelativeLayerChanged; what &= ~eLayerChanged; z = other.z; - relativeLayerHandle = other.relativeLayerHandle; + relativeLayerSurfaceControl = other.relativeLayerSurfaceControl; } if (other.what & eReparent) { what |= eReparent; - parentHandleForChild = other.parentHandleForChild; + parentSurfaceControlForChild = other.parentSurfaceControlForChild; } if (other.what & eDestroySurface) { what |= eDestroySurface; @@ -368,7 +459,7 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eFrameChanged) { what |= eFrameChanged; - frame = other.frame; + orientedDisplaySpaceRect = other.orientedDisplaySpaceRect; } if (other.what & eBufferChanged) { what |= eBufferChanged; @@ -409,7 +500,7 @@ void layer_state_t::merge(const layer_state_t& other) { #ifndef NO_INPUT if (other.what & eInputInfoChanged) { what |= eInputInfoChanged; - inputInfo = other.inputInfo; + inputHandle = new InputWindowHandle(*other.inputHandle); } #endif @@ -439,11 +530,24 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eFrameRateChanged; frameRate = other.frameRate; frameRateCompatibility = other.frameRateCompatibility; + shouldBeSeamless = other.shouldBeSeamless; } if (other.what & eFixedTransformHintChanged) { what |= eFixedTransformHintChanged; fixedTransformHint = other.fixedTransformHint; } + if (other.what & eFrameNumberChanged) { + what |= eFrameNumberChanged; + frameNumber = other.frameNumber; + } + if (other.what & eFrameTimelineInfoChanged) { + what |= eFrameTimelineInfoChanged; + frameTimelineInfo.merge(other.frameTimelineInfo); + } + if (other.what & eAutoRefreshChanged) { + what |= eAutoRefreshChanged; + autoRefresh = other.autoRefresh; + } if ((other.what & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " "other.what=0x%" PRIu64 " what=0x%" PRIu64, @@ -451,25 +555,69 @@ void layer_state_t::merge(const layer_state_t& other) { } } +status_t layer_state_t::matrix22_t::write(Parcel& output) const { + SAFE_PARCEL(output.writeFloat, dsdx); + SAFE_PARCEL(output.writeFloat, dtdx); + SAFE_PARCEL(output.writeFloat, dtdy); + SAFE_PARCEL(output.writeFloat, dsdy); + return NO_ERROR; +} + +status_t layer_state_t::matrix22_t::read(const Parcel& input) { + SAFE_PARCEL(input.readFloat, &dsdx); + SAFE_PARCEL(input.readFloat, &dtdx); + SAFE_PARCEL(input.readFloat, &dtdy); + SAFE_PARCEL(input.readFloat, &dsdy); + return NO_ERROR; +} + // ------------------------------- InputWindowCommands ---------------------------------------- -void InputWindowCommands::merge(const InputWindowCommands& other) { +bool InputWindowCommands::merge(const InputWindowCommands& other) { + bool changes = false; +#ifndef NO_INPUT + changes |= !other.focusRequests.empty(); + focusRequests.insert(focusRequests.end(), std::make_move_iterator(other.focusRequests.begin()), + std::make_move_iterator(other.focusRequests.end())); +#endif + changes |= other.syncInputWindows && !syncInputWindows; syncInputWindows |= other.syncInputWindows; + return changes; +} + +bool InputWindowCommands::empty() const { + bool empty = true; +#ifndef NO_INPUT + empty = focusRequests.empty() && !syncInputWindows; +#endif + return empty; } void InputWindowCommands::clear() { +#ifndef NO_INPUT + focusRequests.clear(); +#endif syncInputWindows = false; } -void InputWindowCommands::write(Parcel& output) const { - output.writeBool(syncInputWindows); +status_t InputWindowCommands::write(Parcel& output) const { +#ifndef NO_INPUT + SAFE_PARCEL(output.writeParcelableVector, focusRequests); +#endif + SAFE_PARCEL(output.writeBool, syncInputWindows); + return NO_ERROR; } -void InputWindowCommands::read(const Parcel& input) { - syncInputWindows = input.readBool(); +status_t InputWindowCommands::read(const Parcel& input) { +#ifndef NO_INPUT + SAFE_PARCEL(input.readParcelableVector, &focusRequests); +#endif + SAFE_PARCEL(input.readBool, &syncInputWindows); + return NO_ERROR; } -bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunctionName) { +bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunctionName, + bool privileged) { const char* functionName = inFunctionName != nullptr ? inFunctionName : "call"; int floatClassification = std::fpclassify(frameRate); if (frameRate < 0 || floatClassification == FP_INFINITE || floatClassification == FP_NAN) { @@ -478,12 +626,95 @@ bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunc } if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT && - compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE) { - ALOGE("%s failed - invalid compatibility value %d", functionName, compatibility); + compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE && + (!privileged || compatibility != ANATIVEWINDOW_FRAME_RATE_EXACT)) { + ALOGE("%s failed - invalid compatibility value %d privileged: %s", functionName, + compatibility, privileged ? "yes" : "no"); return false; } return true; } +// ---------------------------------------------------------------------------- + +status_t CaptureArgs::write(Parcel& output) const { + SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(pixelFormat)); + SAFE_PARCEL(output.write, sourceCrop); + SAFE_PARCEL(output.writeFloat, frameScaleX); + SAFE_PARCEL(output.writeFloat, frameScaleY); + SAFE_PARCEL(output.writeBool, captureSecureLayers); + SAFE_PARCEL(output.writeInt32, uid); + SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(dataspace)); + SAFE_PARCEL(output.writeBool, allowProtected); + SAFE_PARCEL(output.writeBool, grayscale); + return NO_ERROR; +} + +status_t CaptureArgs::read(const Parcel& input) { + int32_t value = 0; + SAFE_PARCEL(input.readInt32, &value); + pixelFormat = static_cast<ui::PixelFormat>(value); + SAFE_PARCEL(input.read, sourceCrop); + SAFE_PARCEL(input.readFloat, &frameScaleX); + SAFE_PARCEL(input.readFloat, &frameScaleY); + SAFE_PARCEL(input.readBool, &captureSecureLayers); + SAFE_PARCEL(input.readInt32, &uid); + SAFE_PARCEL(input.readInt32, &value); + dataspace = static_cast<ui::Dataspace>(value); + SAFE_PARCEL(input.readBool, &allowProtected); + SAFE_PARCEL(input.readBool, &grayscale); + return NO_ERROR; +} + +status_t DisplayCaptureArgs::write(Parcel& output) const { + SAFE_PARCEL(CaptureArgs::write, output); + + SAFE_PARCEL(output.writeStrongBinder, displayToken); + SAFE_PARCEL(output.writeUint32, width); + SAFE_PARCEL(output.writeUint32, height); + SAFE_PARCEL(output.writeBool, useIdentityTransform); + return NO_ERROR; +} + +status_t DisplayCaptureArgs::read(const Parcel& input) { + SAFE_PARCEL(CaptureArgs::read, input); + + SAFE_PARCEL(input.readStrongBinder, &displayToken); + SAFE_PARCEL(input.readUint32, &width); + SAFE_PARCEL(input.readUint32, &height); + SAFE_PARCEL(input.readBool, &useIdentityTransform); + return NO_ERROR; +} + +status_t LayerCaptureArgs::write(Parcel& output) const { + SAFE_PARCEL(CaptureArgs::write, output); + + SAFE_PARCEL(output.writeStrongBinder, layerHandle); + SAFE_PARCEL(output.writeInt32, excludeHandles.size()); + for (auto el : excludeHandles) { + SAFE_PARCEL(output.writeStrongBinder, el); + } + SAFE_PARCEL(output.writeBool, childrenOnly); + return NO_ERROR; +} + +status_t LayerCaptureArgs::read(const Parcel& input) { + SAFE_PARCEL(CaptureArgs::read, input); + + SAFE_PARCEL(input.readStrongBinder, &layerHandle); + + int32_t numExcludeHandles = 0; + SAFE_PARCEL_READ_SIZE(input.readInt32, &numExcludeHandles, input.dataSize()); + excludeHandles.reserve(numExcludeHandles); + for (int i = 0; i < numExcludeHandles; i++) { + sp<IBinder> binder; + SAFE_PARCEL(input.readStrongBinder, &binder); + excludeHandles.emplace(binder); + } + + SAFE_PARCEL(input.readBool, &childrenOnly); + return NO_ERROR; +} + }; // namespace android diff --git a/libs/gui/QueueBufferInputOutput.cpp b/libs/gui/QueueBufferInputOutput.cpp deleted file mode 100644 index 30f0ef6785..0000000000 --- a/libs/gui/QueueBufferInputOutput.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <inttypes.h> - -#define LOG_TAG "QueueBufferInputOutput" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS -//#define LOG_NDEBUG 0 - -#include <gui/IGraphicBufferProducer.h> - -namespace android { - -constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() { - return sizeof(timestamp) + - sizeof(isAutoTimestamp) + - sizeof(dataSpace) + - sizeof(crop) + - sizeof(scalingMode) + - sizeof(transform) + - sizeof(stickyTransform) + - sizeof(getFrameTimestamps); -} - -IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) { - parcel.read(*this); -} - -size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const { - return minFlattenedSize() + - fence->getFlattenedSize() + - surfaceDamage.getFlattenedSize() + - hdrMetadata.getFlattenedSize(); -} - -size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const { - return fence->getFdCount(); -} - -status_t IGraphicBufferProducer::QueueBufferInput::flatten( - void*& buffer, size_t& size, int*& fds, size_t& count) const -{ - if (size < getFlattenedSize()) { - return NO_MEMORY; - } - - FlattenableUtils::write(buffer, size, timestamp); - FlattenableUtils::write(buffer, size, isAutoTimestamp); - FlattenableUtils::write(buffer, size, dataSpace); - FlattenableUtils::write(buffer, size, crop); - FlattenableUtils::write(buffer, size, scalingMode); - FlattenableUtils::write(buffer, size, transform); - FlattenableUtils::write(buffer, size, stickyTransform); - FlattenableUtils::write(buffer, size, getFrameTimestamps); - - status_t result = fence->flatten(buffer, size, fds, count); - if (result != NO_ERROR) { - return result; - } - result = surfaceDamage.flatten(buffer, size); - if (result != NO_ERROR) { - return result; - } - FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize()); - return hdrMetadata.flatten(buffer, size); -} - -status_t IGraphicBufferProducer::QueueBufferInput::unflatten( - void const*& buffer, size_t& size, int const*& fds, size_t& count) -{ - if (size < minFlattenedSize()) { - return NO_MEMORY; - } - - FlattenableUtils::read(buffer, size, timestamp); - FlattenableUtils::read(buffer, size, isAutoTimestamp); - FlattenableUtils::read(buffer, size, dataSpace); - FlattenableUtils::read(buffer, size, crop); - FlattenableUtils::read(buffer, size, scalingMode); - FlattenableUtils::read(buffer, size, transform); - FlattenableUtils::read(buffer, size, stickyTransform); - FlattenableUtils::read(buffer, size, getFrameTimestamps); - - fence = new Fence(); - status_t result = fence->unflatten(buffer, size, fds, count); - if (result != NO_ERROR) { - return result; - } - result = surfaceDamage.unflatten(buffer, size); - if (result != NO_ERROR) { - return result; - } - FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize()); - return hdrMetadata.unflatten(buffer, size); -} - -//////////////////////////////////////////////////////////////////////// -constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() { - return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) + - sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount); -} -size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const { - return minFlattenedSize() + frameTimestamps.getFlattenedSize(); -} - -size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const { - return frameTimestamps.getFdCount(); -} - -status_t IGraphicBufferProducer::QueueBufferOutput::flatten( - void*& buffer, size_t& size, int*& fds, size_t& count) const -{ - if (size < getFlattenedSize()) { - return NO_MEMORY; - } - - FlattenableUtils::write(buffer, size, width); - FlattenableUtils::write(buffer, size, height); - FlattenableUtils::write(buffer, size, transformHint); - FlattenableUtils::write(buffer, size, numPendingBuffers); - FlattenableUtils::write(buffer, size, nextFrameNumber); - FlattenableUtils::write(buffer, size, bufferReplaced); - FlattenableUtils::write(buffer, size, maxBufferCount); - - return frameTimestamps.flatten(buffer, size, fds, count); -} - -status_t IGraphicBufferProducer::QueueBufferOutput::unflatten( - void const*& buffer, size_t& size, int const*& fds, size_t& count) -{ - if (size < minFlattenedSize()) { - return NO_MEMORY; - } - - FlattenableUtils::read(buffer, size, width); - FlattenableUtils::read(buffer, size, height); - FlattenableUtils::read(buffer, size, transformHint); - FlattenableUtils::read(buffer, size, numPendingBuffers); - FlattenableUtils::read(buffer, size, nextFrameNumber); - FlattenableUtils::read(buffer, size, bufferReplaced); - FlattenableUtils::read(buffer, size, maxBufferCount); - - return frameTimestamps.unflatten(buffer, size, fds, count); -} - -} // namespace android diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp new file mode 100644 index 0000000000..f3849bcdcd --- /dev/null +++ b/libs/gui/ScreenCaptureResults.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2021 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/ScreenCaptureResults.h> + +namespace android::gui { + +status_t ScreenCaptureResults::writeToParcel(android::Parcel* parcel) const { + if (buffer != nullptr) { + SAFE_PARCEL(parcel->writeBool, true); + SAFE_PARCEL(parcel->write, *buffer); + } else { + SAFE_PARCEL(parcel->writeBool, false); + } + + if (fence != Fence::NO_FENCE) { + SAFE_PARCEL(parcel->writeBool, true); + SAFE_PARCEL(parcel->write, *fence); + } else { + SAFE_PARCEL(parcel->writeBool, false); + } + + SAFE_PARCEL(parcel->writeBool, capturedSecureLayers); + SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(capturedDataspace)); + SAFE_PARCEL(parcel->writeInt32, result); + return NO_ERROR; +} + +status_t ScreenCaptureResults::readFromParcel(const android::Parcel* parcel) { + bool hasGraphicBuffer; + SAFE_PARCEL(parcel->readBool, &hasGraphicBuffer); + if (hasGraphicBuffer) { + buffer = new GraphicBuffer(); + SAFE_PARCEL(parcel->read, *buffer); + } + + bool hasFence; + SAFE_PARCEL(parcel->readBool, &hasFence); + if (hasFence) { + fence = new Fence(); + SAFE_PARCEL(parcel->read, *fence); + } + + SAFE_PARCEL(parcel->readBool, &capturedSecureLayers); + uint32_t dataspace = 0; + SAFE_PARCEL(parcel->readUint32, &dataspace); + capturedDataspace = static_cast<ui::Dataspace>(dataspace); + SAFE_PARCEL(parcel->readInt32, &result); + return NO_ERROR; +} + +} // namespace android::gui diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index d6f9e635f3..07fc0694d6 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -63,7 +63,8 @@ bool isInterceptorRegistrationOp(int op) { } // namespace -Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp) +Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp, + const sp<IBinder>& surfaceControlHandle) : mGraphicBufferProducer(bufferProducer), mCrop(Rect::EMPTY_RECT), mBufferAge(0), @@ -111,6 +112,7 @@ Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controll mProducerControlledByApp = controlledByApp; mSwapIntervalZero = false; mMaxBufferCount = NUM_BUFFER_SLOTS; + mSurfaceControlHandle = surfaceControlHandle; } Surface::~Surface() { @@ -616,29 +618,31 @@ private: std::mutex mMutex; }; +void Surface::getDequeueBufferInputLocked( + IGraphicBufferProducer::DequeueBufferInput* dequeueInput) { + LOG_ALWAYS_FATAL_IF(dequeueInput == nullptr, "input is null"); + + dequeueInput->width = mReqWidth ? mReqWidth : mUserWidth; + dequeueInput->height = mReqHeight ? mReqHeight : mUserHeight; + + dequeueInput->format = mReqFormat; + dequeueInput->usage = mReqUsage; + + dequeueInput->getTimestamps = mEnableFrameTimestamps; +} + int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { ATRACE_CALL(); ALOGV("Surface::dequeueBuffer"); - uint32_t reqWidth; - uint32_t reqHeight; - PixelFormat reqFormat; - uint64_t reqUsage; - bool enableFrameTimestamps; - + IGraphicBufferProducer::DequeueBufferInput dqInput; { Mutex::Autolock lock(mMutex); if (mReportRemovedBuffers) { mRemovedBuffers.clear(); } - reqWidth = mReqWidth ? mReqWidth : mUserWidth; - reqHeight = mReqHeight ? mReqHeight : mUserHeight; - - reqFormat = mReqFormat; - reqUsage = mReqUsage; - - enableFrameTimestamps = mEnableFrameTimestamps; + getDequeueBufferInputLocked(&dqInput); if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot != BufferItem::INVALID_BUFFER_SLOT) { @@ -656,16 +660,17 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { nsecs_t startTime = systemTime(); FrameEventHistoryDelta frameTimestamps; - status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight, - reqFormat, reqUsage, &mBufferAge, - enableFrameTimestamps ? &frameTimestamps - : nullptr); + status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, dqInput.width, + dqInput.height, dqInput.format, + dqInput.usage, &mBufferAge, + dqInput.getTimestamps ? + &frameTimestamps : nullptr); mLastDequeueDuration = systemTime() - startTime; if (result < 0) { ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer" "(%d, %d, %d, %#" PRIx64 ") failed: %d", - reqWidth, reqHeight, reqFormat, reqUsage, result); + dqInput.width, dqInput.height, dqInput.format, dqInput.usage, result); return result; } @@ -694,7 +699,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { freeAllBuffers(); } - if (enableFrameTimestamps) { + if (dqInput.getTimestamps) { mFrameEventHistory->applyDelta(frameTimestamps); } @@ -737,6 +742,176 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { return OK; } +int Surface::dequeueBuffers(std::vector<BatchBuffer>* buffers) { + using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput; + using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput; + using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput; + using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput; + + ATRACE_CALL(); + ALOGV("Surface::dequeueBuffers"); + + if (buffers->size() == 0) { + ALOGE("%s: must dequeue at least 1 buffer!", __FUNCTION__); + return BAD_VALUE; + } + + if (mSharedBufferMode) { + ALOGE("%s: batch operation is not supported in shared buffer mode!", + __FUNCTION__); + return INVALID_OPERATION; + } + + size_t numBufferRequested = buffers->size(); + DequeueBufferInput input; + + { + Mutex::Autolock lock(mMutex); + if (mReportRemovedBuffers) { + mRemovedBuffers.clear(); + } + + getDequeueBufferInputLocked(&input); + } // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffers + + std::vector<DequeueBufferInput> dequeueInput(numBufferRequested, input); + std::vector<DequeueBufferOutput> dequeueOutput; + + nsecs_t startTime = systemTime(); + + status_t result = mGraphicBufferProducer->dequeueBuffers(dequeueInput, &dequeueOutput); + + mLastDequeueDuration = systemTime() - startTime; + + if (result < 0) { + ALOGV("%s: IGraphicBufferProducer::dequeueBuffers" + "(%d, %d, %d, %#" PRIx64 ") failed: %d", + __FUNCTION__, input.width, input.height, input.format, input.usage, result); + return result; + } + + std::vector<CancelBufferInput> cancelBufferInputs(numBufferRequested); + std::vector<status_t> cancelBufferOutputs; + for (size_t i = 0; i < numBufferRequested; i++) { + cancelBufferInputs[i].slot = dequeueOutput[i].slot; + cancelBufferInputs[i].fence = dequeueOutput[i].fence; + } + + for (const auto& output : dequeueOutput) { + if (output.result < 0) { + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + ALOGV("%s: IGraphicBufferProducer::dequeueBuffers" + "(%d, %d, %d, %#" PRIx64 ") failed: %d", + __FUNCTION__, input.width, input.height, input.format, input.usage, + output.result); + return output.result; + } + + if (output.slot < 0 || output.slot >= NUM_BUFFER_SLOTS) { + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + ALOGE("%s: IGraphicBufferProducer returned invalid slot number %d", + __FUNCTION__, output.slot); + android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging + return FAILED_TRANSACTION; + } + + if (input.getTimestamps && !output.timestamps.has_value()) { + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + ALOGE("%s: no frame timestamp returns!", __FUNCTION__); + return FAILED_TRANSACTION; + } + + // this should never happen + ALOGE_IF(output.fence == nullptr, + "%s: received null Fence! slot=%d", __FUNCTION__, output.slot); + } + + Mutex::Autolock lock(mMutex); + + // Write this while holding the mutex + mLastDequeueStartTime = startTime; + + std::vector<int32_t> requestBufferSlots; + requestBufferSlots.reserve(numBufferRequested); + // handle release all buffers and request buffers + for (const auto& output : dequeueOutput) { + if (output.result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) { + ALOGV("%s: RELEASE_ALL_BUFFERS during batch operation", __FUNCTION__); + freeAllBuffers(); + break; + } + } + + for (const auto& output : dequeueOutput) { + // Collect slots that needs requesting buffer + sp<GraphicBuffer>& gbuf(mSlots[output.slot].buffer); + if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) { + if (mReportRemovedBuffers && (gbuf != nullptr)) { + mRemovedBuffers.push_back(gbuf); + } + requestBufferSlots.push_back(output.slot); + } + } + + // Batch request Buffer + std::vector<RequestBufferOutput> reqBufferOutput; + if (requestBufferSlots.size() > 0) { + result = mGraphicBufferProducer->requestBuffers(requestBufferSlots, &reqBufferOutput); + if (result != NO_ERROR) { + ALOGE("%s: IGraphicBufferProducer::requestBuffers failed: %d", + __FUNCTION__, result); + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + return result; + } + + // Check if we have any single failure + for (size_t i = 0; i < requestBufferSlots.size(); i++) { + if (reqBufferOutput[i].result != OK) { + ALOGE("%s: IGraphicBufferProducer::requestBuffers failed at %zu-th buffer, slot %d", + __FUNCTION__, i, requestBufferSlots[i]); + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + return reqBufferOutput[i].result; + } + } + + // Fill request buffer results to mSlots + for (size_t i = 0; i < requestBufferSlots.size(); i++) { + mSlots[requestBufferSlots[i]].buffer = reqBufferOutput[i].buffer; + } + } + + for (size_t batchIdx = 0; batchIdx < numBufferRequested; batchIdx++) { + const auto& output = dequeueOutput[batchIdx]; + int slot = output.slot; + sp<GraphicBuffer>& gbuf(mSlots[slot].buffer); + + if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) { + static FenceMonitor hwcReleaseThread("HWC release"); + hwcReleaseThread.queueFence(output.fence); + } + + if (input.getTimestamps) { + mFrameEventHistory->applyDelta(output.timestamps.value()); + } + + if (output.fence->isValid()) { + buffers->at(batchIdx).fenceFd = output.fence->dup(); + if (buffers->at(batchIdx).fenceFd == -1) { + ALOGE("%s: error duping fence: %d", __FUNCTION__, errno); + // dup() should never fail; something is badly wrong. Soldier on + // and hope for the best; the worst that should happen is some + // visible corruption that lasts until the next frame. + } + } else { + buffers->at(batchIdx).fenceFd = -1; + } + + buffers->at(batchIdx).buffer = gbuf.get(); + mDequeuedSlots.insert(slot); + } + return OK; +} + int Surface::cancelBuffer(android_native_buffer_t* buffer, int fenceFd) { ATRACE_CALL(); @@ -767,15 +942,65 @@ int Surface::cancelBuffer(android_native_buffer_t* buffer, return OK; } +int Surface::cancelBuffers(const std::vector<BatchBuffer>& buffers) { + using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput; + ATRACE_CALL(); + ALOGV("Surface::cancelBuffers"); + + if (mSharedBufferMode) { + ALOGE("%s: batch operation is not supported in shared buffer mode!", + __FUNCTION__); + return INVALID_OPERATION; + } + + size_t numBuffers = buffers.size(); + std::vector<CancelBufferInput> cancelBufferInputs(numBuffers); + std::vector<status_t> cancelBufferOutputs; + size_t numBuffersCancelled = 0; + int badSlotResult = 0; + for (size_t i = 0; i < numBuffers; i++) { + int slot = getSlotFromBufferLocked(buffers[i].buffer); + int fenceFd = buffers[i].fenceFd; + if (slot < 0) { + if (fenceFd >= 0) { + close(fenceFd); + } + ALOGE("%s: cannot find slot number for cancelled buffer", __FUNCTION__); + badSlotResult = slot; + } else { + sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); + cancelBufferInputs[numBuffersCancelled].slot = slot; + cancelBufferInputs[numBuffersCancelled++].fence = fence; + } + } + cancelBufferInputs.resize(numBuffersCancelled); + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + + + for (size_t i = 0; i < numBuffersCancelled; i++) { + mDequeuedSlots.erase(cancelBufferInputs[i].slot); + } + + if (badSlotResult != 0) { + return badSlotResult; + } + return OK; +} + int Surface::getSlotFromBufferLocked( android_native_buffer_t* buffer) const { + if (buffer == nullptr) { + ALOGE("%s: input buffer is null!", __FUNCTION__); + return BAD_VALUE; + } + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { if (mSlots[i].buffer != nullptr && mSlots[i].buffer->handle == buffer->handle) { return i; } } - ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle); + ALOGE("%s: unknown buffer: %p", __FUNCTION__, buffer->handle); return BAD_VALUE; } @@ -785,42 +1010,22 @@ int Surface::lockBuffer_DEPRECATED(android_native_buffer_t* buffer __attribute__ return OK; } -int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { - ATRACE_CALL(); - ALOGV("Surface::queueBuffer"); - Mutex::Autolock lock(mMutex); - int64_t timestamp; +void Surface::getQueueBufferInputLocked(android_native_buffer_t* buffer, int fenceFd, + nsecs_t timestamp, IGraphicBufferProducer::QueueBufferInput* out) { bool isAutoTimestamp = false; - if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) { + if (timestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) { timestamp = systemTime(SYSTEM_TIME_MONOTONIC); isAutoTimestamp = true; ALOGV("Surface::queueBuffer making up timestamp: %.2f ms", timestamp / 1000000.0); - } else { - timestamp = mTimestamp; } - int i = getSlotFromBufferLocked(buffer); - if (i < 0) { - if (fenceFd >= 0) { - close(fenceFd); - } - return i; - } - if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) { - if (fenceFd >= 0) { - close(fenceFd); - } - return OK; - } - // Make sure the crop rectangle is entirely inside the buffer. Rect crop(Rect::EMPTY_RECT); mCrop.intersect(Rect(buffer->width, buffer->height), &crop); sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); - IGraphicBufferProducer::QueueBufferOutput output; IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp, static_cast<android_dataspace>(mDataSpace), crop, mScalingMode, mTransform ^ mStickyTransform, fence, mStickyTransform, @@ -891,15 +1096,12 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { input.setSurfaceDamage(flippedRegion); } + *out = input; +} - nsecs_t now = systemTime(); - status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output); - mLastQueueDuration = systemTime() - now; - if (err != OK) { - ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err); - } - - mDequeuedSlots.erase(i); +void Surface::onBufferQueuedLocked(int slot, sp<Fence> fence, + const IGraphicBufferProducer::QueueBufferOutput& output) { + mDequeuedSlots.erase(slot); if (mEnableFrameTimestamps) { mFrameEventHistory->applyDelta(output.frameTimestamps); @@ -933,7 +1135,7 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { mDirtyRegion = Region::INVALID_REGION; } - if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == i) { + if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == slot) { mSharedBufferHasBeenQueued = true; } @@ -943,6 +1145,89 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { static FenceMonitor gpuCompletionThread("GPU completion"); gpuCompletionThread.queueFence(fence); } +} + +int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { + ATRACE_CALL(); + ALOGV("Surface::queueBuffer"); + Mutex::Autolock lock(mMutex); + + int i = getSlotFromBufferLocked(buffer); + if (i < 0) { + if (fenceFd >= 0) { + close(fenceFd); + } + return i; + } + if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) { + if (fenceFd >= 0) { + close(fenceFd); + } + return OK; + } + + IGraphicBufferProducer::QueueBufferOutput output; + IGraphicBufferProducer::QueueBufferInput input; + getQueueBufferInputLocked(buffer, fenceFd, mTimestamp, &input); + sp<Fence> fence = input.fence; + + nsecs_t now = systemTime(); + status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output); + mLastQueueDuration = systemTime() - now; + if (err != OK) { + ALOGE("queueBuffer: error queuing buffer, %d", err); + } + + onBufferQueuedLocked(i, fence, output); + return err; +} + +int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers) { + ATRACE_CALL(); + ALOGV("Surface::queueBuffers"); + Mutex::Autolock lock(mMutex); + + if (mSharedBufferMode) { + ALOGE("%s: batched operation is not supported in shared buffer mode", __FUNCTION__); + return INVALID_OPERATION; + } + + size_t numBuffers = buffers.size(); + std::vector<IGraphicBufferProducer::QueueBufferInput> queueBufferInputs(numBuffers); + std::vector<IGraphicBufferProducer::QueueBufferOutput> queueBufferOutputs; + std::vector<int> bufferSlots(numBuffers, -1); + std::vector<sp<Fence>> bufferFences(numBuffers); + + for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) { + int i = getSlotFromBufferLocked(buffers[batchIdx].buffer); + if (i < 0) { + if (buffers[batchIdx].fenceFd >= 0) { + close(buffers[batchIdx].fenceFd); + } + return i; + } + bufferSlots[batchIdx] = i; + + IGraphicBufferProducer::QueueBufferInput input; + getQueueBufferInputLocked( + buffers[batchIdx].buffer, buffers[batchIdx].fenceFd, buffers[batchIdx].timestamp, + &input); + bufferFences[batchIdx] = input.fence; + queueBufferInputs[batchIdx] = input; + } + + nsecs_t now = systemTime(); + status_t err = mGraphicBufferProducer->queueBuffers(queueBufferInputs, &queueBufferOutputs); + mLastQueueDuration = systemTime() - now; + if (err != OK) { + ALOGE("%s: error queuing buffer, %d", __FUNCTION__, err); + } + + + for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) { + onBufferQueuedLocked(bufferSlots[batchIdx], bufferFences[batchIdx], + queueBufferOutputs[batchIdx]); + } return err; } @@ -983,6 +1268,10 @@ int Surface::query(int what, int* value) const { } break; case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: { + status_t err = mGraphicBufferProducer->query(what, value); + if (err == NO_ERROR) { + return NO_ERROR; + } if (composerService()->authenticateSurfaceTexture( mGraphicBufferProducer)) { *value = 1; @@ -1207,6 +1496,12 @@ int Surface::perform(int operation, va_list args) case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER: res = dispatchGetLastQueuedBuffer(args); break; + case NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO: + res = dispatchSetFrameTimelineInfo(args); + break; + case NATIVE_WINDOW_GET_EXTRA_BUFFER_COUNT: + res = dispatchGetExtraBufferCount(args); + break; default: res = NAME_NOT_FOUND; break; @@ -1438,7 +1733,8 @@ int Surface::dispatchGetLastQueueDuration(va_list args) { int Surface::dispatchSetFrameRate(va_list args) { float frameRate = static_cast<float>(va_arg(args, double)); int8_t compatibility = static_cast<int8_t>(va_arg(args, int)); - return setFrameRate(frameRate, compatibility); + bool shouldBeSeamless = static_cast<bool>(va_arg(args, int)); + return setFrameRate(frameRate, compatibility, shouldBeSeamless); } int Surface::dispatchAddCancelInterceptor(va_list args) { @@ -1499,7 +1795,7 @@ int Surface::dispatchGetLastQueuedBuffer(va_list args) { int result = mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, matrix); if (graphicBuffer != nullptr) { - *buffer = reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()); + *buffer = graphicBuffer->toAHardwareBuffer(); AHardwareBuffer_acquire(*buffer); } else { *buffer = nullptr; @@ -1513,6 +1809,23 @@ int Surface::dispatchGetLastQueuedBuffer(va_list args) { return result; } +int Surface::dispatchSetFrameTimelineInfo(va_list args) { + ATRACE_CALL(); + auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t)); + auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t)); + + ALOGV("Surface::%s", __func__); + return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId}); +} + +int Surface::dispatchGetExtraBufferCount(va_list args) { + ATRACE_CALL(); + auto extraBuffers = static_cast<int*>(va_arg(args, int*)); + + ALOGV("Surface::dispatchGetExtraBufferCount"); + return getExtraBufferCount(extraBuffers); +} + bool Surface::transformToDisplayInverse() { return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) == NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; @@ -2266,7 +2579,7 @@ void Surface::ProducerListenerProxy::onBuffersDiscarded(const std::vector<int32_ mSurfaceListener->onBuffersDiscarded(discardedBufs); } -status_t Surface::setFrameRate(float frameRate, int8_t compatibility) { +status_t Surface::setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) { ATRACE_CALL(); ALOGV("Surface::setFrameRate"); @@ -2274,7 +2587,16 @@ status_t Surface::setFrameRate(float frameRate, int8_t compatibility) { return BAD_VALUE; } - return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility); + return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility, + shouldBeSeamless); +} + +status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) { + return composerService()->setFrameTimelineInfo(mGraphicBufferProducer, frameTimelineInfo); +} + +status_t Surface::getExtraBufferCount(int* extraBuffers) const { + return composerService()->getExtraBufferCount(extraBuffers); } }; // namespace android diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 83bc06997a..27fb2a8cd7 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -39,7 +39,7 @@ #include <gui/LayerState.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> -#include <ui/DisplayConfig.h> +#include <ui/DisplayMode.h> #ifndef NO_INPUT #include <input/InputWindow.h> @@ -125,6 +125,9 @@ sp<SurfaceComposerClient> SurfaceComposerClient::getDefault() { return DefaultComposerClient::getComposerClient(); } +JankDataListener::~JankDataListener() { +} + // --------------------------------------------------------------------------- // TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs @@ -174,6 +177,23 @@ CallbackId TransactionCompletedListener::addCallbackFunction( return callbackId; } +void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener, + sp<SurfaceControl> surfaceControl) { + std::lock_guard<std::mutex> lock(mMutex); + mJankListeners.insert({surfaceControl->getHandle(), listener}); +} + +void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>& listener) { + std::lock_guard<std::mutex> lock(mMutex); + for (auto it = mJankListeners.begin(); it != mJankListeners.end();) { + if (it->second == listener) { + it = mJankListeners.erase(it); + } else { + it++; + } + } +} + void TransactionCompletedListener::addSurfaceControlToCallbacks( const sp<SurfaceControl>& surfaceControl, const std::unordered_set<CallbackId>& callbackIds) { @@ -189,6 +209,7 @@ void TransactionCompletedListener::addSurfaceControlToCallbacks( void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) { std::unordered_map<CallbackId, CallbackTranslation> callbacksMap; + std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap; { std::lock_guard<std::mutex> lock(mMutex); @@ -204,6 +225,7 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener * sp<SurfaceControl> that could possibly exist for the callbacks. */ callbacksMap = mCallbacks; + jankListenersMap = mJankListeners; for (const auto& transactionStats : listenerStats.transactionStats) { for (auto& callbackId : transactionStats.callbackIds) { mCallbacks.erase(callbackId); @@ -236,6 +258,13 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener callbackFunction(transactionStats.latchTime, transactionStats.presentFence, surfaceControlStats); } + for (const auto& surfaceStats : transactionStats.surfaceStats) { + if (surfaceStats.jankData.empty()) continue; + for (auto it = jankListenersMap.find(surfaceStats.surfaceControl); + it != jankListenersMap.end(); it++) { + it->second->onJankDataAvailable(surfaceStats.jankData); + } + } } } @@ -348,15 +377,27 @@ void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId) { // --------------------------------------------------------------------------- +// Initialize transaction id counter used to generate transaction ids +// Transactions will start counting at 1, 0 is used for invalid transactions +std::atomic<uint32_t> SurfaceComposerClient::Transaction::idCounter = 1; + +SurfaceComposerClient::Transaction::Transaction() { + mId = generateId(); +} + SurfaceComposerClient::Transaction::Transaction(const Transaction& other) - : mForceSynchronous(other.mForceSynchronous), + : mId(other.mId), + mForceSynchronous(other.mForceSynchronous), mTransactionNestCount(other.mTransactionNestCount), mAnimation(other.mAnimation), mEarlyWakeup(other.mEarlyWakeup), mExplicitEarlyWakeupStart(other.mExplicitEarlyWakeupStart), mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd), mContainsBuffer(other.mContainsBuffer), - mDesiredPresentTime(other.mDesiredPresentTime) { + mDesiredPresentTime(other.mDesiredPresentTime), + mIsAutoTimestamp(other.mIsAutoTimestamp), + mFrameTimelineInfo(other.mFrameTimelineInfo), + mApplyToken(other.mApplyToken) { mDisplayStates = other.mDisplayStates; mComposerStates = other.mComposerStates; mInputWindowCommands = other.mInputWindowCommands; @@ -372,6 +413,10 @@ SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) { return nullptr; } +int64_t SurfaceComposerClient::Transaction::generateId() { + return (((int64_t)getpid()) << 32) | idCounter++; +} + status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) { const uint32_t forceSynchronous = parcel->readUint32(); const uint32_t transactionNestCount = parcel->readUint32(); @@ -381,7 +426,12 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel const bool explicitEarlyWakeupEnd = parcel->readBool(); const bool containsBuffer = parcel->readBool(); const int64_t desiredPresentTime = parcel->readInt64(); + const bool isAutoTimestamp = parcel->readBool(); + FrameTimelineInfo frameTimelineInfo; + SAFE_PARCEL(frameTimelineInfo.read, *parcel); + sp<IBinder> applyToken; + parcel->readNullableStrongBinder(&applyToken); size_t count = static_cast<size_t>(parcel->readUint32()); if (count > parcel->dataSize()) { return BAD_VALUE; @@ -418,7 +468,7 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel } for (size_t j = 0; j < numSurfaces; j++) { sp<SurfaceControl> surface; - surface = SurfaceControl::readFromParcel(parcel); + SAFE_PARCEL(SurfaceControl::readFromParcel, *parcel, &surface); listenerCallbacks[listener].surfaceControls.insert(surface); } } @@ -430,12 +480,14 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> composerStates; composerStates.reserve(count); for (size_t i = 0; i < count; i++) { - sp<IBinder> surfaceControlHandle = parcel->readStrongBinder(); + sp<IBinder> surfaceControlHandle; + SAFE_PARCEL(parcel->readStrongBinder, &surfaceControlHandle); ComposerState composerState; if (composerState.read(*parcel) == BAD_VALUE) { return BAD_VALUE; } + composerStates[surfaceControlHandle] = composerState; } @@ -451,10 +503,13 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd; mContainsBuffer = containsBuffer; mDesiredPresentTime = desiredPresentTime; + mIsAutoTimestamp = isAutoTimestamp; + mFrameTimelineInfo = frameTimelineInfo; mDisplayStates = displayStates; mListenerCallbacks = listenerCallbacks; mComposerStates = composerStates; mInputWindowCommands = inputWindowCommands; + mApplyToken = applyToken; return NO_ERROR; } @@ -480,6 +535,9 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const parcel->writeBool(mExplicitEarlyWakeupEnd); parcel->writeBool(mContainsBuffer); parcel->writeInt64(mDesiredPresentTime); + parcel->writeBool(mIsAutoTimestamp); + SAFE_PARCEL(mFrameTimelineInfo.write, *parcel); + parcel->writeStrongBinder(mApplyToken); parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size())); for (auto const& displayState : mDisplayStates) { displayState.write(*parcel); @@ -494,13 +552,13 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const } parcel->writeUint32(static_cast<uint32_t>(callbackInfo.surfaceControls.size())); for (auto surfaceControl : callbackInfo.surfaceControls) { - surfaceControl->writeToParcel(parcel); + SAFE_PARCEL(surfaceControl->writeToParcel, *parcel); } } parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size())); - for (auto const& [surfaceHandle, composerState] : mComposerStates) { - parcel->writeStrongBinder(surfaceHandle); + for (auto const& [handle, composerState] : mComposerStates) { + SAFE_PARCEL(parcel->writeStrongBinder, handle); composerState.write(*parcel); } @@ -509,11 +567,11 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) { - for (auto const& [surfaceHandle, composerState] : other.mComposerStates) { - if (mComposerStates.count(surfaceHandle) == 0) { - mComposerStates[surfaceHandle] = composerState; + for (auto const& [handle, composerState] : other.mComposerStates) { + if (mComposerStates.count(handle) == 0) { + mComposerStates[handle] = composerState; } else { - mComposerStates[surfaceHandle].state.merge(composerState.state); + mComposerStates[handle].state.merge(composerState.state); } } @@ -555,6 +613,10 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup; mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart; mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd; + mApplyToken = other.mApplyToken; + + mFrameTimelineInfo.merge(other.mFrameTimelineInfo); + other.clear(); return *this; } @@ -571,7 +633,10 @@ void SurfaceComposerClient::Transaction::clear() { mEarlyWakeup = false; mExplicitEarlyWakeupStart = false; mExplicitEarlyWakeupEnd = false; - mDesiredPresentTime = -1; + mDesiredPresentTime = 0; + mIsAutoTimestamp = true; + mFrameTimelineInfo.clear(); + mApplyToken = nullptr; } void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { @@ -582,7 +647,8 @@ void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { uncacheBuffer.id = cacheId; sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); - sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, false, {}); + sf->setTransactionState(FrameTimelineInfo{}, {}, {}, 0, applyToken, {}, systemTime(), true, + uncacheBuffer, false, {}, 0 /* Undefined transactionId */); } void SurfaceComposerClient::Transaction::cacheBuffers() { @@ -592,7 +658,7 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { size_t count = 0; for (auto& [handle, cs] : mComposerStates) { - layer_state_t* s = getLayerState(handle); + layer_state_t* s = &(mComposerStates[handle].state); if (!(s->what & layer_state_t::eBufferChanged)) { continue; } else if (s->what & layer_state_t::eCachedBufferChanged) { @@ -665,8 +731,6 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { } } - mListenerCallbacks.clear(); - cacheBuffers(); Vector<ComposerState> composerStates; @@ -679,10 +743,7 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { composerStates.add(kv.second); } - mComposerStates.clear(); - - displayStates = mDisplayStates; - mDisplayStates.clear(); + displayStates = std::move(mDisplayStates); if (mForceSynchronous) { flags |= ISurfaceComposer::eSynchronous; @@ -703,18 +764,19 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd; } - mForceSynchronous = false; - mAnimation = false; - mEarlyWakeup = false; - mExplicitEarlyWakeupStart = false; - mExplicitEarlyWakeupEnd = false; + sp<IBinder> applyToken = mApplyToken + ? mApplyToken + : IInterface::asBinder(TransactionCompletedListener::getIInstance()); - sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); - sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands, - mDesiredPresentTime, + sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken, + mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp, {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/, - hasListenerCallbacks, listenerCallbacks); - mInputWindowCommands.clear(); + hasListenerCallbacks, listenerCallbacks, mId); + mId = generateId(); + + // Clear the current states and flags + clear(); + mStatus = NO_ERROR; return NO_ERROR; } @@ -762,11 +824,16 @@ void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupEnd() { mExplicitEarlyWakeupEnd = true; } -layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<IBinder>& handle) { +layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) { + auto handle = sc->getHandle(); + if (mComposerStates.count(handle) == 0) { // we don't have it, add an initialized layer_state to our list ComposerState s; + s.state.surface = handle; + s.state.layerId = sc->getLayerId(); + mComposerStates[handle] = s; } @@ -837,8 +904,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLayer return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelativeLayer(const sp<SurfaceControl>& sc, const sp<IBinder>& relativeTo, - int32_t z) { +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelativeLayer( + const sp<SurfaceControl>& sc, const sp<SurfaceControl>& relativeTo, int32_t z) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; @@ -846,7 +913,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelat } s->what |= layer_state_t::eRelativeLayerChanged; s->what &= ~layer_state_t::eLayerChanged; - s->relativeLayerHandle = relativeTo; + s->relativeLayerSurfaceControl = relativeTo; s->z = z; registerSurfaceControlForCallback(sc); @@ -861,9 +928,9 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFlags mStatus = BAD_INDEX; return *this; } - if ((mask & layer_state_t::eLayerOpaque) || - (mask & layer_state_t::eLayerHidden) || - (mask & layer_state_t::eLayerSecure)) { + if ((mask & layer_state_t::eLayerOpaque) || (mask & layer_state_t::eLayerHidden) || + (mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot) || + (mask & layer_state_t::eEnableBackpressure)) { s->what |= layer_state_t::eFlagsChanged; } s->flags &= ~mask; @@ -990,65 +1057,58 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackg return *this; } -SurfaceComposerClient::Transaction& -SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, - const sp<IBinder>& handle, - uint64_t frameNumber) { +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBlurRegions( + const sp<SurfaceControl>& sc, const std::vector<BlurRegion>& blurRegions) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eDeferTransaction_legacy; - s->barrierHandle_legacy = handle; - s->frameNumber_legacy = frameNumber; - - registerSurfaceControlForCallback(sc); + s->what |= layer_state_t::eBlurRegionsChanged; + s->blurRegions = blurRegions; return *this; } SurfaceComposerClient::Transaction& -SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, - const sp<Surface>& barrierSurface, - uint64_t frameNumber) { +SurfaceComposerClient::Transaction::deferTransactionUntil_legacy( + const sp<SurfaceControl>& sc, const sp<SurfaceControl>& barrierSurfaceControl, + uint64_t frameNumber) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } s->what |= layer_state_t::eDeferTransaction_legacy; - s->barrierGbp_legacy = barrierSurface->getIGraphicBufferProducer(); - s->frameNumber_legacy = frameNumber; + s->barrierSurfaceControl_legacy = barrierSurfaceControl; + s->barrierFrameNumber = frameNumber; registerSurfaceControlForCallback(sc); return *this; } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparentChildren( - const sp<SurfaceControl>& sc, - const sp<IBinder>& newParentHandle) { + const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } s->what |= layer_state_t::eReparentChildren; - s->reparentHandle = newParentHandle; + s->reparentSurfaceControl = newParent; registerSurfaceControlForCallback(sc); return *this; } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparent( - const sp<SurfaceControl>& sc, - const sp<IBinder>& newParentHandle) { + const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } s->what |= layer_state_t::eReparent; - s->parentHandleForChild = newParentHandle; + s->parentSurfaceControlForChild = newParent; registerSurfaceControlForCallback(sc); return *this; @@ -1137,7 +1197,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame return *this; } s->what |= layer_state_t::eFrameChanged; - s->frame = frame; + s->orientedDisplaySpaceRect = frame; registerSurfaceControlForCallback(sc); return *this; @@ -1152,6 +1212,9 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe } s->what |= layer_state_t::eBufferChanged; s->buffer = buffer; + if (mIsAutoTimestamp) { + mDesiredPresentTime = systemTime(); + } registerSurfaceControlForCallback(sc); @@ -1246,6 +1309,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSideb SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime( nsecs_t desiredPresentTime) { mDesiredPresentTime = desiredPresentTime; + mIsAutoTimestamp = false; return *this; } @@ -1308,45 +1372,17 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyPr return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::detachChildren( - const sp<SurfaceControl>& sc) { +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameNumber( + const sp<SurfaceControl>& sc, uint64_t frameNumber) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eDetachChildren; - registerSurfaceControlForCallback(sc); - return *this; -} - -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setOverrideScalingMode( - const sp<SurfaceControl>& sc, int32_t overrideScalingMode) { - layer_state_t* s = getLayerState(sc); - if (!s) { - mStatus = BAD_INDEX; - return *this; - } + s->what |= layer_state_t::eFrameNumberChanged; + s->frameNumber = frameNumber; - switch (overrideScalingMode) { - case NATIVE_WINDOW_SCALING_MODE_FREEZE: - case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: - case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: - case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP: - case -1: - break; - default: - ALOGE("unknown scaling mode: %d", - overrideScalingMode); - mStatus = BAD_VALUE; - return *this; - } - - s->what |= layer_state_t::eOverrideScalingModeChanged; - s->overrideScalingMode = overrideScalingMode; - - registerSurfaceControlForCallback(sc); return *this; } @@ -1359,11 +1395,17 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInput mStatus = BAD_INDEX; return *this; } - s->inputInfo = info; + s->inputHandle = new InputWindowHandle(info); s->what |= layer_state_t::eInputInfoChanged; return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow( + const FocusRequest& request) { + mInputWindowCommands.focusRequests.push_back(request); + return *this; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInputWindows() { mInputWindowCommands.syncInputWindows = true; return *this; @@ -1452,19 +1494,24 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setShado } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate( - const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility) { + const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility, + bool shouldBeSeamless) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - if (!ValidateFrameRate(frameRate, compatibility, "Transaction::setFrameRate")) { + // Allow privileged values as well here, those will be ignored by SF if + // the caller is not privileged + if (!ValidateFrameRate(frameRate, compatibility, "Transaction::setFrameRate", + /*privileged=*/true)) { mStatus = BAD_VALUE; return *this; } s->what |= layer_state_t::eFrameRateChanged; s->frameRate = frameRate; s->frameRateCompatibility = compatibility; + s->shouldBeSeamless = shouldBeSeamless; return *this; } @@ -1484,6 +1531,61 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFixed return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineInfo( + const FrameTimelineInfo& frameTimelineInfo) { + mFrameTimelineInfo = frameTimelineInfo; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineInfo( + const sp<SurfaceControl>& sc, const FrameTimelineInfo& frameTimelineInfo) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eFrameTimelineInfoChanged; + s->frameTimelineInfo = frameTimelineInfo; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAutoRefresh( + const sp<SurfaceControl>& sc, bool autoRefresh) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eAutoRefreshChanged; + s->autoRefresh = autoRefresh; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApplyToken( + const sp<IBinder>& applyToken) { + mApplyToken = applyToken; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setStretchEffect( + const sp<SurfaceControl>& sc, float left, float top, float right, float bottom, float vecX, + float vecY, float maxAmount) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eStretchChanged; + s->stretchEffect = StretchEffect{.area = {left, top, right, bottom}, + .vectorX = vecX, + .vectorY = vecY, + .maxAmount = maxAmount}; + return *this; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { @@ -1530,8 +1632,8 @@ void SurfaceComposerClient::Transaction::setDisplayProjection(const sp<IBinder>& const Rect& displayRect) { DisplayState& s(getDisplayState(token)); s.orientation = orientation; - s.viewport = layerStackRect; - s.frame = displayRect; + s.layerStackSpaceRect = layerStackRect; + s.orientedDisplaySpaceRect = displayRect; s.what |= DisplayState::eDisplayProjectionChanged; mForceSynchronous = true; // TODO: do we actually still need this? } @@ -1599,11 +1701,11 @@ void SurfaceComposerClient::dispose() { sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, - SurfaceControl* parent, + const sp<IBinder>& parentHandle, 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, parentHandle, std::move(metadata), outTransformHint); return s; } @@ -1622,14 +1724,16 @@ sp<SurfaceControl> SurfaceComposerClient::createWithSurfaceParent(const String8& sp<IGraphicBufferProducer> gbp; uint32_t transformHint = 0; + int32_t id = -1; err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp, - std::move(metadata), &handle, &gbp, &transformHint); + std::move(metadata), &handle, &gbp, &id, + &transformHint); if (outTransformHint) { *outTransformHint = transformHint; } ALOGE_IF(err, "SurfaceComposerClient::createWithSurfaceParent error %s", strerror(-err)); if (err == NO_ERROR) { - return new SurfaceControl(this, handle, gbp, transformHint); + return new SurfaceControl(this, handle, gbp, id, transformHint); } } return nullptr; @@ -1638,29 +1742,27 @@ 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, + const sp<IBinder>& parentHandle, + LayerMetadata metadata, uint32_t* outTransformHint) { sp<SurfaceControl> sur; status_t err = mStatus; if (mStatus == NO_ERROR) { sp<IBinder> handle; - sp<IBinder> parentHandle; sp<IGraphicBufferProducer> gbp; - if (parent != nullptr) { - parentHandle = parent->getHandle(); - } - uint32_t transformHint = 0; + int32_t id = -1; err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata), - &handle, &gbp, &transformHint); + &handle, &gbp, &id, &transformHint); + if (outTransformHint) { *outTransformHint = transformHint; } ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err)); if (err == NO_ERROR) { - *outSurface = new SurfaceControl(this, handle, gbp, transformHint); + *outSurface = new SurfaceControl(this, handle, gbp, id, transformHint); } } return err; @@ -1673,9 +1775,10 @@ sp<SurfaceControl> SurfaceComposerClient::mirrorSurface(SurfaceControl* mirrorFr sp<IBinder> handle; sp<IBinder> mirrorFromHandle = mirrorFromSurface->getHandle(); - status_t err = mClient->mirrorSurface(mirrorFromHandle, &handle); + int32_t layer_id = -1; + status_t err = mClient->mirrorSurface(mirrorFromHandle, &handle, &layer_id); if (err == NO_ERROR) { - return new SurfaceControl(this, handle, nullptr, true /* owned */); + return new SurfaceControl(this, handle, nullptr, layer_id, true /* owned */); } return nullptr; } @@ -1716,55 +1819,51 @@ status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display, Displ return ComposerService::getComposerService()->getDisplayInfo(display, info); } -status_t SurfaceComposerClient::getDisplayConfigs(const sp<IBinder>& display, - Vector<DisplayConfig>* configs) { - return ComposerService::getComposerService()->getDisplayConfigs(display, configs); +status_t SurfaceComposerClient::getDisplayModes(const sp<IBinder>& display, + Vector<ui::DisplayMode>* modes) { + return ComposerService::getComposerService()->getDisplayModes(display, modes); } -status_t SurfaceComposerClient::getActiveDisplayConfig(const sp<IBinder>& display, - DisplayConfig* config) { - Vector<DisplayConfig> configs; - status_t result = getDisplayConfigs(display, &configs); +status_t SurfaceComposerClient::getActiveDisplayMode(const sp<IBinder>& display, + ui::DisplayMode* mode) { + Vector<ui::DisplayMode> modes; + status_t result = getDisplayModes(display, &modes); if (result != NO_ERROR) { return result; } - int activeId = getActiveConfig(display); + int activeId = getActiveDisplayModeId(display); if (activeId < 0) { - ALOGE("No active configuration found"); + ALOGE("No active mode found"); return NAME_NOT_FOUND; } - *config = configs[static_cast<size_t>(activeId)]; + *mode = modes[static_cast<size_t>(activeId)]; return NO_ERROR; } -int SurfaceComposerClient::getActiveConfig(const sp<IBinder>& display) { - return ComposerService::getComposerService()->getActiveConfig(display); +int SurfaceComposerClient::getActiveDisplayModeId(const sp<IBinder>& display) { + return ComposerService::getComposerService()->getActiveDisplayModeId(display); } -status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t defaultConfig, - float primaryRefreshRateMin, - float primaryRefreshRateMax, - float appRequestRefreshRateMin, - float appRequestRefreshRateMax) { +status_t SurfaceComposerClient::setDesiredDisplayModeSpecs( + const sp<IBinder>& displayToken, size_t defaultMode, bool allowGroupSwitching, + float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin, + float appRequestRefreshRateMax) { return ComposerService::getComposerService() - ->setDesiredDisplayConfigSpecs(displayToken, defaultConfig, primaryRefreshRateMin, - primaryRefreshRateMax, appRequestRefreshRateMin, - appRequestRefreshRateMax); + ->setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching, + primaryRefreshRateMin, primaryRefreshRateMax, + appRequestRefreshRateMin, appRequestRefreshRateMax); } -status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t* outDefaultConfig, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax) { +status_t SurfaceComposerClient::getDesiredDisplayModeSpecs( + const sp<IBinder>& displayToken, size_t* outDefaultMode, bool* outAllowGroupSwitching, + float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax, + float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax) { return ComposerService::getComposerService() - ->getDesiredDisplayConfigSpecs(displayToken, outDefaultConfig, outPrimaryRefreshRateMin, - outPrimaryRefreshRateMax, outAppRequestRefreshRateMin, - outAppRequestRefreshRateMax); + ->getDesiredDisplayModeSpecs(displayToken, outDefaultMode, outAllowGroupSwitching, + outPrimaryRefreshRateMin, outPrimaryRefreshRateMax, + outAppRequestRefreshRateMin, outAppRequestRefreshRateMax); } status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display, @@ -1893,8 +1992,8 @@ status_t SurfaceComposerClient::setDisplayBrightness(const sp<IBinder>& displayT return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness); } -status_t SurfaceComposerClient::notifyPowerHint(int32_t hintId) { - return ComposerService::getComposerService()->notifyPowerHint(hintId); +status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) { + return ComposerService::getComposerService()->notifyPowerBoost(boostId); } status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColor, @@ -1905,61 +2004,34 @@ status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColo lightRadius); } +int SurfaceComposerClient::getGPUContextPriority() { + return ComposerService::getComposerService()->getGPUContextPriority(); +} + // ---------------------------------------------------------------------------- -status_t ScreenshotClient::capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ui::Rotation rotation, bool captureSecureLayers, - sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers) { +status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs, + const sp<IScreenCaptureListener>& captureListener) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (s == nullptr) return NO_INIT; - status_t ret = s->captureScreen(display, outBuffer, outCapturedSecureLayers, reqDataSpace, - reqPixelFormat, sourceCrop, reqWidth, reqHeight, - useIdentityTransform, rotation, captureSecureLayers); - if (ret != NO_ERROR) { - return ret; - } - return ret; -} -status_t ScreenshotClient::capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ui::Rotation rotation, sp<GraphicBuffer>* outBuffer) { - bool ignored; - return capture(display, reqDataSpace, reqPixelFormat, sourceCrop, reqWidth, reqHeight, - useIdentityTransform, rotation, false, outBuffer, ignored); + return s->captureDisplay(captureArgs, captureListener); } -status_t ScreenshotClient::capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, - sp<GraphicBuffer>* outBuffer) { +status_t ScreenshotClient::captureDisplay(uint64_t displayOrLayerStack, + const sp<IScreenCaptureListener>& captureListener) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (s == nullptr) return NO_INIT; - return s->captureScreen(displayOrLayerStack, outDataspace, outBuffer); -} -status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - float frameScale, sp<GraphicBuffer>* outBuffer) { - sp<ISurfaceComposer> s(ComposerService::getComposerService()); - if (s == nullptr) return NO_INIT; - status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat, - sourceCrop, {}, frameScale, false /* childrenOnly */); - return ret; + return s->captureDisplay(displayOrLayerStack, captureListener); } -status_t ScreenshotClient::captureChildLayers( - const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace, ui::PixelFormat reqPixelFormat, - const Rect& sourceCrop, - const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& excludeHandles, - float frameScale, sp<GraphicBuffer>* outBuffer) { +status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs, + const sp<IScreenCaptureListener>& captureListener) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (s == nullptr) return NO_INIT; - status_t ret = - s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat, sourceCrop, - excludeHandles, frameScale, true /* childrenOnly */); - return ret; + + return s->captureLayers(captureArgs, captureListener); } } // namespace android diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index a332a1f2a8..e842382ded 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -24,6 +24,7 @@ #include <android/native_window.h> #include <utils/Errors.h> +#include <utils/KeyedVector.h> #include <utils/Log.h> #include <utils/threads.h> @@ -46,11 +47,12 @@ namespace android { // ============================================================================ SurfaceControl::SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle, - const sp<IGraphicBufferProducer>& gbp, + const sp<IGraphicBufferProducer>& gbp, int32_t layerId, uint32_t transform) : mClient(client), mHandle(handle), mGraphicBufferProducer(gbp), + mLayerId(layerId), mTransformHint(transform) {} SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) { @@ -58,6 +60,7 @@ SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) { mHandle = other->mHandle; mGraphicBufferProducer = other->mGraphicBufferProducer; mTransformHint = other->mTransformHint; + mLayerId = other->mLayerId; } SurfaceControl::~SurfaceControl() @@ -148,6 +151,10 @@ sp<IBinder> SurfaceControl::getHandle() const return mHandle; } +int32_t SurfaceControl::getLayerId() const { + return mLayerId; +} + sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer() const { Mutex::Autolock _l(mLock); @@ -169,31 +176,60 @@ void SurfaceControl::setTransformHint(uint32_t hint) { 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) { - sp<IBinder> client = parcel->readStrongBinder(); - sp<IBinder> handle = parcel->readStrongBinder(); - if (client == nullptr || handle == nullptr) - { - ALOGE("Invalid parcel"); - return nullptr; - } +status_t SurfaceControl::writeToParcel(Parcel& parcel) { + SAFE_PARCEL(parcel.writeStrongBinder, ISurfaceComposerClient::asBinder(mClient->getClient())); + SAFE_PARCEL(parcel.writeStrongBinder, mHandle); + SAFE_PARCEL(parcel.writeStrongBinder, IGraphicBufferProducer::asBinder(mGraphicBufferProducer)); + SAFE_PARCEL(parcel.writeInt32, mLayerId); + SAFE_PARCEL(parcel.writeUint32, mTransformHint); + + return NO_ERROR; +} + +status_t SurfaceControl::readFromParcel(const Parcel& parcel, + sp<SurfaceControl>* outSurfaceControl) { + sp<IBinder> client; + sp<IBinder> handle; sp<IBinder> gbp; - parcel->readNullableStrongBinder(&gbp); + int32_t layerId; + uint32_t transformHint; + + SAFE_PARCEL(parcel.readStrongBinder, &client); + SAFE_PARCEL(parcel.readStrongBinder, &handle); + SAFE_PARCEL(parcel.readNullableStrongBinder, &gbp); + SAFE_PARCEL(parcel.readInt32, &layerId); + SAFE_PARCEL(parcel.readUint32, &transformHint); - 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), + *outSurfaceControl = + new SurfaceControl(new SurfaceComposerClient( + interface_cast<ISurfaceComposerClient>(client)), + handle.get(), interface_cast<IGraphicBufferProducer>(gbp), layerId, transformHint); + + return NO_ERROR; +} + +status_t SurfaceControl::readNullableFromParcel(const Parcel& parcel, + sp<SurfaceControl>* outSurfaceControl) { + bool isNotNull; + SAFE_PARCEL(parcel.readBool, &isNotNull); + if (isNotNull) { + SAFE_PARCEL(SurfaceControl::readFromParcel, parcel, outSurfaceControl); + } + + return NO_ERROR; +} + +status_t SurfaceControl::writeNullableToParcel(Parcel& parcel, + const sp<SurfaceControl>& surfaceControl) { + auto isNotNull = surfaceControl != nullptr; + SAFE_PARCEL(parcel.writeBool, isNotNull); + if (isNotNull) { + SAFE_PARCEL(surfaceControl->writeToParcel, parcel); + } + + return NO_ERROR; } // ---------------------------------------------------------------------------- diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp index fcae05c8ad..1a8fc1a00a 100644 --- a/libs/gui/SyncFeatures.cpp +++ b/libs/gui/SyncFeatures.cpp @@ -27,8 +27,6 @@ #include <private/gui/SyncFeatures.h> -extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); - namespace android { ANDROID_SINGLETON_STATIC_INSTANCE(SyncFeatures); @@ -40,8 +38,8 @@ SyncFeatures::SyncFeatures() : Singleton<SyncFeatures>(), EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); // This can only be called after EGL has been initialized; otherwise the // check below will abort. - const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); - LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryStringImplementationANDROID failed"); + const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); + LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryString failed"); if (strstr(exts, "EGL_ANDROID_native_fence_sync")) { // This makes GLConsumer use the EGL_ANDROID_native_fence_sync // extension to create Android native fences to signal when all @@ -73,15 +71,7 @@ bool SyncFeatures::useNativeFenceSync() const { return mHasNativeFenceSync; } bool SyncFeatures::useFenceSync() const { -#ifdef DONT_USE_FENCE_SYNC - // on some devices it's better to not use EGL_KHR_fence_sync - // even if they have it - return false; -#else - // currently we shall only attempt to use EGL_KHR_fence_sync if - // USE_FENCE_SYNC is set in our makefile return !mHasNativeFenceSync && mHasFenceSync; -#endif } bool SyncFeatures::useWaitSync() const { return (useNativeFenceSync() || useFenceSync()) && mHasWaitSync; diff --git a/libs/gui/TransactionTracing.cpp b/libs/gui/TransactionTracing.cpp new file mode 100644 index 0000000000..eedc3df009 --- /dev/null +++ b/libs/gui/TransactionTracing.cpp @@ -0,0 +1,53 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gui/TransactionTracing.h" +#include "gui/ISurfaceComposer.h" + +#include <private/gui/ComposerService.h> + +namespace android { + +sp<TransactionTraceListener> TransactionTraceListener::sInstance = nullptr; +std::mutex TransactionTraceListener::sMutex; + +TransactionTraceListener::TransactionTraceListener() {} + +sp<TransactionTraceListener> TransactionTraceListener::getInstance() { + const std::lock_guard<std::mutex> lock(sMutex); + + if (sInstance == nullptr) { + sInstance = new TransactionTraceListener; + + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + sf->addTransactionTraceListener(sInstance); + } + + return sInstance; +} + +binder::Status TransactionTraceListener::onToggled(bool enabled) { + ALOGD("TransactionTraceListener: onToggled listener called"); + mTracingEnabled = enabled; + + return binder::Status::ok(); +} + +bool TransactionTraceListener::isTracingEnabled() { + return mTracingEnabled; +} + +} // namespace android
\ No newline at end of file diff --git a/libs/ui/include/ui/UiConfig.h b/libs/gui/aidl/android/gui/IScreenCaptureListener.aidl index d1d6014a7b..e0f66cd0a1 100644 --- a/libs/ui/include/ui/UiConfig.h +++ b/libs/gui/aidl/android/gui/IScreenCaptureListener.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,16 +14,11 @@ * limitations under the License. */ -#ifndef ANDROID_UI_CONFIG_H -#define ANDROID_UI_CONFIG_H +package android.gui; -#include <string> +import android.gui.ScreenCaptureResults; -namespace android { - -// Append the libui configuration details to configStr. -void appendUiConfigString(std::string& configStr); - -}; // namespace android - -#endif /*ANDROID_UI_CONFIG_H*/ +/** @hide */ +oneway interface IScreenCaptureListener { + void onScreenCaptureCompleted(in ScreenCaptureResults captureResults); +}
\ No newline at end of file diff --git a/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl b/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl new file mode 100644 index 0000000000..5cd12fdc2b --- /dev/null +++ b/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl @@ -0,0 +1,6 @@ +package android.gui; + +/** @hide */ +interface ITransactionTraceListener { + void onToggled(boolean enabled); +}
\ No newline at end of file diff --git a/libs/gui/include/gui/GuiConfig.h b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl index 7aa54321fd..9908edd2ef 100644 --- a/libs/gui/include/gui/GuiConfig.h +++ b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,16 +14,6 @@ * limitations under the License. */ -#ifndef ANDROID_GUI_CONFIG_H -#define ANDROID_GUI_CONFIG_H +package android.gui; -#include <string> - -namespace android { - -// Append the libgui configuration details to configStr. -void appendGuiConfigString(std::string& configStr); - -}; // namespace android - -#endif /*ANDROID_GUI_CONFIG_H*/ +parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h";
\ No newline at end of file diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 2320771a38..bccb71b362 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -28,6 +28,7 @@ #include <system/window.h> #include <thread> +#include <queue> namespace android { @@ -66,24 +67,34 @@ class BLASTBufferQueue : public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener { public: - BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height, - bool enableTripleBuffering = true); + BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width, + int height, int32_t format, bool enableTripleBuffering = true); sp<IGraphicBufferProducer> getIGraphicBufferProducer() const { return mProducer; } + sp<Surface> getSurface(bool includeSurfaceControlHandle); void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ } - void onFrameReplaced(const BufferItem& item) override {onFrameAvailable(item);} + void onFrameReplaced(const BufferItem& item) override; void onFrameAvailable(const BufferItem& item) override; + void onFrameDequeued(const uint64_t) override; + void onFrameCancelled(const uint64_t) override; void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence, const std::vector<SurfaceControlStats>& stats); void setNextTransaction(SurfaceComposerClient::Transaction *t); + void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber); + void setTransactionCompleteCallback(uint64_t frameNumber, + std::function<void(int64_t)>&& transactionCompleteCallback); - void update(const sp<SurfaceControl>& surface, int width, int height); + void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format); + void flushShadowQueue() { mFlushShadowQueue = true; } - virtual ~BLASTBufferQueue() = default; + status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless); + status_t setFrameTimelineInfo(const FrameTimelineInfo& info); + + virtual ~BLASTBufferQueue(); private: friend class BLASTBufferQueueHelper; @@ -91,10 +102,17 @@ private: // can't be copied BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs); BLASTBufferQueue(const BLASTBufferQueue& rhs); + void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, + sp<IGraphicBufferConsumer>* outConsumer); void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex); - Rect computeCrop(const BufferItem& item); + Rect computeCrop(const BufferItem& item) REQUIRES(mMutex); + // Return true if we need to reject the buffer based on the scaling mode and the buffer size. + bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex); + bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex); + static PixelFormat convertBufferFormat(PixelFormat& format); + std::string mName; sp<SurfaceControl> mSurfaceControl; std::mutex mMutex; @@ -106,17 +124,20 @@ private: int32_t mNumFrameAvailable GUARDED_BY(mMutex); int32_t mNumAcquired GUARDED_BY(mMutex); - + bool mInitialCallbackReceived GUARDED_BY(mMutex) = false; struct PendingReleaseItem { BufferItem item; sp<Fence> releaseFence; }; std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex); + // Keep a reference to the currently presented buffer so we can release it when the next buffer + // is ready to be presented. PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex); - int mWidth GUARDED_BY(mMutex); - int mHeight GUARDED_BY(mMutex); + ui::Size mSize GUARDED_BY(mMutex); + ui::Size mRequestedSize GUARDED_BY(mMutex); + int32_t mFormat GUARDED_BY(mMutex); uint32_t mTransformHint GUARDED_BY(mMutex); @@ -125,6 +146,41 @@ private: sp<BLASTBufferItemConsumer> mBufferItemConsumer; SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex); + std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>> + mPendingTransactions GUARDED_BY(mMutex); + + // If set to true, the next queue buffer will wait until the shadow queue has been processed by + // the adapter. + bool mFlushShadowQueue = false; + // Last requested auto refresh state set by the producer. The state indicates that the consumer + // should acquire the next frame as soon as it can and not wait for a frame to become available. + // This is only relevant for shared buffer mode. + bool mAutoRefresh GUARDED_BY(mMutex) = false; + + std::queue<FrameTimelineInfo> mNextFrameTimelineInfoQueue GUARDED_BY(mMutex); + + // Last acquired buffer's scaling mode. This is used to check if we should update the blast + // layer size immediately or wait until we get the next buffer. This will support scenarios + // where the layer can change sizes and the buffer will scale to fit the new size. + uint32_t mLastBufferScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; + + // Tracks the last acquired frame number + uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0; + + std::function<void(int64_t)> mTransactionCompleteCallback GUARDED_BY(mMutex) = nullptr; + uint64_t mTransactionCompleteFrameNumber GUARDED_BY(mMutex){0}; + + // Queues up transactions using this token in SurfaceFlinger. This prevents queued up + // transactions from other parts of the client from blocking this transaction. + const sp<IBinder> mApplyToken GUARDED_BY(mMutex) = new BBinder(); + + // Guards access to mDequeueTimestamps since we cannot hold to mMutex in onFrameDequeued or + // we will deadlock. + std::mutex mTimestampMutex; + // Tracks buffer dequeue times by the client. This info is sent to SurfaceFlinger which uses + // it for debugging purposes. + std::unordered_map<uint64_t /* bufferId */, nsecs_t> mDequeueTimestamps + GUARDED_BY(mTimestampMutex); }; } // namespace android diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h index 7db69eca9d..6aa801ab86 100644 --- a/libs/gui/include/gui/BufferQueueConsumer.h +++ b/libs/gui/include/gui/BufferQueueConsumer.h @@ -174,6 +174,10 @@ public: // Value used to determine if present time is valid. constexpr static int MAX_REASONABLE_NSEC = 1'000'000'000ULL; // 1 second + // This allows the consumer to acquire an additional buffer if that buffer is not droppable and + // will eventually be released or acquired by the consumer. + void setAllowExtraAcquire(bool /* allow */); + private: sp<BufferQueueCore> mCore; diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h index 557c28b1b7..8d0828d88e 100644 --- a/libs/gui/include/gui/BufferQueueCore.h +++ b/libs/gui/include/gui/BufferQueueCore.h @@ -354,6 +354,9 @@ private: // mTransformHintInUse is to cache the mTransformHint used by the producer. uint32_t mTransformHintInUse; + // This allows the consumer to acquire an additional buffer if that buffer is not droppable and + // will eventually be released or acquired by the consumer. + bool mAllowExtraAcquire = false; }; // class BufferQueueCore } // namespace android diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index eb5b00418a..d74c2ba72b 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -19,14 +19,25 @@ #include <utils/Looper.h> namespace android { +using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; + +struct VsyncEventData { + // The Vsync Id corresponsing to this vsync event. This will be used to + // populate ISurfaceComposer::setFrameTimelineVsync and + // SurfaceComposerClient::setFrameTimelineVsync + int64_t id = FrameTimelineInfo::INVALID_VSYNC_ID; + + // The deadline in CLOCK_MONOTONIC that the app needs to complete its + // frame by (both on the CPU and the GPU) + int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max(); +}; class DisplayEventDispatcher : public LooperCallback { public: explicit DisplayEventDispatcher( const sp<Looper>& looper, ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp, - ISurfaceComposer::ConfigChanged configChanged = - ISurfaceComposer::eConfigChangedSuppress); + ISurfaceComposer::EventRegistrationFlags eventRegistration = {}); status_t initialize(); void dispose(); @@ -43,16 +54,22 @@ private: DisplayEventReceiver mReceiver; bool mWaitingForVsync; - virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0; + std::vector<FrameRateOverride> mFrameRateOverrides; + + virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, + VsyncEventData vsyncEventData) = 0; virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) = 0; - virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, - int32_t configId, nsecs_t vsyncPeriod) = 0; + virtual void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId, + nsecs_t vsyncPeriod) = 0; // AChoreographer-specific hook for processing null-events so that looper // can be properly poked. virtual void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) = 0; + virtual void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, + std::vector<FrameRateOverride> overrides) = 0; + bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, - uint32_t* outCount); + uint32_t* outCount, VsyncEventData* outVsyncEventData); }; } // namespace android diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index 0e10d1ad8e..7179a20d22 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -52,37 +52,52 @@ public: enum { DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'), DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'), - DISPLAY_EVENT_CONFIG_CHANGED = fourcc('c', 'o', 'n', 'f'), + DISPLAY_EVENT_MODE_CHANGE = fourcc('m', 'o', 'd', 'e'), DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'), + DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'), + DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'), }; struct Event { + // We add __attribute__((aligned(8))) for nsecs_t fields because + // we need to make sure all fields are aligned the same with x86 + // and x64 (long long has different default alignment): + // + // https://en.wikipedia.org/wiki/Data_structure_alignment struct Header { uint32_t type; - PhysicalDisplayId displayId; + PhysicalDisplayId displayId __attribute__((aligned(8))); nsecs_t timestamp __attribute__((aligned(8))); }; struct VSync { uint32_t count; - nsecs_t expectedVSyncTimestamp; + nsecs_t expectedVSyncTimestamp __attribute__((aligned(8))); + nsecs_t deadlineTimestamp __attribute__((aligned(8))); + int64_t vsyncId; }; struct Hotplug { bool connected; }; - struct Config { - int32_t configId; - nsecs_t vsyncPeriod; + struct ModeChange { + int32_t modeId; + nsecs_t vsyncPeriod __attribute__((aligned(8))); + }; + + struct FrameRateOverride { + uid_t uid __attribute__((aligned(8))); + float frameRateHz __attribute__((aligned(8))); }; Header header; union { VSync vsync; Hotplug hotplug; - Config config; + ModeChange modeChange; + FrameRateOverride frameRateOverride; }; }; @@ -91,13 +106,12 @@ public: * DisplayEventReceiver creates and registers an event connection with * SurfaceFlinger. VSync events are disabled by default. Call setVSyncRate * or requestNextVsync to receive them. - * To receive Config Changed events specify this in the constructor. - * Other events start being delivered immediately. + * To receive ModeChanged and/or FrameRateOverrides events specify this in + * the constructor. Other events start being delivered immediately. */ explicit DisplayEventReceiver( ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp, - ISurfaceComposer::ConfigChanged configChanged = - ISurfaceComposer::eConfigChangedSuppress); + ISurfaceComposer::EventRegistrationFlags eventRegistration = {}); /* * ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events diff --git a/libs/gui/include/gui/FrameTimelineInfo.h b/libs/gui/include/gui/FrameTimelineInfo.h new file mode 100644 index 0000000000..3b4c009609 --- /dev/null +++ b/libs/gui/include/gui/FrameTimelineInfo.h @@ -0,0 +1,43 @@ +/* + * Copyright 2021 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 <stdint.h> + +#include <android/os/IInputConstants.h> +#include <binder/Parcel.h> + +namespace android { + +struct FrameTimelineInfo { + // Needs to be in sync with android.graphics.FrameInfo.INVALID_VSYNC_ID in java + static constexpr int64_t INVALID_VSYNC_ID = -1; + + // The vsync id that was used to start the transaction + int64_t vsyncId = INVALID_VSYNC_ID; + + // The id of the input event that caused this buffer + int32_t inputEventId = android::os::IInputConstants::INVALID_INPUT_EVENT_ID; + + status_t write(Parcel& output) const; + status_t read(const Parcel& input); + + void merge(const FrameTimelineInfo& other); + void clear(); +}; + +} // namespace android diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index 45e0a134ba..c3b92622b6 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -38,6 +38,9 @@ #include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h> #include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h> +#include <optional> +#include <vector> + namespace android { // ---------------------------------------------------------------------------- @@ -289,8 +292,9 @@ public: const sp<GraphicBuffer>& buffer) = 0; struct QueueBufferInput : public Flattenable<QueueBufferInput> { - friend class Flattenable<QueueBufferInput>; - explicit inline QueueBufferInput(const Parcel& parcel); + explicit inline QueueBufferInput(const Parcel& parcel) { + parcel.read(*this); + } // timestamp - a monotonically increasing value in nanoseconds // isAutoTimestamp - if the timestamp was synthesized at queue time @@ -304,21 +308,29 @@ public: // camera mode). // getFrameTimestamps - whether or not the latest frame timestamps // should be retrieved from the consumer. + // slot - the slot index to queue. This is used only by queueBuffers(). + // queueBuffer() ignores this value and uses the argument `slot` + // instead. inline QueueBufferInput(int64_t _timestamp, bool _isAutoTimestamp, android_dataspace _dataSpace, const Rect& _crop, int _scalingMode, uint32_t _transform, const sp<Fence>& _fence, - uint32_t _sticky = 0, bool _getFrameTimestamps = false) + uint32_t _sticky = 0, bool _getFrameTimestamps = false, + int _slot = -1) : timestamp(_timestamp), isAutoTimestamp(_isAutoTimestamp), dataSpace(_dataSpace), crop(_crop), scalingMode(_scalingMode), - transform(_transform), stickyTransform(_sticky), fence(_fence), - surfaceDamage(), getFrameTimestamps(_getFrameTimestamps) { } + transform(_transform), stickyTransform(_sticky), + fence(_fence), surfaceDamage(), + getFrameTimestamps(_getFrameTimestamps), slot(_slot) { } + + QueueBufferInput() = default; inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp, android_dataspace* outDataSpace, Rect* outCrop, int* outScalingMode, uint32_t* outTransform, sp<Fence>* outFence, uint32_t* outStickyTransform = nullptr, - bool* outGetFrameTimestamps = nullptr) const { + bool* outGetFrameTimestamps = nullptr, + int* outSlot = nullptr) const { *outTimestamp = timestamp; *outIsAutoTimestamp = bool(isAutoTimestamp); *outDataSpace = dataSpace; @@ -332,6 +344,9 @@ public: if (outGetFrameTimestamps) { *outGetFrameTimestamps = getFrameTimestamps; } + if (outSlot) { + *outSlot = slot; + } } // Flattenable protocol @@ -357,6 +372,7 @@ public: sp<Fence> fence; Region surfaceDamage; bool getFrameTimestamps{false}; + int slot{-1}; HdrMetadata hdrMetadata; }; @@ -385,6 +401,7 @@ public: FrameEventHistoryDelta frameTimestamps; bool bufferReplaced{false}; int maxBufferCount{0}; + status_t result{NO_ERROR}; }; // queueBuffer indicates that the client has finished filling in the @@ -404,6 +421,10 @@ public: // Upon success, the output will be filled with meaningful values // (refer to the documentation below). // + // Note: QueueBufferInput::slot was added to QueueBufferInput to be used by + // queueBuffers(), the batched version of queueBuffer(). The non-batched + // method (queueBuffer()) uses `slot` and ignores `input.slot`. + // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the buffer queue has been abandoned or the producer is not // connected. @@ -639,6 +660,147 @@ public: // the width and height used for dequeueBuffer will be additionally swapped. virtual status_t setAutoPrerotation(bool autoPrerotation); + struct RequestBufferOutput : public Flattenable<RequestBufferOutput> { + RequestBufferOutput() = default; + + // Flattenable protocol + static constexpr size_t minFlattenedSize(); + size_t getFlattenedSize() const; + size_t getFdCount() const; + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + + status_t result; + sp<GraphicBuffer> buffer; + }; + + // Batched version of requestBuffer(). + // This method behaves like a sequence of requestBuffer() calls. + // The return value of the batched method will only be about the + // transaction. For a local call, the return value will always be NO_ERROR. + virtual status_t requestBuffers( + const std::vector<int32_t>& slots, + std::vector<RequestBufferOutput>* outputs); + + struct DequeueBufferInput : public LightFlattenable<DequeueBufferInput> { + DequeueBufferInput() = default; + + // LightFlattenable protocol + inline bool isFixedSize() const { return true; } + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; + status_t unflatten(void const* buffer, size_t size); + + uint32_t width; + uint32_t height; + PixelFormat format; + uint64_t usage; + bool getTimestamps; + }; + + struct DequeueBufferOutput : public Flattenable<DequeueBufferOutput> { + DequeueBufferOutput() = default; + + // Flattenable protocol + static constexpr size_t minFlattenedSize(); + size_t getFlattenedSize() const; + size_t getFdCount() const; + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + + status_t result; + int slot = -1; + sp<Fence> fence = Fence::NO_FENCE; + uint64_t bufferAge; + std::optional<FrameEventHistoryDelta> timestamps; + }; + + // Batched version of dequeueBuffer(). + // This method behaves like a sequence of dequeueBuffer() calls. + // The return value of the batched method will only be about the + // transaction. For a local call, the return value will always be NO_ERROR. + virtual status_t dequeueBuffers( + const std::vector<DequeueBufferInput>& inputs, + std::vector<DequeueBufferOutput>* outputs); + + // Batched version of detachBuffer(). + // This method behaves like a sequence of detachBuffer() calls. + // The return value of the batched method will only be about the + // transaction. For a local call, the return value will always be NO_ERROR. + virtual status_t detachBuffers(const std::vector<int32_t>& slots, + std::vector<status_t>* results); + + + struct AttachBufferOutput : public LightFlattenable<AttachBufferOutput> { + AttachBufferOutput() = default; + + // LightFlattenable protocol + inline bool isFixedSize() const { return true; } + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; + status_t unflatten(void const* buffer, size_t size); + + status_t result; + int slot; + }; + // Batched version of attachBuffer(). + // This method behaves like a sequence of attachBuffer() calls. + // The return value of the batched method will only be about the + // transaction. For a local call, the return value will always be NO_ERROR. + virtual status_t attachBuffers( + const std::vector<sp<GraphicBuffer>>& buffers, + std::vector<AttachBufferOutput>* outputs); + + // Batched version of queueBuffer(). + // This method behaves like a sequence of queueBuffer() calls. + // The return value of the batched method will only be about the + // transaction. For a local call, the return value will always be NO_ERROR. + // + // Note: QueueBufferInput::slot was added to QueueBufferInput to include the + // `slot` input argument of the non-batched method queueBuffer(). + virtual status_t queueBuffers(const std::vector<QueueBufferInput>& inputs, + std::vector<QueueBufferOutput>* outputs); + + struct CancelBufferInput : public Flattenable<CancelBufferInput> { + CancelBufferInput() = default; + + // Flattenable protocol + static constexpr size_t minFlattenedSize(); + size_t getFlattenedSize() const; + size_t getFdCount() const; + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + + int slot; + sp<Fence> fence; + }; + // Batched version of cancelBuffer(). + // This method behaves like a sequence of cancelBuffer() calls. + // The return value of the batched method will only be about the + // transaction. For a local call, the return value will always be NO_ERROR. + virtual status_t cancelBuffers( + const std::vector<CancelBufferInput>& inputs, + std::vector<status_t>* results); + + struct QueryOutput : public LightFlattenable<QueryOutput> { + QueryOutput() = default; + + // LightFlattenable protocol + inline bool isFixedSize() const { return true; } + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; + status_t unflatten(void const* buffer, size_t size); + + status_t result; + int64_t value; + }; + // Batched version of query(). + // This method behaves like a sequence of query() calls. + // The return value of the batched method will only be about the + // transaction. For a local call, the return value will always be NO_ERROR. + virtual status_t query(const std::vector<int32_t> inputs, + std::vector<QueryOutput>* outputs); + #ifndef NO_BINDER // Static method exports any IGraphicBufferProducer object to a parcel. It // handles null producer as well. diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 8d3160a815..292838e9ae 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -22,16 +22,21 @@ #include <binder/IBinder.h> #include <binder/IInterface.h> +#include <android/gui/IScreenCaptureListener.h> +#include <android/gui/ITransactionTraceListener.h> +#include <gui/FrameTimelineInfo.h> #include <gui/ITransactionCompletedListener.h> +#include <input/Flags.h> + #include <math/vec4.h> #include <ui/ConfigStoreTypes.h> +#include <ui/DisplayId.h> #include <ui/DisplayedFrameStats.h> #include <ui/FrameStats.h> #include <ui/GraphicBuffer.h> #include <ui/GraphicTypes.h> -#include <ui/PhysicalDisplayId.h> #include <ui/PixelFormat.h> #include <ui/Rotation.h> @@ -48,11 +53,12 @@ namespace android { struct client_cache_t; struct ComposerState; -struct DisplayConfig; +struct DisplayCaptureArgs; struct DisplayInfo; struct DisplayStatInfo; struct DisplayState; struct InputWindowCommands; +struct LayerCaptureArgs; class LayerDebugInfo; class HdrCapabilities; class IDisplayEventConnection; @@ -62,8 +68,11 @@ class IRegionSamplingListener; class Rect; enum class FrameEvent; +using gui::IScreenCaptureListener; + namespace ui { +struct DisplayMode; struct DisplayState; } // namespace ui @@ -102,7 +111,12 @@ public: eVsyncSourceSurfaceFlinger = 1 }; - enum ConfigChanged { eConfigChangedSuppress = 0, eConfigChangedDispatch = 1 }; + enum class EventRegistration { + modeChanged = 1 << 0, + frameRateOverride = 1 << 1, + }; + + using EventRegistrationFlags = Flags<EventRegistration>; /* * Create a connection with SurfaceFlinger. @@ -112,7 +126,7 @@ public: /* return an IDisplayEventConnection */ virtual sp<IDisplayEventConnection> createDisplayEventConnection( VsyncSource vsyncSource = eVsyncSourceApp, - ConfigChanged configChanged = eConfigChangedSuppress) = 0; + EventRegistrationFlags eventRegistration = {}) = 0; /* create a virtual display * requires ACCESS_SURFACE_FLINGER permission. @@ -147,13 +161,12 @@ public: } /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */ - virtual void setTransactionState(const Vector<ComposerState>& state, - const Vector<DisplayState>& displays, uint32_t flags, - const sp<IBinder>& applyToken, - const InputWindowCommands& inputWindowCommands, - int64_t desiredPresentTime, - const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, - const std::vector<ListenerCallbacks>& listenerCallbacks) = 0; + virtual status_t setTransactionState( + const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& state, + const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, + const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, + bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, + const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) = 0; /* signal that we're done booting. * Requires ACCESS_SURFACE_FLINGER permission @@ -194,15 +207,15 @@ public: virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*) = 0; /** - * Get configurations supported by given physical display. + * Get modes supported by given physical display. */ - virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>*) = 0; + virtual status_t getDisplayModes(const sp<IBinder>& display, Vector<ui::DisplayMode>*) = 0; /** - * Get the index into configurations returned by getDisplayConfigs, - * corresponding to the active configuration. + * Get the index into modes returned by getDisplayModes, + * corresponding to the active mode. */ - virtual int getActiveConfig(const sp<IBinder>& display) = 0; + virtual int getActiveDisplayModeId(const sp<IBinder>& display) = 0; virtual status_t getDisplayColorModes(const sp<IBinder>& display, Vector<ui::ColorMode>* outColorModes) = 0; @@ -246,65 +259,17 @@ public: /** * Capture the specified screen. This requires READ_FRAME_BUFFER * permission. This function will fail if there is a secure window on - * screen. + * screen and DisplayCaptureArgs.captureSecureLayers is false. * * This function can capture a subregion (the source crop) of the screen. * The subregion can be optionally rotated. It will also be scaled to * match the size of the output buffer. - * - * reqDataspace and reqPixelFormat specify the data space and pixel format - * of the buffer. The caller should pick the data space and pixel format - * that it can consume. - * - * sourceCrop is the crop on the logical display. - * - * reqWidth and reqHeight specifies the size of the buffer. When either - * of them is 0, they are set to the size of the logical display viewport. - * - * When useIdentityTransform is true, layer transformations are disabled. - * - * rotation specifies the rotation of the source crop (and the pixels in - * it) around its center. - */ - virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - bool& outCapturedSecureLayers, ui::Dataspace reqDataspace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ui::Rotation rotation = ui::ROTATION_0, - bool captureSecureLayers = false) = 0; - /** - * Capture the specified screen. This requires READ_FRAME_BUFFER - * permission. This function will fail if there is a secure window on - * screen. - * - * This function can capture a subregion (the source crop) of the screen - * into an sRGB buffer with RGBA_8888 pixel format. - * The subregion can be optionally rotated. It will also be scaled to - * match the size of the output buffer. - * - * At the moment, sourceCrop is ignored and is always set to the visible - * region (projected display viewport) of the screen. - * - * reqWidth and reqHeight specifies the size of the buffer. When either - * of them is 0, they are set to the size of the logical display viewport. - * - * When useIdentityTransform is true, layer transformations are disabled. - * - * rotation specifies the rotation of the source crop (and the pixels in - * it) around its center. - */ - virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - const Rect& sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - bool useIdentityTransform, - ui::Rotation rotation = ui::ROTATION_0) { - bool outIgnored; - return captureScreen(display, outBuffer, outIgnored, ui::Dataspace::V0_SRGB, - ui::PixelFormat::RGBA_8888, sourceCrop, reqWidth, reqHeight, - useIdentityTransform, rotation); - } + */ + virtual status_t captureDisplay(const DisplayCaptureArgs& args, + const sp<IScreenCaptureListener>& captureListener) = 0; - virtual status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, - sp<GraphicBuffer>* outBuffer) = 0; + virtual status_t captureDisplay(uint64_t displayOrLayerStack, + const sp<IScreenCaptureListener>& captureListener) = 0; template <class AA> struct SpHash { @@ -313,27 +278,11 @@ public: /** * Capture a subtree of the layer hierarchy, potentially ignoring the root node. - * - * reqDataspace and reqPixelFormat specify the data space and pixel format - * of the buffer. The caller should pick the data space and pixel format - * that it can consume. + * This requires READ_FRAME_BUFFER permission. This function will fail if there + * is a secure window on screen */ - virtual status_t captureLayers( - const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer, - ui::Dataspace reqDataspace, ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeHandles, - float frameScale = 1.0, bool childrenOnly = false) = 0; - - /** - * Capture a subtree of the layer hierarchy into an sRGB buffer with RGBA_8888 pixel format, - * potentially ignoring the root node. - */ - status_t captureLayers(const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer, - const Rect& sourceCrop, float frameScale = 1.0, - bool childrenOnly = false) { - return captureLayers(layerHandleBinder, outBuffer, ui::Dataspace::V0_SRGB, - ui::PixelFormat::RGBA_8888, sourceCrop, {}, frameScale, childrenOnly); - } + virtual status_t captureLayers(const LayerCaptureArgs& args, + const sp<IScreenCaptureListener>& captureListener) = 0; /* Clears the frame statistics for animations. * @@ -437,33 +386,31 @@ public: /* Sets the refresh rate boundaries for the display. * * The primary refresh rate range represents display manager's general guidance on the display - * configs we'll consider when switching refresh rates. Unless we get an explicit signal from an + * modes we'll consider when switching refresh rates. Unless we get an explicit signal from an * app, we should stay within this range. * - * The app request refresh rate range allows us to consider more display configs when switching + * The app request refresh rate range allows us to consider more display modes when switching * refresh rates. Although we should generally stay within the primary range, specific * considerations, such as layer frame rate settings specified via the setFrameRate() api, may * cause us to go outside the primary range. We never go outside the app request range. The app * request range will be greater than or equal to the primary refresh rate range, never smaller. * - * defaultConfig is used to narrow the list of display configs SurfaceFlinger will consider - * switching between. Only configs with a config group and resolution matching defaultConfig - * will be considered for switching. The defaultConfig index corresponds to the list of configs - * returned from getDisplayConfigs(). - */ - virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t defaultConfig, - float primaryRefreshRateMin, - float primaryRefreshRateMax, - float appRequestRefreshRateMin, - float appRequestRefreshRateMax) = 0; - - virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t* outDefaultConfig, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax) = 0; + * defaultMode is used to narrow the list of display modes SurfaceFlinger will consider + * switching between. Only modes with a mode group and resolution matching defaultMode + * will be considered for switching. The defaultMode index corresponds to the list of modes + * returned from getDisplayModes(). + */ + virtual status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t defaultMode, + bool allowGroupSwitching, + float primaryRefreshRateMin, + float primaryRefreshRateMax, + float appRequestRefreshRateMin, + float appRequestRefreshRateMax) = 0; + + virtual status_t getDesiredDisplayModeSpecs( + const sp<IBinder>& displayToken, size_t* outDefaultMode, bool* outAllowGroupSwitching, + float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax, + float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax) = 0; /* * Gets whether brightness operations are supported on a display. * @@ -496,14 +443,14 @@ public: virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) = 0; /* - * Sends a power hint to the composer. This function is asynchronous. + * Sends a power boost to the composer. This function is asynchronous. * - * hintId - * hint id according to android::hardware::power::V1_0::PowerHint + * boostId + * boost id according to android::hardware::power::Boost * * Returns NO_ERROR upon success. */ - virtual status_t notifyPowerHint(int32_t hintId) = 0; + virtual status_t notifyPowerBoost(int32_t boostId) = 0; /* * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows @@ -531,7 +478,7 @@ public: * Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info. */ virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate, - int8_t compatibility) = 0; + int8_t compatibility, bool shouldBeSeamless) = 0; /* * Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired, @@ -540,6 +487,42 @@ public: * for tests. Release the token by releasing the returned IBinder reference. */ virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) = 0; + + /* + * Sets the frame timeline vsync info received from choreographer that corresponds to next + * buffer submitted on that surface. + */ + virtual status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface, + const FrameTimelineInfo& frameTimelineInfo) = 0; + + /* + * Adds a TransactionTraceListener to listen for transaction tracing state updates. + */ + virtual status_t addTransactionTraceListener( + const sp<gui::ITransactionTraceListener>& listener) = 0; + + /** + * Gets priority of the RenderEngine in SurfaceFlinger. + */ + virtual int getGPUContextPriority() = 0; + + /** + * Gets the extra buffers a client would need to allocate if it passes + * the Choreographer#getVsyncId with its buffers. + * + * When Choreographer#getVsyncId is passed to SurfaceFlinger, it is used + * as an indication of when to latch the buffer. SurfaceFlinger will make + * sure that it will give the app at least the time configured as the + * 'appDuration' before trying to latch the buffer. + * + * The total buffers needed for a given configuration is basically the + * numbers of vsyncs a single buffer is used across the stack. For the default + * configuration a buffer is held ~1 vsync by the app, ~1 vsync by SurfaceFlinger + * and 1 vsync by the display. The extra buffers are calculated as the + * number of additional buffers on top of the 3 buffers already allocated + * by the app. + */ + virtual status_t getExtraBufferCount(int* extraBuffers) const = 0; }; // ---------------------------------------------------------------------------- @@ -559,10 +542,10 @@ public: SET_TRANSACTION_STATE, AUTHENTICATE_SURFACE, GET_SUPPORTED_FRAME_TIMESTAMPS, - GET_DISPLAY_CONFIGS, - GET_ACTIVE_CONFIG, + GET_DISPLAY_MODES, + GET_ACTIVE_DISPLAY_MODE, GET_DISPLAY_STATE, - CAPTURE_SCREEN, + CAPTURE_DISPLAY, CAPTURE_LAYERS, CLEAR_ANIMATION_FRAME_STATS, GET_ANIMATION_FRAME_STATS, @@ -586,12 +569,12 @@ public: GET_PHYSICAL_DISPLAY_IDS, ADD_REGION_SAMPLING_LISTENER, REMOVE_REGION_SAMPLING_LISTENER, - SET_DESIRED_DISPLAY_CONFIG_SPECS, - GET_DESIRED_DISPLAY_CONFIG_SPECS, + SET_DESIRED_DISPLAY_MODE_SPECS, + GET_DESIRED_DISPLAY_MODE_SPECS, GET_DISPLAY_BRIGHTNESS_SUPPORT, SET_DISPLAY_BRIGHTNESS, - CAPTURE_SCREEN_BY_ID, - NOTIFY_POWER_HINT, + CAPTURE_DISPLAY_BY_ID, + NOTIFY_POWER_BOOST, SET_GLOBAL_SHADOW_SETTINGS, GET_AUTO_LOW_LATENCY_MODE_SUPPORT, SET_AUTO_LOW_LATENCY_MODE, @@ -599,6 +582,10 @@ public: SET_GAME_CONTENT_TYPE, SET_FRAME_RATE, ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN, + SET_FRAME_TIMELINE_INFO, + ADD_TRANSACTION_TRACE_LISTENER, + GET_GPU_CONTEXT_PRIORITY, + GET_EXTRA_BUFFER_COUNT, // Always append new enum to the end. }; diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h index 3afbabf1dc..9e9e191480 100644 --- a/libs/gui/include/gui/ISurfaceComposerClient.h +++ b/libs/gui/include/gui/ISurfaceComposerClient.h @@ -36,6 +36,7 @@ public: enum { // (keep in sync with SurfaceControl.java) eHidden = 0x00000004, eDestroyBackbuffer = 0x00000020, + eSkipScreenshot = 0x00000040, eSecure = 0x00000080, eNonPremultiplied = 0x00000100, eOpaque = 0x00000400, @@ -51,13 +52,15 @@ public: eFXSurfaceMask = 0x000F0000, }; + // TODO(b/172002646): Clean up the Surface Creation Arguments /* * Requires ACCESS_SURFACE_FLINGER permission */ 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, uint32_t* outTransformHint) = 0; + sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, + uint32_t* outTransformHint) = 0; /* * Requires ACCESS_SURFACE_FLINGER permission @@ -66,7 +69,7 @@ public: PixelFormat format, uint32_t flags, const sp<IGraphicBufferProducer>& parent, LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, + sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, uint32_t* outTransformHint) = 0; /* @@ -79,7 +82,8 @@ public: */ virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0; - virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) = 0; + virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle, + int32_t* outLayerId) = 0; }; class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> { diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h index c58634b8a2..cb17ceecd3 100644 --- a/libs/gui/include/gui/ITransactionCompletedListener.h +++ b/libs/gui/include/gui/ITransactionCompletedListener.h @@ -16,6 +16,8 @@ #pragma once +#include "JankInfo.h" + #include <binder/IInterface.h> #include <binder/Parcel.h> #include <binder/Parcelable.h> @@ -57,6 +59,26 @@ public: nsecs_t dequeueReadyTime; }; +/** + * Jank information representing SurfaceFlinger's jank classification about frames for a specific + * surface. + */ +class JankData : public Parcelable { +public: + status_t writeToParcel(Parcel* output) const override; + status_t readFromParcel(const Parcel* input) override; + + JankData(); + JankData(int64_t frameVsyncId, int32_t jankType) + : frameVsyncId(frameVsyncId), jankType(jankType) {} + + // Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId + int64_t frameVsyncId; + + // Bitmask of janks that occurred + int32_t jankType; +}; + class SurfaceStats : public Parcelable { public: status_t writeToParcel(Parcel* output) const override; @@ -64,18 +86,21 @@ public: SurfaceStats() = default; SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence, - uint32_t hint, FrameEventHistoryStats frameEventStats) + uint32_t hint, FrameEventHistoryStats frameEventStats, + std::vector<JankData> jankData) : surfaceControl(sc), acquireTime(time), previousReleaseFence(prevReleaseFence), transformHint(hint), - eventStats(frameEventStats) {} + eventStats(frameEventStats), + jankData(std::move(jankData)) {} sp<IBinder> surfaceControl; nsecs_t acquireTime = -1; sp<Fence> previousReleaseFence; uint32_t transformHint = 0; FrameEventHistoryStats eventStats; + std::vector<JankData> jankData; }; class TransactionStats : public Parcelable { diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h new file mode 100644 index 0000000000..85ae9cb2bc --- /dev/null +++ b/libs/gui/include/gui/JankInfo.h @@ -0,0 +1,47 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace android { + +// Jank information tracked by SurfaceFlinger(SF) for perfetto tracing and telemetry. +enum JankType { + // No Jank + None = 0x0, + // Jank that occurs in the layers below SurfaceFlinger + DisplayHAL = 0x1, + // SF took too long on the CPU + SurfaceFlingerCpuDeadlineMissed = 0x2, + // SF took too long on the GPU + SurfaceFlingerGpuDeadlineMissed = 0x4, + // Either App or GPU took too long on the frame + AppDeadlineMissed = 0x8, + // Vsync predictions have drifted beyond the threshold from the actual HWVsync + PredictionError = 0x10, + // Janks caused due to the time SF was scheduled to work on the frame + // Example: SF woke up too early and latched a buffer resulting in an early present + SurfaceFlingerScheduling = 0x20, + // A buffer is said to be stuffed if it was expected to be presented on a vsync but was + // presented later because the previous buffer was presented in its expected vsync. This + // usually happens if there is an unexpectedly long frame causing the rest of the buffers + // to enter a stuffed state. + BufferStuffing = 0x40, + // Jank due to unknown reasons. + Unknown = 0x80, +}; + +} // namespace android diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h index d58e019799..41982c28a2 100644 --- a/libs/gui/include/gui/LayerMetadata.h +++ b/libs/gui/include/gui/LayerMetadata.h @@ -27,6 +27,9 @@ enum { METADATA_WINDOW_TYPE = 2, METADATA_TASK_ID = 3, METADATA_MOUSE_CURSOR = 4, + METADATA_ACCESSIBILITY_ID = 5, + METADATA_OWNER_PID = 6, + METADATA_DEQUEUE_TIME = 7 }; struct LayerMetadata : public Parcelable { @@ -49,6 +52,8 @@ struct LayerMetadata : public Parcelable { bool has(uint32_t key) const; int32_t getInt32(uint32_t key, int32_t fallback) const; void setInt32(uint32_t key, int32_t value); + std::optional<int64_t> getInt64(uint32_t key) const; + void setInt64(uint32_t key, int64_t value); std::string itemToString(uint32_t key, const char* separator) const; }; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index e60f6777ae..b273805e25 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -16,6 +16,23 @@ #ifndef ANDROID_SF_LAYER_STATE_H #define ANDROID_SF_LAYER_STATE_H +#define SAFE_PARCEL(FUNC, ...) \ + { \ + status_t error = FUNC(__VA_ARGS__); \ + if (error) { \ + ALOGE("ERROR(%d). Failed to call parcel %s(%s)", error, #FUNC, #__VA_ARGS__); \ + return error; \ + } \ + } + +#define SAFE_PARCEL_READ_SIZE(FUNC, COUNT, SIZE) \ + { \ + SAFE_PARCEL(FUNC, COUNT); \ + if (static_cast<unsigned int>(*COUNT) > SIZE) { \ + ALOGE("ERROR(BAD_VALUE). %s was greater than dataSize", #COUNT); \ + return BAD_VALUE; \ + } \ + } #include <stdint.h> #include <sys/types.h> @@ -26,15 +43,20 @@ #include <math/mat4.h> #ifndef NO_INPUT +#include <android/FocusRequest.h> #include <input/InputWindow.h> #endif +#include <gui/ISurfaceComposer.h> #include <gui/LayerMetadata.h> +#include <gui/SurfaceControl.h> #include <math/vec3.h> +#include <ui/BlurRegion.h> #include <ui/GraphicTypes.h> #include <ui/Rect.h> #include <ui/Region.h> #include <ui/Rotation.h> +#include <ui/StretchEffect.h> #include <ui/Transform.h> #include <utils/Errors.h> @@ -57,9 +79,14 @@ struct client_cache_t { */ struct layer_state_t { enum { - eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java - eLayerOpaque = 0x02, // SURFACE_OPAQUE - eLayerSecure = 0x80, // SECURE + eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java + eLayerOpaque = 0x02, // SURFACE_OPAQUE + eLayerSkipScreenshot = 0x40, // SKIP_SCREENSHOT + eLayerSecure = 0x80, // SECURE + // Queue up BufferStateLayer buffers instead of dropping the oldest buffer when this flag is + // set. This blocks the client until all the buffers have been presented. If the buffers + // have presentation timestamps, then we may drop buffers. + eEnableBackpressure = 0x100, // ENABLE_BACKPRESSURE }; enum { @@ -73,10 +100,10 @@ struct layer_state_t { eLayerStackChanged = 0x00000080, eCropChanged_legacy = 0x00000100, eDeferTransaction_legacy = 0x00000200, - eOverrideScalingModeChanged = 0x00000400, + /* was ScalingModeChanged, now available 0x00000400, */ eShadowRadiusChanged = 0x00000800, eReparentChildren = 0x00001000, - eDetachChildren = 0x00002000, + /* was eDetachChildren, now available 0x00002000, */ eRelativeLayerChanged = 0x00004000, eReparent = 0x00008000, eColorChanged = 0x00010000, @@ -105,45 +132,14 @@ struct layer_state_t { eBackgroundBlurRadiusChanged = 0x80'00000000, eProducerDisconnect = 0x100'00000000, eFixedTransformHintChanged = 0x200'00000000, + eFrameNumberChanged = 0x400'00000000, + eFrameTimelineInfoChanged = 0x800'00000000, + eBlurRegionsChanged = 0x1000'00000000, + eAutoRefreshChanged = 0x2000'00000000, + eStretchChanged = 0x4000'00000000, }; - layer_state_t() - : what(0), - x(0), - y(0), - z(0), - w(0), - h(0), - layerStack(0), - alpha(0), - flags(0), - mask(0), - reserved(0), - crop_legacy(Rect::INVALID_RECT), - cornerRadius(0.0f), - backgroundBlurRadius(0), - frameNumber_legacy(0), - overrideScalingMode(-1), - transform(0), - transformToDisplayInverse(false), - crop(Rect::INVALID_RECT), - frame(Rect::INVALID_RECT), - dataspace(ui::Dataspace::UNKNOWN), - surfaceDamageRegion(), - api(-1), - colorTransform(mat4()), - bgColorAlpha(0), - bgColorDataspace(ui::Dataspace::UNKNOWN), - colorSpaceAgnostic(false), - shadowRadius(0.0f), - frameRateSelectionPriority(-1), - frameRate(0.0f), - frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT), - fixedTransformHint(ui::Transform::ROT_INVALID) { - matrix.dsdx = matrix.dtdy = 1.0f; - matrix.dsdy = matrix.dtdx = 0.0f; - hdrMetadata.validTypes = 0; - } + layer_state_t(); void merge(const layer_state_t& other); status_t write(Parcel& output) const; @@ -154,8 +150,11 @@ struct layer_state_t { float dtdx{0}; float dtdy{0}; float dsdy{0}; + status_t write(Parcel& output) const; + status_t read(const Parcel& input); }; sp<IBinder> surface; + int32_t layerId; uint64_t what; float x; float y; @@ -164,23 +163,20 @@ struct layer_state_t { uint32_t h; uint32_t layerStack; float alpha; - uint8_t flags; - uint8_t mask; + uint32_t flags; + uint32_t mask; uint8_t reserved; matrix22_t matrix; Rect crop_legacy; float cornerRadius; uint32_t backgroundBlurRadius; - sp<IBinder> barrierHandle_legacy; - sp<IBinder> reparentHandle; - uint64_t frameNumber_legacy; - int32_t overrideScalingMode; - - sp<IGraphicBufferProducer> barrierGbp_legacy; + sp<SurfaceControl> barrierSurfaceControl_legacy; + sp<SurfaceControl> reparentSurfaceControl; + uint64_t barrierFrameNumber; - sp<IBinder> relativeLayerHandle; + sp<SurfaceControl> relativeLayerSurfaceControl; - sp<IBinder> parentHandleForChild; + sp<SurfaceControl> parentSurfaceControlForChild; half3 color; @@ -190,7 +186,7 @@ struct layer_state_t { uint32_t transform; bool transformToDisplayInverse; Rect crop; - Rect frame; + Rect orientedDisplaySpaceRect; sp<GraphicBuffer> buffer; sp<Fence> acquireFence; ui::Dataspace dataspace; @@ -199,9 +195,10 @@ struct layer_state_t { int32_t api; sp<NativeHandle> sidebandStream; mat4 colorTransform; + std::vector<BlurRegion> blurRegions; #ifndef NO_INPUT - InputWindowInfo inputInfo; + sp<InputWindowHandle> inputHandle = new InputWindowHandle(); #endif client_cache_t cachedBuffer; @@ -228,6 +225,7 @@ struct layer_state_t { // Layer frame rate and compatibility. See ANativeWindow_setFrameRate(). float frameRate; int8_t frameRateCompatibility; + bool shouldBeSeamless; // Set by window manager indicating the layer and all its children are // in a different orientation than the display. The hint suggests that @@ -237,6 +235,20 @@ struct layer_state_t { // a buffer of a different size. -1 means the transform hint is not set, // otherwise the value will be a valid ui::Rotation. ui::Transform::RotationFlags fixedTransformHint; + + // Used by BlastBufferQueue to forward the framenumber generated by the + // graphics producer. + uint64_t frameNumber; + + FrameTimelineInfo frameTimelineInfo; + + // Indicates that the consumer should acquire the next frame as soon as it + // can and not wait for a frame to become available. This is only relevant + // in shared buffer mode. + bool autoRefresh; + + // Stretch effect to be applied to this layer + StretchEffect stretchEffect; }; struct ComposerState { @@ -263,18 +275,18 @@ struct DisplayState { // These states define how layers are projected onto the physical display. // - // Layers are first clipped to `viewport'. They are then translated and - // scaled from `viewport' to `frame'. Finally, they are rotated according - // to `orientation', `width', and `height'. + // Layers are first clipped to `layerStackSpaceRect'. They are then translated and + // scaled from `layerStackSpaceRect' to `orientedDisplaySpaceRect'. Finally, they are rotated + // according to `orientation', `width', and `height'. // - // For example, assume viewport is Rect(0, 0, 200, 100), frame is Rect(20, - // 10, 420, 210), and the size of the display is WxH. When orientation is - // 0, layers will be scaled by a factor of 2 and translated by (20, 10). - // When orientation is 1, layers will be additionally rotated by 90 - // degrees around the origin clockwise and translated by (W, 0). + // For example, assume layerStackSpaceRect is Rect(0, 0, 200, 100), orientedDisplaySpaceRect is + // Rect(20, 10, 420, 210), and the size of the display is WxH. When orientation is 0, layers + // will be scaled by a factor of 2 and translated by (20, 10). When orientation is 1, layers + // will be additionally rotated by 90 degrees around the origin clockwise and translated by (W, + // 0). ui::Rotation orientation = ui::ROTATION_0; - Rect viewport; - Rect frame; + Rect layerStackSpaceRect; + Rect orientedDisplaySpaceRect; uint32_t width, height; @@ -283,12 +295,17 @@ struct DisplayState { }; struct InputWindowCommands { +#ifndef NO_INPUT + std::vector<FocusRequest> focusRequests; +#endif bool syncInputWindows{false}; - void merge(const InputWindowCommands& other); + // Merges the passed in commands and returns true if there were any changes. + bool merge(const InputWindowCommands& other); + bool empty() const; void clear(); - void write(Parcel& output) const; - void read(const Parcel& input); + status_t write(Parcel& output) const; + status_t read(const Parcel& input); }; static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) { @@ -301,11 +318,64 @@ static inline int compare_type(const DisplayState& lhs, const DisplayState& rhs) return compare_type(lhs.token, rhs.token); } -// Returns true if the frameRate and compatibility are valid values, false -// othwerise. If either of the params are invalid, an error log is printed, and -// functionName is added to the log to indicate which function call failed. -// functionName can be null. -bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* functionName); +// Returns true if the frameRate is valid. +// +// @param frameRate the frame rate in Hz +// @param compatibility a ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* +// @param functionName calling function or nullptr. Used for logging +// @param privileged whether caller has unscoped surfaceflinger access +bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* functionName, + bool privileged = false); + +struct CaptureArgs { + const static int32_t UNSET_UID = -1; + virtual ~CaptureArgs() = default; + + ui::PixelFormat pixelFormat{ui::PixelFormat::RGBA_8888}; + Rect sourceCrop; + float frameScaleX{1}; + float frameScaleY{1}; + bool captureSecureLayers{false}; + int32_t uid{UNSET_UID}; + // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured + // result will be in the display's colorspace. + // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be + // different from SRGB (byte per color), and failed when checking colors in tests. + // NOTE: In normal cases, we want the screen to be captured in display's colorspace. + ui::Dataspace dataspace = ui::Dataspace::UNKNOWN; + + // The receiver of the capture can handle protected buffer. A protected buffer has + // GRALLOC_USAGE_PROTECTED usage bit and must not be accessed unprotected behaviour. + // Any read/write access from unprotected context will result in undefined behaviour. + // Protected contents are typically DRM contents. This has no direct implication to the + // secure property of the surface, which is specified by the application explicitly to avoid + // the contents being accessed/captured by screenshot or unsecure display. + bool allowProtected = false; + + bool grayscale = false; + + virtual status_t write(Parcel& output) const; + virtual status_t read(const Parcel& input); +}; + +struct DisplayCaptureArgs : CaptureArgs { + sp<IBinder> displayToken; + uint32_t width{0}; + uint32_t height{0}; + bool useIdentityTransform{false}; + + status_t write(Parcel& output) const override; + status_t read(const Parcel& input) override; +}; + +struct LayerCaptureArgs : CaptureArgs { + sp<IBinder> layerHandle; + std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeHandles; + bool childrenOnly{false}; + + status_t write(Parcel& output) const override; + status_t read(const Parcel& input) override; +}; }; // namespace android diff --git a/libs/gui/include/gui/ScreenCaptureResults.h b/libs/gui/include/gui/ScreenCaptureResults.h new file mode 100644 index 0000000000..0ccc6e8b8a --- /dev/null +++ b/libs/gui/include/gui/ScreenCaptureResults.h @@ -0,0 +1,49 @@ +/* + * Copyright 2021 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 + +#define SAFE_PARCEL(FUNC, ...) \ + { \ + status_t error = FUNC(__VA_ARGS__); \ + if (error) { \ + ALOGE("ERROR(%d). Failed to call parcel %s(%s)", error, #FUNC, #__VA_ARGS__); \ + return error; \ + } \ + } + +#include <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <ui/Fence.h> +#include <ui/GraphicBuffer.h> + +namespace android::gui { + +struct ScreenCaptureResults : public Parcelable { +public: + ScreenCaptureResults() = default; + virtual ~ScreenCaptureResults() = default; + status_t writeToParcel(android::Parcel* parcel) const override; + status_t readFromParcel(const android::Parcel* parcel) override; + + sp<GraphicBuffer> buffer; + sp<Fence> fence = Fence::NO_FENCE; + bool capturedSecureLayers{false}; + ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB}; + status_t result = OK; +}; + +} // namespace android::gui diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 55b4101908..58812214d2 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -18,6 +18,7 @@ #define ANDROID_GUI_SURFACE_H #include <gui/BufferQueueDefs.h> +#include <gui/FrameTimelineInfo.h> #include <gui/HdrMetadata.h> #include <gui/IGraphicBufferProducer.h> #include <gui/IProducerListener.h> @@ -68,7 +69,6 @@ class Surface : public ANativeObjectBase<ANativeWindow, Surface, RefBase> { public: - /* * creates a Surface from the given IGraphicBufferProducer (which concrete * implementation is a BufferQueue). @@ -83,9 +83,15 @@ public: * * the controlledByApp flag indicates that this Surface (producer) is * controlled by the application. This flag is used at connect time. + * + * Pass in the SurfaceControlHandle to store a weak reference to the layer + * that the Surface was created from. This handle can be used to create a + * child surface without using the IGBP to identify the layer. This is used + * for surfaces created by the BlastBufferQueue whose IGBP is created on the + * client and cannot be verified in SF. */ - explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer, - bool controlledByApp = false); + explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp = false, + const sp<IBinder>& surfaceControlHandle = nullptr); /* getIGraphicBufferProducer() returns the IGraphicBufferProducer this * Surface was created with. Usually it's an error to use the @@ -93,6 +99,8 @@ public: */ sp<IGraphicBufferProducer> getIGraphicBufferProducer() const; + sp<IBinder> getSurfaceControlHandle() const { return mSurfaceControlHandle; } + /* convenience function to check that the given surface is non NULL as * well as its IGraphicBufferProducer */ static bool isValid(const sp<Surface>& surface) { @@ -120,7 +128,7 @@ public: * delay during dequeueBuffer. If there are already the maximum number of * buffers allocated, this function has no effect. */ - void allocateBuffers(); + virtual void allocateBuffers(); /* Sets the generation number on the IGraphicBufferProducer and updates the * generation number on any buffers attached to the Surface after this call. @@ -179,7 +187,9 @@ public: status_t getUniqueId(uint64_t* outId) const; status_t getConsumerUsage(uint64_t* outUsage) const; - status_t setFrameRate(float frameRate, int8_t compatibility); + virtual status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless); + virtual status_t setFrameTimelineInfo(const FrameTimelineInfo& info); + virtual status_t getExtraBufferCount(int* extraBuffers) const; protected: virtual ~Surface(); @@ -265,6 +275,8 @@ private: int dispatchAddQueueInterceptor(va_list args); int dispatchAddQueryInterceptor(va_list args); int dispatchGetLastQueuedBuffer(va_list args); + int dispatchSetFrameTimelineInfo(va_list args); + int dispatchGetExtraBufferCount(va_list args); bool transformToDisplayInverse(); protected: @@ -335,6 +347,23 @@ public: static status_t attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer, ui::Dataspace dataspace); + // Batch version of dequeueBuffer, cancelBuffer and queueBuffer + // Note that these batched operations are not supported when shared buffer mode is being used. + struct BatchBuffer { + ANativeWindowBuffer* buffer = nullptr; + int fenceFd = -1; + }; + virtual int dequeueBuffers(std::vector<BatchBuffer>* buffers); + virtual int cancelBuffers(const std::vector<BatchBuffer>& buffers); + + struct BatchQueuedBuffer { + ANativeWindowBuffer* buffer = nullptr; + int fenceFd = -1; + nsecs_t timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; + }; + virtual int queueBuffers( + const std::vector<BatchQueuedBuffer>& buffers); + protected: enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS }; enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 }; @@ -364,6 +393,14 @@ protected: void freeAllBuffers(); int getSlotFromBufferLocked(android_native_buffer_t* buffer) const; + void getDequeueBufferInputLocked(IGraphicBufferProducer::DequeueBufferInput* dequeueInput); + + void getQueueBufferInputLocked(android_native_buffer_t* buffer, int fenceFd, nsecs_t timestamp, + IGraphicBufferProducer::QueueBufferInput* out); + + void onBufferQueuedLocked(int slot, sp<Fence> fence, + const IGraphicBufferProducer::QueueBufferOutput& output); + struct BufferSlot { sp<GraphicBuffer> buffer; Region dirtyRegion; @@ -539,6 +576,11 @@ protected: bool mEnableFrameTimestamps = false; std::unique_ptr<ProducerFrameEventHistory> mFrameEventHistory; + // Reference to the SurfaceFlinger layer that was used to create this + // surface. This is only populated when the Surface is created from + // a BlastBufferQueue. + sp<IBinder> mSurfaceControlHandle; + bool mReportRemovedBuffers = false; std::vector<sp<GraphicBuffer>> mRemovedBuffers; int mMaxBufferCount; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index adcb8982a0..e89f3a7b44 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -29,6 +29,7 @@ #include <utils/SortedVector.h> #include <utils/threads.h> +#include <ui/BlurRegion.h> #include <ui/ConfigStoreTypes.h> #include <ui/DisplayedFrameStats.h> #include <ui/FrameStats.h> @@ -109,28 +110,29 @@ public: // Get immutable information about given physical display. static status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*); - // Get configurations supported by given physical display. - static status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>*); + // Get modes supported by given physical display. + static status_t getDisplayModes(const sp<IBinder>& display, Vector<ui::DisplayMode>*); - // Get the ID of the active DisplayConfig, as getDisplayConfigs index. - static int getActiveConfig(const sp<IBinder>& display); + // Get the ID of the active DisplayMode, as getDisplayModes index. + static int getActiveDisplayModeId(const sp<IBinder>& display); - // Shorthand for getDisplayConfigs element at getActiveConfig index. - static status_t getActiveDisplayConfig(const sp<IBinder>& display, DisplayConfig*); + // Shorthand for getDisplayModes element at getActiveDisplayModeId index. + static status_t getActiveDisplayMode(const sp<IBinder>& display, ui::DisplayMode*); // Sets the refresh rate boundaries for the display. - static status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t defaultConfig, float primaryRefreshRateMin, - float primaryRefreshRateMax, - float appRequestRefreshRateMin, - float appRequestRefreshRateMax); + static status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t defaultMode, + bool allowGroupSwitching, + float primaryRefreshRateMin, + float primaryRefreshRateMax, + float appRequestRefreshRateMin, + float appRequestRefreshRateMax); // Gets the refresh rate boundaries for the display. - static status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t* outDefaultConfig, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax); + static status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + size_t* outDefaultMode, bool* outAllowGroupSwitching, + float* outPrimaryRefreshRateMin, + float* outPrimaryRefreshRateMax, + float* outAppRequestRefreshRateMin, + float* outAppRequestRefreshRateMax); // Gets the list of supported color modes for the given display static status_t getDisplayColorModes(const sp<IBinder>& display, @@ -182,6 +184,11 @@ public: static bool getProtectedContentSupport(); /** + * Gets the context priority of surface flinger's render engine. + */ + static int getGPUContextPriority(); + + /** * Uncaches a buffer in ISurfaceComposer. It must be uncached via a transaction so that it is * in order with other transactions that use buffers. */ @@ -217,14 +224,14 @@ public: static status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness); /* - * Sends a power hint to the composer. This function is asynchronous. + * Sends a power boost to the composer. This function is asynchronous. * - * hintId - * hint id according to android::hardware::power::V1_0::PowerHint + * boostId + * boost id according to android::hardware::power::Boost * * Returns NO_ERROR upon success. */ - static status_t notifyPowerHint(int32_t hintId); + static status_t notifyPowerBoost(int32_t boostId); /* * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows @@ -253,13 +260,13 @@ public: static sp<SurfaceComposerClient> getDefault(); //! Create a surface - sp<SurfaceControl> createSurface(const String8& name, // name of the surface - uint32_t w, // width in pixel - uint32_t h, // height in pixel - PixelFormat format, // pixel-format desired - uint32_t flags = 0, // usage flags - SurfaceControl* parent = nullptr, // parent - LayerMetadata metadata = LayerMetadata(), // metadata + sp<SurfaceControl> createSurface(const String8& name, // name of the surface + uint32_t w, // width in pixel + uint32_t h, // height in pixel + PixelFormat format, // pixel-format desired + uint32_t flags = 0, // usage flags + const sp<IBinder>& parentHandle = nullptr, // parentHandle + LayerMetadata metadata = LayerMetadata(), // metadata uint32_t* outTransformHint = nullptr); status_t createSurfaceChecked(const String8& name, // name of the surface @@ -267,9 +274,9 @@ public: 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 + const sp<IBinder>& parentHandle = nullptr, // parentHandle + LayerMetadata metadata = LayerMetadata(), // metadata uint32_t* outTransformHint = nullptr); //! Create a surface @@ -339,12 +346,18 @@ public: }; class Transaction : public Parcelable { + private: + static std::atomic<uint32_t> idCounter; + int64_t generateId(); + protected: std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates; - SortedVector<DisplayState > mDisplayStates; + SortedVector<DisplayState> mDisplayStates; std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> mListenerCallbacks; + uint64_t mId; + uint32_t mForceSynchronous = 0; uint32_t mTransactionNestCount = 0; bool mAnimation = false; @@ -359,28 +372,36 @@ public: // to be presented. When it is not possible to present at exactly that time, it will be // presented after the time has passed. // + // If the client didn't pass a desired presentation time, mDesiredPresentTime will be + // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true. + // // Desired present times that are more than 1 second in the future may be ignored. // When a desired present time has already passed, the transaction will be presented as soon // as possible. // // Transactions from the same process are presented in the same order that they are applied. // The desired present time does not affect this ordering. - int64_t mDesiredPresentTime = -1; + int64_t mDesiredPresentTime = 0; + bool mIsAutoTimestamp = true; + + // The vsync id provided by Choreographer.getVsyncId and the input event id + FrameTimelineInfo mFrameTimelineInfo; + + // If not null, transactions will be queued up using this token otherwise a common token + // per process will be used. + sp<IBinder> mApplyToken = nullptr; InputWindowCommands mInputWindowCommands; int mStatus = NO_ERROR; - layer_state_t* getLayerState(const sp<IBinder>& surfaceHandle); - layer_state_t* getLayerState(const sp<SurfaceControl>& sc) { - return getLayerState(sc->getHandle()); - } + layer_state_t* getLayerState(const sp<SurfaceControl>& sc); DisplayState& getDisplayState(const sp<IBinder>& token); void cacheBuffers(); void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc); public: - Transaction() = default; + Transaction(); virtual ~Transaction() = default; Transaction(Transaction const& other); @@ -418,7 +439,7 @@ public: // If the relative is removed, the Surface will have no layer and be // invisible, until the next time set(Relative)Layer is called. Transaction& setRelativeLayer(const sp<SurfaceControl>& sc, - const sp<IBinder>& relativeTo, int32_t z); + const sp<SurfaceControl>& relativeTo, int32_t z); Transaction& setFlags(const sp<SurfaceControl>& sc, uint32_t flags, uint32_t mask); Transaction& setTransparentRegionHint(const sp<SurfaceControl>& sc, @@ -431,6 +452,8 @@ public: Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius); Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc, int backgroundBlurRadius); + Transaction& setBlurRegions(const sp<SurfaceControl>& sc, + const std::vector<BlurRegion>& regions); Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack); Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p); // Defers applying any changes made in this transaction until the Layer @@ -438,22 +461,16 @@ public: // by handle is removed, then we will apply this transaction regardless of // what frame number has been reached. Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, - const sp<IBinder>& handle, uint64_t frameNumber); - // A variant of deferTransactionUntil_legacy which identifies the Layer we wait for by - // Surface instead of Handle. Useful for clients which may not have the - // SurfaceControl for some of their Surfaces. Otherwise behaves identically. - Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, - const sp<Surface>& barrierSurface, + const sp<SurfaceControl>& barrierSurfaceControl, uint64_t frameNumber); // Reparents all children of this layer to the new parent handle. Transaction& reparentChildren(const sp<SurfaceControl>& sc, - const sp<IBinder>& newParentHandle); + const sp<SurfaceControl>& newParent); /// Reparents the current layer to the new parent handle. The new parent must not be null. // This can be used instead of reparentChildren if the caller wants to // only re-parent a specific child. - Transaction& reparent(const sp<SurfaceControl>& sc, - const sp<IBinder>& newParentHandle); + Transaction& reparent(const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent); Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color); @@ -487,26 +504,12 @@ public: // ONLY FOR BLAST ADAPTER Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc); - - // Detaches all child surfaces (and their children recursively) - // from their SurfaceControl. - // The child SurfaceControls will not throw exceptions or return errors, - // but transactions will have no effect. - // The child surfaces will continue to follow their parent surfaces, - // and remain eligible for rendering, but their relative state will be - // frozen. We use this in the WindowManager, in app shutdown/relaunch - // scenarios, where the app would otherwise clean up its child Surfaces. - // Sometimes the WindowManager needs to extend their lifetime slightly - // in order to perform an exit animation or prevent flicker. - Transaction& detachChildren(const sp<SurfaceControl>& sc); - // Set an override scaling mode as documented in <system/window.h> - // the override scaling mode will take precedence over any client - // specified scaling mode. -1 will clear the override scaling mode. - Transaction& setOverrideScalingMode(const sp<SurfaceControl>& sc, - int32_t overrideScalingMode); + // Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour. + Transaction& setFrameNumber(const sp<SurfaceControl>& sc, uint64_t frameNumber); #ifndef NO_INPUT Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info); + Transaction& setFocusedWindow(const FocusRequest& request); Transaction& syncInputWindows(); #endif @@ -519,7 +522,7 @@ public: Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius); Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate, - int8_t compatibility); + int8_t compatibility, bool shouldBeSeamless); // Set by window manager indicating the layer and all its children are // in a different orientation than the display. The hint suggests that @@ -529,6 +532,28 @@ public: // a buffer of a different size. Transaction& setFixedTransformHint(const sp<SurfaceControl>& sc, int32_t transformHint); + // Sets the frame timeline vsync id received from choreographer that corresponds + // to the transaction, and the input event id that identifies the input event that caused + // the current frame. + Transaction& setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo); + // Variant that only applies to a specific SurfaceControl. + Transaction& setFrameTimelineInfo(const sp<SurfaceControl>& sc, + const FrameTimelineInfo& frameTimelineInfo); + + // Indicates that the consumer should acquire the next frame as soon as it + // can and not wait for a frame to become available. This is only relevant + // in shared buffer mode. + Transaction& setAutoRefresh(const sp<SurfaceControl>& sc, bool autoRefresh); + + // Queues up transactions using this token in SurfaceFlinger. By default, all transactions + // from a client are placed on the same queue. This can be used to prevent multiple + // transactions from blocking each other. + Transaction& setApplyToken(const sp<IBinder>& token); + + Transaction& setStretchEffect(const sp<SurfaceControl>& sc, float left, float top, + float right, float bottom, float vecX, float vecY, + float maxAmount); + status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); @@ -592,32 +617,22 @@ private: class ScreenshotClient { public: - // if cropping isn't required, callers may pass in a default Rect, e.g.: - // capture(display, producer, Rect(), reqWidth, ...); - static status_t capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ui::Rotation rotation, bool captureSecureLayers, - sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers); - static status_t capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ui::Rotation rotation, sp<GraphicBuffer>* outBuffer); - static status_t capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, - sp<GraphicBuffer>* outBuffer); - static status_t captureLayers(const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - float frameScale, sp<GraphicBuffer>* outBuffer); - static status_t captureChildLayers( - const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& - excludeHandles, - float frameScale, sp<GraphicBuffer>* outBuffer); + static status_t captureDisplay(const DisplayCaptureArgs& captureArgs, + const sp<IScreenCaptureListener>& captureListener); + static status_t captureDisplay(uint64_t displayOrLayerStack, + const sp<IScreenCaptureListener>& captureListener); + static status_t captureLayers(const LayerCaptureArgs& captureArgs, + const sp<IScreenCaptureListener>& captureListener); }; // --------------------------------------------------------------------------- +class JankDataListener : public VirtualLightRefBase { +public: + virtual ~JankDataListener() = 0; + virtual void onJankDataAvailable(const std::vector<JankData>& jankData) = 0; +}; + class TransactionCompletedListener : public BnTransactionCompletedListener { TransactionCompletedListener(); @@ -636,6 +651,7 @@ class TransactionCompletedListener : public BnTransactionCompletedListener { }; std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex); + std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex); public: static sp<TransactionCompletedListener> getInstance(); @@ -651,6 +667,18 @@ public: void addSurfaceControlToCallbacks(const sp<SurfaceControl>& surfaceControl, const std::unordered_set<CallbackId>& callbackIds); + /* + * Adds a jank listener to be informed about SurfaceFlinger's jank classification for a specific + * surface. Jank classifications arrive as part of the transaction callbacks about previous + * frames submitted to this Surface. + */ + void addJankListener(const sp<JankDataListener>& listener, sp<SurfaceControl> surfaceControl); + + /** + * Removes a jank listener previously added to addJankCallback. + */ + void removeJankListener(const sp<JankDataListener>& listener); + // Overrides BnTransactionCompletedListener's onTransactionCompleted void onTransactionCompleted(ListenerStats stats) override; }; diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h index ac2bbccfd2..35bdfc155d 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h @@ -20,7 +20,6 @@ #include <stdint.h> #include <sys/types.h> -#include <utils/KeyedVector.h> #include <utils/RefBase.h> #include <utils/threads.h> @@ -44,8 +43,12 @@ class SurfaceComposerClient; class SurfaceControl : public RefBase { public: - static sp<SurfaceControl> readFromParcel(const Parcel* parcel); - void writeToParcel(Parcel* parcel); + static status_t readFromParcel(const Parcel& parcel, sp<SurfaceControl>* outSurfaceControl); + status_t writeToParcel(Parcel& parcel); + + static status_t readNullableFromParcel(const Parcel& parcel, + sp<SurfaceControl>* outSurfaceControl); + static status_t writeNullableToParcel(Parcel& parcel, const sp<SurfaceControl>& surfaceControl); static bool isValid(const sp<SurfaceControl>& surface) { return (surface != nullptr) && surface->isValid(); @@ -70,6 +73,7 @@ public: sp<Surface> getSurface() const; sp<Surface> createSurface() const; sp<IBinder> getHandle() const; + int32_t getLayerId() const; sp<IGraphicBufferProducer> getIGraphicBufferProducer() const; @@ -85,7 +89,8 @@ public: explicit SurfaceControl(const sp<SurfaceControl>& other); SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle, - const sp<IGraphicBufferProducer>& gbp, uint32_t transformHint = 0); + const sp<IGraphicBufferProducer>& gbp, int32_t layerId, + uint32_t transformHint = 0); private: // can't be copied @@ -105,6 +110,7 @@ private: sp<IGraphicBufferProducer> mGraphicBufferProducer; mutable Mutex mLock; mutable sp<Surface> mSurfaceData; + int32_t mLayerId; uint32_t mTransformHint; }; diff --git a/libs/gui/include/gui/SyncScreenCaptureListener.h b/libs/gui/include/gui/SyncScreenCaptureListener.h new file mode 100644 index 0000000000..0784fbc058 --- /dev/null +++ b/libs/gui/include/gui/SyncScreenCaptureListener.h @@ -0,0 +1,45 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/gui/BnScreenCaptureListener.h> +#include <gui/SurfaceComposerClient.h> +#include <future> + +namespace android { + +using gui::ScreenCaptureResults; + +struct SyncScreenCaptureListener : gui::BnScreenCaptureListener { +public: + binder::Status onScreenCaptureCompleted(const ScreenCaptureResults& captureResults) override { + resultsPromise.set_value(captureResults); + return binder::Status::ok(); + } + + ScreenCaptureResults waitForResults() { + std::future<ScreenCaptureResults> resultsFuture = resultsPromise.get_future(); + const auto screenCaptureResults = resultsFuture.get(); + screenCaptureResults.fence->waitForever(""); + return screenCaptureResults; + } + +private: + std::promise<ScreenCaptureResults> resultsPromise; +}; + +} // namespace android
\ No newline at end of file diff --git a/libs/gui/include/gui/TransactionTracing.h b/libs/gui/include/gui/TransactionTracing.h new file mode 100644 index 0000000000..9efba47a18 --- /dev/null +++ b/libs/gui/include/gui/TransactionTracing.h @@ -0,0 +1,41 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/gui/BnTransactionTraceListener.h> +#include <utils/Mutex.h> + +namespace android { + +class TransactionTraceListener : public gui::BnTransactionTraceListener { + static std::mutex sMutex; + static sp<TransactionTraceListener> sInstance; + + TransactionTraceListener(); + +public: + static sp<TransactionTraceListener> getInstance(); + + binder::Status onToggled(bool enabled) override; + + bool isTracingEnabled(); + +private: + bool mTracingEnabled = false; +}; + +} // namespace android diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h index cc64fd45dd..f7dcbc698d 100644 --- a/libs/gui/include/gui/view/Surface.h +++ b/libs/gui/include/gui/view/Surface.h @@ -21,6 +21,7 @@ #include <utils/StrongPointer.h> #include <utils/String16.h> +#include <binder/IBinder.h> #include <binder/Parcelable.h> namespace android { @@ -43,6 +44,7 @@ class Surface : public Parcelable { String16 name; sp<IGraphicBufferProducer> graphicBufferProducer; + sp<IBinder> surfaceControlHandle; virtual status_t writeToParcel(Parcel* parcel) const override; virtual status_t readFromParcel(const Parcel* parcel) override; diff --git a/libs/gui/sysprop/Android.bp b/libs/gui/sysprop/Android.bp index bddb0ac5ee..64b1eacaa2 100644 --- a/libs/gui/sysprop/Android.bp +++ b/libs/gui/sysprop/Android.bp @@ -1,12 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - sysprop_library { name: "LibGuiProperties", srcs: ["*.sysprop"], diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 6077b4e59f..53c13c8859 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -2,15 +2,6 @@ // Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) // to integrate with auto-test framework. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_test { name: "libgui_test", test_suites: ["device-tests"], @@ -23,7 +14,7 @@ cc_test { srcs: [ "BLASTBufferQueue_test.cpp", - "BufferItemConsumer_test.cpp", + "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", "CpuConsumer_test.cpp", "EndToEndNativeInputTest.cpp", @@ -67,6 +58,30 @@ cc_test { header_libs: ["libsurfaceflinger_headers"], } +// Build the tests that need to run with both 32bit and 64bit. +cc_test { + name: "libgui_multilib_test", + test_suites: ["device-tests"], + + clang: true, + cflags: [ + "-Wall", + "-Werror", + ], + + srcs: [ + "DisplayEventStructLayout_test.cpp", + ], + + shared_libs: [ + "libgui", + ], + + compile_multilib: "both", + + header_libs: ["libsurfaceflinger_headers"], +} + // Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) // This test has a main method, and requires a separate binary to be built. // To add move tests like this, just add additional cc_test statements, diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index da5bbdd2e6..5447b7659b 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -24,9 +24,11 @@ #include <gui/FrameTimestamps.h> #include <gui/IGraphicBufferProducer.h> #include <gui/IProducerListener.h> +#include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> +#include <gui/SyncScreenCaptureListener.h> #include <private/gui/ComposerService.h> -#include <ui/DisplayConfig.h> +#include <ui/DisplayMode.h> #include <ui/GraphicBuffer.h> #include <ui/GraphicTypes.h> #include <ui/Transform.h> @@ -43,20 +45,21 @@ using android::hardware::graphics::common::V1_2::BufferUsage; class BLASTBufferQueueHelper { public: BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) { - mBlastBufferQueueAdapter = new BLASTBufferQueue(sc, width, height); + mBlastBufferQueueAdapter = new BLASTBufferQueue("TestBLASTBufferQueue", sc, width, height, + PIXEL_FORMAT_RGBA_8888); } void update(const sp<SurfaceControl>& sc, int width, int height) { - mBlastBufferQueueAdapter->update(sc, width, height); + mBlastBufferQueueAdapter->update(sc, width, height, PIXEL_FORMAT_RGBA_8888); } void setNextTransaction(Transaction* next) { mBlastBufferQueueAdapter->setNextTransaction(next); } - int getWidth() { return mBlastBufferQueueAdapter->mWidth; } + int getWidth() { return mBlastBufferQueueAdapter->mSize.width; } - int getHeight() { return mBlastBufferQueueAdapter->mHeight; } + int getHeight() { return mBlastBufferQueueAdapter->mSize.height; } Transaction* getNextTransaction() { return mBlastBufferQueueAdapter->mNextTransaction; } @@ -104,9 +107,9 @@ protected: t.apply(); t.clear(); - DisplayConfig config; - ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &config)); - const ui::Size& resolution = config.resolution; + ui::DisplayMode mode; + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &mode)); + const ui::Size& resolution = mode.resolution; mDisplayWidth = resolution.getWidth(); mDisplayHeight = resolution.getHeight(); @@ -120,6 +123,9 @@ protected: .show(mSurfaceControl) .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB) .apply(); + + mCaptureArgs.displayToken = mDisplayToken; + mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB; } void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) { @@ -165,18 +171,20 @@ protected: void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b, Rect region, int32_t border = 0, bool outsideRegion = false) { + sp<GraphicBuffer>& captureBuf = mCaptureResults.buffer; const auto epsilon = 3; - const auto width = mScreenCaptureBuf->getWidth(); - const auto height = mScreenCaptureBuf->getHeight(); - const auto stride = mScreenCaptureBuf->getStride(); + const auto width = captureBuf->getWidth(); + const auto height = captureBuf->getHeight(); + const auto stride = captureBuf->getStride(); uint32_t* bufData; - mScreenCaptureBuf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_READ_OFTEN), - reinterpret_cast<void**>(&bufData)); + captureBuf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_READ_OFTEN), + reinterpret_cast<void**>(&bufData)); for (uint32_t row = 0; row < height; row++) { for (uint32_t col = 0; col < width; col++) { uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col); + ASSERT_NE(nullptr, pixel); bool inRegion; if (!outsideRegion) { inRegion = row >= region.top + border && row < region.bottom - border && @@ -196,20 +204,62 @@ protected: } } } - mScreenCaptureBuf->unlock(); + captureBuf->unlock(); ASSERT_EQ(false, ::testing::Test::HasFailure()); } + static status_t captureDisplay(DisplayCaptureArgs& captureArgs, + ScreenCaptureResults& captureResults) { + const auto sf = ComposerService::getComposerService(); + SurfaceComposerClient::Transaction().apply(true); + + const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener(); + status_t status = sf->captureDisplay(captureArgs, captureListener); + if (status != NO_ERROR) { + return status; + } + captureResults = captureListener->waitForResults(); + return captureResults.result; + } + + void queueBuffer(sp<IGraphicBufferProducer> igbp, uint8_t r, uint8_t g, uint8_t b, + nsecs_t presentTimeDelay) { + int slot; + sp<Fence> fence; + sp<GraphicBuffer> buf; + auto ret = igbp->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight, + PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr); + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret); + ASSERT_EQ(OK, igbp->requestBuffer(slot, &buf)); + + uint32_t* bufData; + buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN), + reinterpret_cast<void**>(&bufData)); + fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight() / 2), buf->getStride(), r, g, b); + buf->unlock(); + + IGraphicBufferProducer::QueueBufferOutput qbOutput; + nsecs_t timestampNanos = systemTime() + presentTimeDelay; + IGraphicBufferProducer::QueueBufferInput input(timestampNanos, false, HAL_DATASPACE_UNKNOWN, + Rect(mDisplayWidth, mDisplayHeight / 2), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, + Fence::NO_FENCE); + igbp->queueBuffer(slot, input, &qbOutput); + } + sp<SurfaceComposerClient> mClient; sp<ISurfaceComposer> mComposer; sp<IBinder> mDisplayToken; sp<SurfaceControl> mSurfaceControl; - sp<GraphicBuffer> mScreenCaptureBuf; uint32_t mDisplayWidth; uint32_t mDisplayHeight; + + DisplayCaptureArgs mCaptureArgs; + ScreenCaptureResults mCaptureResults; }; TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) { @@ -228,8 +278,15 @@ TEST_F(BLASTBufferQueueTest, Update) { PIXEL_FORMAT_RGBA_8888); adapter.update(updateSurface, mDisplayWidth / 2, mDisplayHeight / 2); ASSERT_EQ(updateSurface, adapter.getSurfaceControl()); - ASSERT_EQ(mDisplayWidth / 2, adapter.getWidth()); - ASSERT_EQ(mDisplayHeight / 2, adapter.getHeight()); + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + + int32_t width; + igbProducer->query(NATIVE_WINDOW_WIDTH, &width); + ASSERT_EQ(mDisplayWidth / 2, width); + int32_t height; + igbProducer->query(NATIVE_WINDOW_HEIGHT, &height); + ASSERT_EQ(mDisplayHeight / 2, height); } TEST_F(BLASTBufferQueueTest, SetNextTransaction) { @@ -301,12 +358,7 @@ TEST_F(BLASTBufferQueueTest, onFrameAvailable_Apply) { adapter.waitForCallbacks(); // capture screen and verify that it is red - bool capturedSecureLayers; - ASSERT_EQ(NO_ERROR, - mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers, - ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), - mDisplayWidth, mDisplayHeight, - /*useIdentityTransform*/ false)); + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); ASSERT_NO_FATAL_FAILURE( checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); } @@ -383,12 +435,8 @@ TEST_F(BLASTBufferQueueTest, SetCrop_Item) { adapter.waitForCallbacks(); // capture screen and verify that it is red - bool capturedSecureLayers; - ASSERT_EQ(NO_ERROR, - mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers, - ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), - mDisplayWidth, mDisplayHeight, - /*useIdentityTransform*/ false)); + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); } @@ -444,12 +492,8 @@ TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) { adapter.waitForCallbacks(); // capture screen and verify that it is red - bool capturedSecureLayers; - ASSERT_EQ(NO_ERROR, - mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers, - ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), - mDisplayWidth, mDisplayHeight, - /*useIdentityTransform*/ false)); + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( checkScreenCapture(r, g, b, {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength})); @@ -459,6 +503,93 @@ TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) { /*border*/ 0, /*outsideRegion*/ true)); } +class TestProducerListener : public BnProducerListener { +public: + sp<IGraphicBufferProducer> mIgbp; + TestProducerListener(const sp<IGraphicBufferProducer>& igbp) : mIgbp(igbp) {} + void onBufferReleased() override { + sp<GraphicBuffer> buffer; + sp<Fence> fence; + mIgbp->detachNextBuffer(&buffer, &fence); + } +}; + +TEST_F(BLASTBufferQueueTest, CustomProducerListener) { + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + sp<IGraphicBufferProducer> igbProducer = adapter.getIGraphicBufferProducer(); + ASSERT_NE(nullptr, igbProducer.get()); + ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2)); + IGraphicBufferProducer::QueueBufferOutput qbOutput; + ASSERT_EQ(NO_ERROR, + igbProducer->connect(new TestProducerListener(igbProducer), NATIVE_WINDOW_API_CPU, + false, &qbOutput)); + ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint); + for (int i = 0; i < 3; i++) { + int slot; + sp<Fence> fence; + sp<GraphicBuffer> buf; + auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight, + PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr); + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret); + ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf)); + IGraphicBufferProducer::QueueBufferOutput qbOutput; + IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN, + Rect(mDisplayWidth, mDisplayHeight), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, + Fence::NO_FENCE); + igbProducer->queueBuffer(slot, input, &qbOutput); + } + adapter.waitForCallbacks(); +} + +TEST_F(BLASTBufferQueueTest, QueryNativeWindowQueuesToWindowComposer) { + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp<android::Surface> surface = new Surface(adapter.getIGraphicBufferProducer()); + ANativeWindow* nativeWindow = (ANativeWindow*)(surface.get()); + int queuesToNativeWindow = 0; + int err = nativeWindow->query(nativeWindow, NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, + &queuesToNativeWindow); + ASSERT_EQ(NO_ERROR, err); + ASSERT_EQ(queuesToNativeWindow, 1); +} + +TEST_F(BLASTBufferQueueTest, OutOfOrderTransactionTest) { + sp<SurfaceControl> bgSurface = + mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState); + ASSERT_NE(nullptr, bgSurface.get()); + Transaction t; + t.setLayerStack(bgSurface, 0) + .show(bgSurface) + .setDataspace(bgSurface, ui::Dataspace::V0_SRGB) + .setFrame(bgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight)) + .setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1) + .apply(); + + BLASTBufferQueueHelper slowAdapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + sp<IGraphicBufferProducer> slowIgbProducer; + setUpProducer(slowAdapter, slowIgbProducer); + nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count(); + queueBuffer(slowIgbProducer, 0 /* r */, 0 /* g */, 0 /* b */, presentTimeDelay); + + BLASTBufferQueueHelper fastAdapter(bgSurface, mDisplayWidth, mDisplayHeight); + sp<IGraphicBufferProducer> fastIgbProducer; + setUpProducer(fastAdapter, fastIgbProducer); + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + queueBuffer(fastIgbProducer, r, g, b, 0 /* presentTimeDelay */); + fastAdapter.waitForCallbacks(); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + class BLASTBufferQueueTransformTest : public BLASTBufferQueueTest { public: void test(uint32_t tr) { @@ -483,18 +614,14 @@ public: IGraphicBufferProducer::QueueBufferOutput qbOutput; IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN, Rect(bufWidth, bufHeight), - NATIVE_WINDOW_SCALING_MODE_FREEZE, tr, - Fence::NO_FENCE); + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, + tr, Fence::NO_FENCE); igbProducer->queueBuffer(slot, input, &qbOutput); ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint); adapter.waitForCallbacks(); - bool capturedSecureLayers; - ASSERT_EQ(NO_ERROR, - mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers, - ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, - Rect(), mDisplayWidth, mDisplayHeight, - /*useIdentityTransform*/ false)); + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + switch (tr) { case ui::Transform::ROT_0: ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0, diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp new file mode 100644 index 0000000000..7dbba31ba8 --- /dev/null +++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <gui/DisplayEventReceiver.h> + +namespace android::test { + +#define CHECK_OFFSET(type, member, expected_offset) \ + static_assert((offsetof(type, member) == (expected_offset)), "") + +TEST(DisplayEventStructLayoutTest, TestEventAlignment) { + CHECK_OFFSET(DisplayEventReceiver::Event, vsync, 24); + CHECK_OFFSET(DisplayEventReceiver::Event, hotplug, 24); + CHECK_OFFSET(DisplayEventReceiver::Event, modeChange, 24); + + CHECK_OFFSET(DisplayEventReceiver::Event::Header, type, 0); + CHECK_OFFSET(DisplayEventReceiver::Event::Header, displayId, 8); + CHECK_OFFSET(DisplayEventReceiver::Event::Header, timestamp, 16); + + CHECK_OFFSET(DisplayEventReceiver::Event::VSync, count, 0); + CHECK_OFFSET(DisplayEventReceiver::Event::VSync, expectedVSyncTimestamp, 8); + CHECK_OFFSET(DisplayEventReceiver::Event::VSync, deadlineTimestamp, 16); + CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncId, 24); + + CHECK_OFFSET(DisplayEventReceiver::Event::Hotplug, connected, 0); + + CHECK_OFFSET(DisplayEventReceiver::Event::ModeChange, modeId, 0); + CHECK_OFFSET(DisplayEventReceiver::Event::ModeChange, vsyncPeriod, 8); + + CHECK_OFFSET(DisplayEventReceiver::Event::FrameRateOverride, uid, 0); + CHECK_OFFSET(DisplayEventReceiver::Event::FrameRateOverride, frameRateHz, 8); +} + +} // namespace android::test diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index b1d3ecbf36..d65a40baf6 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -36,18 +36,18 @@ #include <gui/SurfaceComposerClient.h> #include <gui/SurfaceControl.h> -#include <input/InputWindow.h> -#include <input/IInputFlinger.h> -#include <input/InputTransport.h> +#include <android/os/IInputFlinger.h> #include <input/Input.h> +#include <input/InputTransport.h> +#include <input/InputWindow.h> -#include <ui/DisplayConfig.h> +#include <ui/DisplayMode.h> #include <ui/Rect.h> #include <ui/Region.h> +using android::os::IInputFlinger; -namespace android { -namespace test { +namespace android::test { using Transaction = SurfaceComposerClient::Transaction; @@ -62,16 +62,16 @@ sp<IInputFlinger> getInputFlinger() { // We use the top 10 layers as a way to haphazardly place ourselves above anything else. static const int LAYER_BASE = INT32_MAX - 10; +static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s; class InputSurface { public: InputSurface(const sp<SurfaceControl> &sc, int width, int height) { mSurfaceControl = sc; - InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel); - mInputFlinger = getInputFlinger(); - mInputFlinger->registerInputChannel(mServerChannel); + mClientChannel = std::make_shared<InputChannel>(); + mInputFlinger->createInputChannel("testchannels", mClientChannel.get()); populateInputInfo(width, height); @@ -95,6 +95,15 @@ public: return std::make_unique<InputSurface>(surfaceControl, width, height); } + static std::unique_ptr<InputSurface> makeBlastInputSurface(const sp<SurfaceComposerClient> &scc, + int width, int height) { + sp<SurfaceControl> surfaceControl = + scc->createSurface(String8("Test Buffer Surface"), width, height, + PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState); + return std::make_unique<InputSurface>(surfaceControl, width, height); + } + static std::unique_ptr<InputSurface> makeContainerInputSurface( const sp<SurfaceComposerClient> &scc, int width, int height) { sp<SurfaceControl> surfaceControl = @@ -153,10 +162,26 @@ public: EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS); } - ~InputSurface() { - mInputFlinger->unregisterInputChannel(mServerChannel); + void expectTapWithFlag(int x, int y, int32_t flags) { + InputEvent *ev = consumeEvent(); + ASSERT_NE(ev, nullptr); + ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType()); + MotionEvent *mev = static_cast<MotionEvent *>(ev); + EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction()); + EXPECT_EQ(x, mev->getX(0)); + EXPECT_EQ(y, mev->getY(0)); + EXPECT_EQ(flags, mev->getFlags() & flags); + + ev = consumeEvent(); + ASSERT_NE(ev, nullptr); + ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType()); + mev = static_cast<MotionEvent *>(ev); + EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction()); + EXPECT_EQ(flags, mev->getFlags() & flags); } + ~InputSurface() { mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken()); } + void doTransaction(std::function<void(SurfaceComposerClient::Transaction&, const sp<SurfaceControl>&)> transactionBody) { SurfaceComposerClient::Transaction t; @@ -164,17 +189,30 @@ public: t.apply(true); } - void showAt(int x, int y) { + void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) { SurfaceComposerClient::Transaction t; t.show(mSurfaceControl); t.setInputWindowInfo(mSurfaceControl, mInputInfo); t.setLayer(mSurfaceControl, LAYER_BASE); t.setPosition(mSurfaceControl, x, y); - t.setCrop_legacy(mSurfaceControl, Rect(0, 0, 100, 100)); + t.setCrop_legacy(mSurfaceControl, crop); t.setAlpha(mSurfaceControl, 1); t.apply(true); } + void requestFocus() { + SurfaceComposerClient::Transaction t; + FocusRequest request; + request.token = mInputInfo.token; + request.windowName = mInputInfo.name; + request.focusedToken = nullptr; + request.focusedWindowName = ""; + request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); + request.displayId = 0; + t.setFocusedWindow(request); + t.apply(true); + } + private: void waitForEventAvailable() { struct pollfd fd; @@ -185,14 +223,13 @@ private: } void populateInputInfo(int width, int height) { - mInputInfo.token = mServerChannel->getConnectionToken(); + mInputInfo.token = mClientChannel->getConnectionToken(); mInputInfo.name = "Test info"; - mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL; - mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION; - mInputInfo.dispatchingTimeout = seconds_to_nanoseconds(5); + mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL; + mInputInfo.type = InputWindowInfo::Type::BASE_APPLICATION; + mInputInfo.dispatchingTimeout = 5s; mInputInfo.globalScaleFactor = 1.0; - mInputInfo.canReceiveKeys = true; - mInputInfo.hasFocus = true; + mInputInfo.focusable = true; mInputInfo.hasWallpaper = false; mInputInfo.paused = false; @@ -201,19 +238,19 @@ private: // TODO: Fill in from SF? mInputInfo.ownerPid = 11111; mInputInfo.ownerUid = 11111; - mInputInfo.inputFeatures = 0; mInputInfo.displayId = 0; InputApplicationInfo aInfo; aInfo.token = new BBinder(); aInfo.name = "Test app info"; - aInfo.dispatchingTimeout = seconds_to_nanoseconds(5); + aInfo.dispatchingTimeoutMillis = + std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count(); mInputInfo.applicationInfo = aInfo; } public: sp<SurfaceControl> mSurfaceControl; - sp<InputChannel> mServerChannel, mClientChannel; + std::shared_ptr<InputChannel> mClientChannel; sp<IInputFlinger> mInputFlinger; InputWindowInfo mInputInfo; @@ -235,13 +272,13 @@ public: const auto display = mComposerClient->getInternalDisplayToken(); ASSERT_NE(display, nullptr); - DisplayConfig config; - ASSERT_EQ(NO_ERROR, mComposerClient->getActiveDisplayConfig(display, &config)); + ui::DisplayMode mode; + ASSERT_EQ(NO_ERROR, mComposerClient->getActiveDisplayMode(display, &mode)); // After a new buffer is queued, SurfaceFlinger is notified and will // latch the new buffer on next vsync. Let's heuristically wait for 3 // vsyncs. - mBufferPostDelay = static_cast<int32_t>(1e6 / config.refreshRate) * 3; + mBufferPostDelay = static_cast<int32_t>(1e6 / mode.refreshRate) * 3; } void TearDown() { @@ -280,7 +317,6 @@ void injectTap(int x, int y) { TEST_F(InputSurfacesTest, can_receive_input) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(100, 100); - surface->assertFocusChange(true); injectTap(101, 101); @@ -296,12 +332,9 @@ TEST_F(InputSurfacesTest, can_receive_input) { TEST_F(InputSurfacesTest, input_respects_positioning) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(100, 100); - surface->assertFocusChange(true); std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100); surface2->showAt(200, 200); - surface->assertFocusChange(false); - surface2->assertFocusChange(true); injectTap(201, 201); surface2->expectTap(1, 1); @@ -328,16 +361,11 @@ TEST_F(InputSurfacesTest, input_respects_layering) { std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100); surface->showAt(10, 10); - surface->assertFocusChange(true); surface2->showAt(10, 10); - surface->assertFocusChange(false); - surface2->assertFocusChange(true); surface->doTransaction([](auto &t, auto &sc) { t.setLayer(sc, LAYER_BASE + 1); }); - surface2->assertFocusChange(false); - surface->assertFocusChange(true); injectTap(11, 11); surface->expectTap(1, 1); @@ -345,8 +373,6 @@ TEST_F(InputSurfacesTest, input_respects_layering) { surface2->doTransaction([](auto &t, auto &sc) { t.setLayer(sc, LAYER_BASE + 1); }); - surface2->assertFocusChange(true); - surface->assertFocusChange(false); injectTap(11, 11); surface2->expectTap(1, 1); @@ -354,8 +380,6 @@ TEST_F(InputSurfacesTest, input_respects_layering) { surface2->doTransaction([](auto &t, auto &sc) { t.hide(sc); }); - surface2->assertFocusChange(false); - surface->assertFocusChange(true); injectTap(11, 11); surface->expectTap(1, 1); @@ -368,12 +392,9 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets) { std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); - bgSurface->assertFocusChange(true); fgSurface->mInputInfo.surfaceInset = 5; fgSurface->showAt(100, 100); - fgSurface->assertFocusChange(true); - bgSurface->assertFocusChange(false); injectTap(106, 106); fgSurface->expectTap(1, 1); @@ -387,16 +408,13 @@ TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) { std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100); parentSurface->showAt(100, 100); - parentSurface->assertFocusChange(true); childSurface->mInputInfo.surfaceInset = 10; childSurface->showAt(100, 100); - childSurface->assertFocusChange(true); - parentSurface->assertFocusChange(false); childSurface->doTransaction([&](auto &t, auto &sc) { t.setPosition(sc, -5, -5); - t.reparent(sc, parentSurface->mSurfaceControl->getHandle()); + t.reparent(sc, parentSurface->mSurfaceControl); }); injectTap(106, 106); @@ -411,12 +429,9 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); - bgSurface->assertFocusChange(true); fgSurface->mInputInfo.surfaceInset = 5; fgSurface->showAt(100, 100); - bgSurface->assertFocusChange(false); - fgSurface->assertFocusChange(true); fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); }); @@ -433,7 +448,6 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { // In case we pass the very big inset without any checking. fgSurface->mInputInfo.surfaceInset = INT32_MAX; fgSurface->showAt(100, 100); - fgSurface->assertFocusChange(true); fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); }); @@ -450,7 +464,6 @@ TEST_F(InputSurfacesTest, input_ignores_transparent_region) { t.setTransparentRegionHint(sc, transparentRegion); }); surface->showAt(100, 100); - surface->assertFocusChange(true); injectTap(101, 101); surface->expectTap(1, 1); } @@ -465,10 +478,7 @@ TEST_F(InputSurfacesTest, input_ignores_buffer_layer_buffer) { InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); bgSurface->showAt(10, 10); - bgSurface->assertFocusChange(true); bufferSurface->showAt(10, 10); - bgSurface->assertFocusChange(false); - bufferSurface->assertFocusChange(true); injectTap(11, 11); bufferSurface->expectTap(1, 1); @@ -485,10 +495,7 @@ TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) { postBuffer(bufferSurface->mSurfaceControl); bgSurface->showAt(10, 10); - bgSurface->assertFocusChange(true); bufferSurface->showAt(10, 10); - bufferSurface->assertFocusChange(true); - bgSurface->assertFocusChange(false); injectTap(11, 11); bufferSurface->expectTap(1, 1); @@ -504,10 +511,7 @@ TEST_F(InputSurfacesTest, input_ignores_color_layer_alpha) { std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(10, 10); - bgSurface->assertFocusChange(true); fgSurface->showAt(10, 10); - bgSurface->assertFocusChange(false); - fgSurface->assertFocusChange(true); injectTap(11, 11); fgSurface->expectTap(1, 1); @@ -524,17 +528,12 @@ TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); bgSurface->showAt(10, 10); - bgSurface->assertFocusChange(true); containerSurface->showAt(10, 10); - bgSurface->assertFocusChange(false); - containerSurface->assertFocusChange(true); injectTap(11, 11); containerSurface->expectTap(1, 1); containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); }); - containerSurface->assertFocusChange(false); - bgSurface->assertFocusChange(true); injectTap(11, 11); bgSurface->expectTap(1, 1); @@ -543,7 +542,6 @@ TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { TEST_F(InputSurfacesTest, input_respects_outscreen) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(-1, -1); - surface->assertFocusChange(true); injectTap(0, 0); surface->expectTap(1, 1); @@ -555,11 +553,183 @@ TEST_F(InputSurfacesTest, input_ignores_cursor_layer) { InputSurface::makeCursorInputSurface(mComposerClient, 10, 10); surface->showAt(10, 10); - surface->assertFocusChange(true); cursorSurface->showAt(10, 10); injectTap(11, 11); surface->expectTap(1, 1); } + +TEST_F(InputSurfacesTest, can_be_focused) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(100, 100); + surface->requestFocus(); + + surface->assertFocusChange(true); } + +TEST_F(InputSurfacesTest, rotate_surface) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(10, 10); + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, 0, 1, -1, 0); // 90 degrees + }); + injectTap(8, 11); + surface->expectTap(1, 2); + + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, -1, 0, 0, -1); // 180 degrees + }); + injectTap(9, 8); + surface->expectTap(1, 2); + + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, 0, -1, 1, 0); // 270 degrees + }); + injectTap(12, 9); + surface->expectTap(1, 2); } + +TEST_F(InputSurfacesTest, rotate_surface_with_scale) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(10, 10); + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees + }); + injectTap(2, 12); + surface->expectTap(1, 2); + + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees + }); + injectTap(8, 2); + surface->expectTap(1, 2); + + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees + }); + injectTap(18, 8); + surface->expectTap(1, 2); +} + +TEST_F(InputSurfacesTest, rotate_surface_with_scale_and_insets) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->mInputInfo.surfaceInset = 5; + surface->showAt(100, 100); + + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees + }); + injectTap(40, 120); + surface->expectTap(5, 10); + + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees + }); + injectTap(80, 40); + surface->expectTap(5, 10); + + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees + }); + injectTap(160, 80); + surface->expectTap(5, 10); +} + +TEST_F(InputSurfacesTest, touch_flag_obscured) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(100, 100); + + // Add non touchable window to fully cover touchable window. Window behind gets touch, but + // with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED + std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100); + nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE; + nonTouchableSurface->mInputInfo.ownerUid = 22222; + // Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by + // the default obscured/untrusted touch filter introduced in S. + nonTouchableSurface->mInputInfo.touchOcclusionMode = TouchOcclusionMode::ALLOW; + nonTouchableSurface->showAt(100, 100); + + injectTap(190, 199); + surface->expectTapWithFlag(90, 99, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED); +} + +TEST_F(InputSurfacesTest, touch_flag_partially_obscured_with_crop) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(100, 100); + + // Add non touchable window to cover touchable window, but parent is cropped to not cover area + // that will be tapped. Window behind gets touch, but with flag + // AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED + std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100); + nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE; + parentSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE; + nonTouchableSurface->mInputInfo.ownerUid = 22222; + parentSurface->mInputInfo.ownerUid = 22222; + nonTouchableSurface->showAt(0, 0); + parentSurface->showAt(100, 100); + + nonTouchableSurface->doTransaction([&](auto &t, auto &sc) { + t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50)); + t.reparent(sc, parentSurface->mSurfaceControl); + }); + + injectTap(190, 199); + surface->expectTapWithFlag(90, 99, AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED); +} + +TEST_F(InputSurfacesTest, touch_not_obscured_with_crop) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(100, 100); + + // Add non touchable window to cover touchable window, but parent is cropped to avoid covering + // the touchable window. Window behind gets touch with no obscured flags. + std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100); + nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE; + parentSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE; + nonTouchableSurface->mInputInfo.ownerUid = 22222; + parentSurface->mInputInfo.ownerUid = 22222; + nonTouchableSurface->showAt(0, 0); + parentSurface->showAt(50, 50); + + nonTouchableSurface->doTransaction([&](auto &t, auto &sc) { + t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50)); + t.reparent(sc, parentSurface->mSurfaceControl); + }); + + injectTap(101, 110); + surface->expectTap(1, 10); +} + +TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_bql) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + + std::unique_ptr<InputSurface> bufferSurface = + InputSurface::makeBufferInputSurface(mComposerClient, 0, 0); + bufferSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE; + bufferSurface->mInputInfo.ownerUid = 22222; + + surface->showAt(10, 10); + bufferSurface->showAt(50, 50, Rect::EMPTY_RECT); + + injectTap(11, 11); + surface->expectTap(1, 1); +} + +TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_blast) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + + std::unique_ptr<InputSurface> bufferSurface = + InputSurface::makeBlastInputSurface(mComposerClient, 0, 0); + bufferSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE; + bufferSurface->mInputInfo.ownerUid = 22222; + + surface->showAt(10, 10); + bufferSurface->showAt(50, 50, Rect::EMPTY_RECT); + + injectTap(11, 11); + surface->expectTap(1, 1); +} + +} // namespace android::test diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp index 15bd32d354..2af2fe1f91 100644 --- a/libs/gui/tests/IGraphicBufferProducer_test.cpp +++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp @@ -70,6 +70,9 @@ namespace { const int QUEUE_BUFFER_INPUT_SCALING_MODE = 0; const int QUEUE_BUFFER_INPUT_TRANSFORM = 0; const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE; + const uint32_t QUEUE_BUFFER_INPUT_STICKY_TRANSFORM = 0; + const bool QUEUE_BUFFER_INPUT_GET_TIMESTAMPS = 0; + const int QUEUE_BUFFER_INPUT_SLOT = -1; // Enums to control which IGraphicBufferProducer backend to test. enum IGraphicBufferProducerTestCode { @@ -156,6 +159,9 @@ protected: scalingMode = QUEUE_BUFFER_INPUT_SCALING_MODE; transform = QUEUE_BUFFER_INPUT_TRANSFORM; fence = QUEUE_BUFFER_INPUT_FENCE; + stickyTransform = QUEUE_BUFFER_INPUT_STICKY_TRANSFORM; + getTimestamps = QUEUE_BUFFER_INPUT_GET_TIMESTAMPS; + slot = QUEUE_BUFFER_INPUT_SLOT; } IGraphicBufferProducer::QueueBufferInput build() { @@ -166,7 +172,10 @@ protected: crop, scalingMode, transform, - fence); + fence, + stickyTransform, + getTimestamps, + slot); } QueueBufferInputBuilder& setTimestamp(int64_t timestamp) { @@ -204,6 +213,21 @@ protected: return *this; } + QueueBufferInputBuilder& setStickyTransform(uint32_t stickyTransform) { + this->stickyTransform = stickyTransform; + return *this; + } + + QueueBufferInputBuilder& setGetTimestamps(bool getTimestamps) { + this->getTimestamps = getTimestamps; + return *this; + } + + QueueBufferInputBuilder& setSlot(int slot) { + this->slot = slot; + return *this; + } + private: int64_t timestamp; bool isAutoTimestamp; @@ -212,17 +236,17 @@ protected: int scalingMode; uint32_t transform; sp<Fence> fence; - }; // struct QueueBufferInputBuilder - - // To easily store dequeueBuffer results into containers - struct DequeueBufferResult { + uint32_t stickyTransform; + bool getTimestamps; int slot; - sp<Fence> fence; - }; + }; // struct QueueBufferInputBuilder - status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) { - return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage, - nullptr, nullptr); + status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, + IGraphicBufferProducer::DequeueBufferOutput* result) { + result->result = + mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage, + &result->bufferAge, nullptr); + return result->result; } void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence, @@ -336,6 +360,27 @@ TEST_P(IGraphicBufferProducerTest, Query_Succeeds) { EXPECT_OK(mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value)); EXPECT_EQ(DEFAULT_CONSUMER_USAGE_BITS, value); + { // Test the batched version + std::vector<int32_t> inputs = { + NATIVE_WINDOW_WIDTH, + NATIVE_WINDOW_HEIGHT, + NATIVE_WINDOW_FORMAT, + NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, + NATIVE_WINDOW_CONSUMER_USAGE_BITS }; + using QueryOutput = IGraphicBufferProducer::QueryOutput; + std::vector<QueryOutput> outputs; + EXPECT_OK(mProducer->query(inputs, &outputs)); + EXPECT_EQ(DEFAULT_WIDTH, static_cast<uint32_t>(outputs[0].value)); + EXPECT_EQ(DEFAULT_HEIGHT, static_cast<uint32_t>(outputs[1].value)); + EXPECT_EQ(DEFAULT_FORMAT, outputs[2].value); + EXPECT_LE(0, outputs[3].value); + EXPECT_FALSE(outputs[4].value); + EXPECT_EQ(DEFAULT_CONSUMER_USAGE_BITS, outputs[5].value); + for (const QueryOutput& output : outputs) { + EXPECT_OK(output.result); + } + } } TEST_P(IGraphicBufferProducerTest, Query_ReturnsError) { @@ -358,6 +403,24 @@ TEST_P(IGraphicBufferProducerTest, Query_ReturnsError) { EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value)); // TODO: Consider documented the above enums as unsupported or make a new enum for IGBP + { // Test the batched version + std::vector<int32_t> inputs = { + -1, + static_cast<int32_t>(0xDEADBEEF), + NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE, + NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, + NATIVE_WINDOW_CONCRETE_TYPE, + NATIVE_WINDOW_DEFAULT_WIDTH, + NATIVE_WINDOW_DEFAULT_HEIGHT, + NATIVE_WINDOW_TRANSFORM_HINT}; + using QueryOutput = IGraphicBufferProducer::QueryOutput; + std::vector<QueryOutput> outputs; + EXPECT_OK(mProducer->query(inputs, &outputs)); + for (const QueryOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } + // Value was NULL EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/nullptr)); @@ -416,29 +479,113 @@ TEST_P(IGraphicBufferProducerTest, Queue_Succeeds) { // Buffer was not in the dequeued state EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + + { // Test batched methods + constexpr size_t BATCH_SIZE = 4; + + ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE)); + // Dequeue + using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput; + using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput; + DequeueBufferInput dequeueInput; + dequeueInput.width = DEFAULT_WIDTH; + dequeueInput.height = DEFAULT_HEIGHT; + dequeueInput.format = DEFAULT_FORMAT; + dequeueInput.usage = TEST_PRODUCER_USAGE_BITS; + dequeueInput.getTimestamps = false; + std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput); + std::vector<DequeueBufferOutput> dequeueOutputs; + EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs)); + ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size()); + + // Request + std::vector<int32_t> requestInputs; + requestInputs.reserve(BATCH_SIZE); + for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) { + ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + dequeueOutput.result); + requestInputs.emplace_back(dequeueOutput.slot); + } + using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput; + std::vector<RequestBufferOutput> requestOutputs; + EXPECT_OK(mProducer->requestBuffers(requestInputs, &requestOutputs)); + ASSERT_EQ(requestInputs.size(), requestOutputs.size()); + for (const RequestBufferOutput& requestOutput : requestOutputs) { + EXPECT_OK(requestOutput.result); + } + + // Queue + using QueueBufferInput = IGraphicBufferProducer::QueueBufferInput; + using QueueBufferOutput = IGraphicBufferProducer::QueueBufferOutput; + std::vector<QueueBufferInput> queueInputs; + queueInputs.reserve(BATCH_SIZE); + for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) { + queueInputs.emplace_back(CreateBufferInput()).slot = + dequeueOutput.slot; + } + std::vector<QueueBufferOutput> queueOutputs; + EXPECT_OK(mProducer->queueBuffers(queueInputs, &queueOutputs)); + ASSERT_EQ(queueInputs.size(), queueOutputs.size()); + for (const QueueBufferOutput& queueOutput : queueOutputs) { + EXPECT_OK(queueOutput.result); + } + + // Re-queue + EXPECT_OK(mProducer->queueBuffers(queueInputs, &queueOutputs)); + ASSERT_EQ(queueInputs.size(), queueOutputs.size()); + for (const QueueBufferOutput& queueOutput : queueOutputs) { + EXPECT_EQ(BAD_VALUE, queueOutput.result); + } + } } TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + using QueueBufferInput = IGraphicBufferProducer::QueueBufferInput; + using QueueBufferOutput = IGraphicBufferProducer::QueueBufferOutput; // Invalid slot number { // A generic "valid" input - IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); - IGraphicBufferProducer::QueueBufferOutput output; + QueueBufferInput input = CreateBufferInput(); + QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/-1, input, &output)); EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0xDEADBEEF, input, &output)); EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(BufferQueue::NUM_BUFFER_SLOTS, input, &output)); + + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = -1; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } } // Slot was not in the dequeued state (all slots start out in Free state) { - IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); - IGraphicBufferProducer::QueueBufferOutput output; + QueueBufferInput input = CreateBufferInput(); + QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0, input, &output)); + + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = 0; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } } // Put the slot into the "dequeued" state for the rest of the test @@ -453,10 +600,22 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { // Slot was enqueued without requesting a buffer { - IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); - IGraphicBufferProducer::QueueBufferOutput output; + QueueBufferInput input = CreateBufferInput(); + QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = dequeuedSlot; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } } // Request the buffer so that the rest of the tests don't fail on earlier checks. @@ -467,11 +626,23 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { { sp<Fence> nullFence = nullptr; - IGraphicBufferProducer::QueueBufferInput input = + QueueBufferInput input = QueueBufferInputBuilder().setFence(nullFence).build(); - IGraphicBufferProducer::QueueBufferOutput output; + QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = dequeuedSlot; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } } // Scaling mode was unknown @@ -482,9 +653,33 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = dequeuedSlot; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } + input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build(); EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = dequeuedSlot; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } } // Crop rect is out of bounds of the buffer dimensions @@ -495,6 +690,18 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = dequeuedSlot; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } } // Abandon the buffer queue so that the last test fails @@ -507,6 +714,18 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { // TODO(b/73267953): Make BufferHub honor producer and consumer connection. EXPECT_EQ(NO_INIT, mProducer->queueBuffer(dequeuedSlot, input, &output)); + + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = dequeuedSlot; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(NO_INIT, output.result); + } + } } } @@ -525,6 +744,44 @@ TEST_P(IGraphicBufferProducerTest, CancelBuffer_DoesntCrash) { // No return code, but at least test that it doesn't blow up... // TODO: add a return code mProducer->cancelBuffer(dequeuedSlot, dequeuedFence); + + { // Test batched methods + constexpr size_t BATCH_SIZE = 4; + ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE)); + + // Dequeue + using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput; + using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput; + DequeueBufferInput dequeueInput; + dequeueInput.width = DEFAULT_WIDTH; + dequeueInput.height = DEFAULT_HEIGHT; + dequeueInput.format = DEFAULT_FORMAT; + dequeueInput.usage = TEST_PRODUCER_USAGE_BITS; + dequeueInput.getTimestamps = false; + std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput); + std::vector<DequeueBufferOutput> dequeueOutputs; + EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs)); + ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size()); + + // Cancel + using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput; + std::vector<CancelBufferInput> cancelInputs; + cancelInputs.reserve(BATCH_SIZE); + for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) { + ASSERT_EQ(OK, + ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + dequeueOutput.result); + CancelBufferInput& cancelInput = cancelInputs.emplace_back(); + cancelInput.slot = dequeueOutput.slot; + cancelInput.fence = dequeueOutput.fence; + } + std::vector<status_t> cancelOutputs; + EXPECT_OK(mProducer->cancelBuffers(cancelInputs, &cancelOutputs)); + ASSERT_EQ(cancelInputs.size(), cancelOutputs.size()); + for (status_t result : cancelOutputs) { + EXPECT_OK(result); + } + } } TEST_P(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) { @@ -541,11 +798,11 @@ TEST_P(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) { << "bufferCount: " << minBuffers; // Should now be able to dequeue up to minBuffers times - DequeueBufferResult result; + IGraphicBufferProducer::DequeueBufferOutput result; for (int i = 0; i < minBuffers; ++i) { EXPECT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS, &result))) + TEST_PRODUCER_USAGE_BITS, &result))) << "iteration: " << i << ", slot: " << result.slot; } @@ -558,7 +815,6 @@ TEST_P(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) { ASSERT_OK(mProducer->requestBuffer(result.slot, &buffer)); ASSERT_OK(mProducer->queueBuffer(result.slot, input, &output)); - // Should now be able to dequeue up to maxBuffers times int dequeuedSlot = -1; sp<Fence> dequeuedFence; @@ -794,6 +1050,71 @@ TEST_P(IGraphicBufferProducerTest, DetachThenAttach_Succeeds) { EXPECT_OK(mProducer->attachBuffer(&slot, buffer)); EXPECT_OK(buffer->initCheck()); + + ASSERT_OK(mProducer->detachBuffer(slot)); + + { // Test batched methods + constexpr size_t BATCH_SIZE = 4; + ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE)); + + // Dequeue + using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput; + using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput; + DequeueBufferInput dequeueInput; + dequeueInput.width = DEFAULT_WIDTH; + dequeueInput.height = DEFAULT_HEIGHT; + dequeueInput.format = DEFAULT_FORMAT; + dequeueInput.usage = TEST_PRODUCER_USAGE_BITS; + dequeueInput.getTimestamps = false; + std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput); + std::vector<DequeueBufferOutput> dequeueOutputs; + EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs)); + ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size()); + + // Request + std::vector<int32_t> requestInputs; + requestInputs.reserve(BATCH_SIZE); + for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) { + ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + dequeueOutput.result); + requestInputs.emplace_back(dequeueOutput.slot); + } + using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput; + std::vector<RequestBufferOutput> requestOutputs; + EXPECT_OK(mProducer->requestBuffers(requestInputs, &requestOutputs)); + ASSERT_EQ(requestInputs.size(), requestOutputs.size()); + for (const RequestBufferOutput& requestOutput : requestOutputs) { + EXPECT_OK(requestOutput.result); + } + + // Detach + std::vector<int32_t> detachInputs; + detachInputs.reserve(BATCH_SIZE); + for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) { + detachInputs.emplace_back(dequeueOutput.slot); + } + std::vector<status_t> detachOutputs; + EXPECT_OK(mProducer->detachBuffers(detachInputs, &detachOutputs)); + ASSERT_EQ(detachInputs.size(), detachOutputs.size()); + for (status_t result : detachOutputs) { + EXPECT_OK(result); + } + + // Attach + using AttachBufferOutput = IGraphicBufferProducer::AttachBufferOutput; + std::vector<sp<GraphicBuffer>> attachInputs; + attachInputs.reserve(BATCH_SIZE); + for (const RequestBufferOutput& requestOutput : requestOutputs) { + attachInputs.emplace_back(requestOutput.buffer); + } + std::vector<AttachBufferOutput> attachOutputs; + EXPECT_OK(mProducer->attachBuffers(attachInputs, &attachOutputs)); + ASSERT_EQ(attachInputs.size(), attachOutputs.size()); + for (const AttachBufferOutput& attachOutput : attachOutputs) { + EXPECT_OK(attachOutput.result); + EXPECT_NE(-1, attachOutput.slot); + } + } } #if USE_BUFFER_HUB_AS_BUFFER_QUEUE diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 592913c46b..43909ac00d 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -28,6 +28,7 @@ #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> +#include <gui/SyncScreenCaptureListener.h> #include <inttypes.h> #include <private/gui/ComposerService.h> #include <ui/BufferQueueDefs.h> @@ -197,6 +198,20 @@ protected: ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); } + static status_t captureDisplay(DisplayCaptureArgs& captureArgs, + ScreenCaptureResults& captureResults) { + const auto sf = ComposerService::getComposerService(); + SurfaceComposerClient::Transaction().apply(true); + + const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener(); + status_t status = sf->captureDisplay(captureArgs, captureListener); + if (status != NO_ERROR) { + return status; + } + captureResults = captureListener->waitForResults(); + return captureResults.result; + } + sp<Surface> mSurface; sp<SurfaceComposerClient> mComposerClient; sp<SurfaceControl> mSurfaceControl; @@ -244,11 +259,13 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersDontSucceed) { const sp<IBinder> display = sf->getInternalDisplayToken(); ASSERT_FALSE(display == nullptr); - sp<GraphicBuffer> outBuffer; - bool ignored; - ASSERT_EQ(NO_ERROR, - sf->captureScreen(display, &outBuffer, ignored, ui::Dataspace::V0_SRGB, - ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false)); + DisplayCaptureArgs captureArgs; + captureArgs.displayToken = display; + captureArgs.width = 64; + captureArgs.height = 64; + + ScreenCaptureResults captureResults; + ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults)); ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU)); @@ -278,9 +295,7 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersDontSucceed) { &buf)); ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1)); } - ASSERT_EQ(NO_ERROR, - sf->captureScreen(display, &outBuffer, ignored, ui::Dataspace::V0_SRGB, - ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false)); + ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults)); } TEST_F(SurfaceTest, ConcreteTypeIsSurface) { @@ -662,8 +677,7 @@ public: NewFrameEventsEntry mNewFrameEntryOverride = { 0, 0, 0, nullptr }; }; - -class FakeSurfaceComposer : public ISurfaceComposer{ +class FakeSurfaceComposer : public ISurfaceComposer { public: ~FakeSurfaceComposer() override {} @@ -673,7 +687,7 @@ public: sp<ISurfaceComposerClient> createConnection() override { return nullptr; } sp<IDisplayEventConnection> createDisplayEventConnection( - ISurfaceComposer::VsyncSource, ISurfaceComposer::ConfigChanged) override { + ISurfaceComposer::VsyncSource, ISurfaceComposer::EventRegistrationFlags) override { return nullptr; } sp<IBinder> createDisplay(const String8& /*displayName*/, @@ -681,13 +695,17 @@ public: void destroyDisplay(const sp<IBinder>& /*display */) override {} std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override { return {}; } sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId) const override { return nullptr; } - void setTransactionState(const Vector<ComposerState>& /*state*/, - const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/, - const sp<IBinder>& /*applyToken*/, - const InputWindowCommands& /*inputWindowCommands*/, - int64_t /*desiredPresentTime*/, const client_cache_t& /*cachedBuffer*/, - bool /*hasListenerCallbacks*/, - const std::vector<ListenerCallbacks>& /*listenerCallbacks*/) override { + status_t setTransactionState(const FrameTimelineInfo& /*frameTimelineInfo*/, + const Vector<ComposerState>& /*state*/, + const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/, + const sp<IBinder>& /*applyToken*/, + const InputWindowCommands& /*inputWindowCommands*/, + int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/, + const client_cache_t& /*cachedBuffer*/, + bool /*hasListenerCallbacks*/, + const std::vector<ListenerCallbacks>& /*listenerCallbacks*/, + uint64_t /*transactionId*/) override { + return NO_ERROR; } void bootFinished() override {} @@ -719,7 +737,7 @@ public: status_t getDisplayInfo(const sp<IBinder>& /*display*/, DisplayInfo*) override { return NO_ERROR; } - status_t getDisplayConfigs(const sp<IBinder>& /*display*/, Vector<DisplayConfig>*) override { + status_t getDisplayModes(const sp<IBinder>& /*display*/, Vector<ui::DisplayMode>*) override { return NO_ERROR; } status_t getDisplayState(const sp<IBinder>& /*display*/, ui::DisplayState*) override { @@ -727,7 +745,7 @@ public: } status_t getDisplayStats(const sp<IBinder>& /*display*/, DisplayStatInfo* /*stats*/) override { return NO_ERROR; } - int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; } + int getActiveDisplayModeId(const sp<IBinder>& /*display*/) override { return 0; } status_t getDisplayColorModes(const sp<IBinder>& /*display*/, Vector<ColorMode>* /*outColorModes*/) override { return NO_ERROR; @@ -742,12 +760,8 @@ public: } status_t setActiveColorMode(const sp<IBinder>& /*display*/, ColorMode /*colorMode*/) override { return NO_ERROR; } - status_t captureScreen(const sp<IBinder>& /*display*/, sp<GraphicBuffer>* /*outBuffer*/, - bool& /*outCapturedSecureLayers*/, ui::Dataspace /*reqDataspace*/, - ui::PixelFormat /*reqPixelFormat*/, const Rect& /*sourceCrop*/, - uint32_t /*reqWidth*/, uint32_t /*reqHeight*/, - bool /*useIdentityTransform*/, ui::Rotation, - bool /*captureSecureLayers*/) override { + status_t captureDisplay(const DisplayCaptureArgs& /* captureArgs */, + const sp<IScreenCaptureListener>& /* captureListener */) override { return NO_ERROR; } status_t getAutoLowLatencyModeSupport(const sp<IBinder>& /*display*/, @@ -760,17 +774,13 @@ public: return NO_ERROR; } void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {} - status_t captureScreen(uint64_t /*displayOrLayerStack*/, ui::Dataspace* /*outDataspace*/, - sp<GraphicBuffer>* /*outBuffer*/) override { + status_t captureDisplay(uint64_t /*displayOrLayerStack*/, + const sp<IScreenCaptureListener>& /* captureListener */) override { return NO_ERROR; } virtual status_t captureLayers( - const sp<IBinder>& /*parentHandle*/, sp<GraphicBuffer>* /*outBuffer*/, - ui::Dataspace /*reqDataspace*/, ui::PixelFormat /*reqPixelFormat*/, - const Rect& /*sourceCrop*/, - const std::unordered_set<sp<IBinder>, - ISurfaceComposer::SpHash<IBinder>>& /*excludeHandles*/, - float /*frameScale*/, bool /*childrenOnly*/) override { + const LayerCaptureArgs& /* captureArgs */, + const sp<IScreenCaptureListener>& /* captureListener */) override { return NO_ERROR; } status_t clearAnimationFrameStats() override { return NO_ERROR; } @@ -833,23 +843,24 @@ public: const sp<IRegionSamplingListener>& /*listener*/) override { return NO_ERROR; } - status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/, - int32_t /*defaultConfig*/, - float /*primaryRefreshRateMin*/, - float /*primaryRefreshRateMax*/, - float /*appRequestRefreshRateMin*/, - float /*appRequestRefreshRateMax*/) { + status_t setDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/, size_t /*defaultMode*/, + bool /*allowGroupSwitching*/, + float /*primaryRefreshRateMin*/, + float /*primaryRefreshRateMax*/, + float /*appRequestRefreshRateMin*/, + float /*appRequestRefreshRateMax*/) { return NO_ERROR; } - status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/, - int32_t* /*outDefaultConfig*/, - float* /*outPrimaryRefreshRateMin*/, - float* /*outPrimaryRefreshRateMax*/, - float* /*outAppRequestRefreshRateMin*/, - float* /*outAppRequestRefreshRateMax*/) override { + status_t getDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/, + size_t* /*outDefaultMode*/, + bool* /*outAllowGroupSwitching*/, + float* /*outPrimaryRefreshRateMin*/, + float* /*outPrimaryRefreshRateMax*/, + float* /*outAppRequestRefreshRateMin*/, + float* /*outAppRequestRefreshRateMax*/) override { return NO_ERROR; }; - status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; } + status_t notifyPowerBoost(int32_t /*boostId*/) override { return NO_ERROR; } status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/, float /*lightPosY*/, float /*lightPosZ*/, @@ -858,11 +869,27 @@ public: } status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/, - int8_t /*compatibility*/) override { + int8_t /*compatibility*/, bool /*shouldBeSeamless*/) override { return NO_ERROR; } - status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) { return NO_ERROR; } + status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) override { + return NO_ERROR; + } + + status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& /*surface*/, + const FrameTimelineInfo& /*frameTimelineInfo*/) override { + return NO_ERROR; + } + + status_t addTransactionTraceListener( + const sp<gui::ITransactionTraceListener>& /*listener*/) override { + return NO_ERROR; + } + + int getGPUContextPriority() override { return 0; }; + + status_t getExtraBufferCount(int* /*extraBuffers*/) const override { return NO_ERROR; } protected: IBinder* onAsBinder() override { return nullptr; } @@ -1999,4 +2026,86 @@ TEST_F(SurfaceTest, DefaultMaxBufferCountSetAndUpdated) { EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count); } +TEST_F(SurfaceTest, BatchOperations) { + const int BUFFER_COUNT = 16; + const int BATCH_SIZE = 8; + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1); + sp<Surface> surface = new Surface(producer); + sp<ANativeWindow> window(surface); + sp<StubProducerListener> listener = new StubProducerListener(); + + ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener, + /*reportBufferRemoval*/false)); + + ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); + + std::vector<Surface::BatchBuffer> buffers(BATCH_SIZE); + + // Batch dequeued buffers can be queued individually + ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); + for (size_t i = 0; i < BATCH_SIZE; i++) { + ANativeWindowBuffer* buffer = buffers[i].buffer; + int fence = buffers[i].fenceFd; + ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence)); + } + + // Batch dequeued buffers can be canceled individually + ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); + for (size_t i = 0; i < BATCH_SIZE; i++) { + ANativeWindowBuffer* buffer = buffers[i].buffer; + int fence = buffers[i].fenceFd; + ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); + } + + // Batch dequeued buffers can be batch cancelled + ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); + ASSERT_EQ(NO_ERROR, surface->cancelBuffers(buffers)); + + // Batch dequeued buffers can be batch queued + ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); + std::vector<Surface::BatchQueuedBuffer> queuedBuffers(BATCH_SIZE); + for (size_t i = 0; i < BATCH_SIZE; i++) { + queuedBuffers[i].buffer = buffers[i].buffer; + queuedBuffers[i].fenceFd = buffers[i].fenceFd; + queuedBuffers[i].timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; + } + ASSERT_EQ(NO_ERROR, surface->queueBuffers(queuedBuffers)); + + ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); +} + +TEST_F(SurfaceTest, BatchIllegalOperations) { + const int BUFFER_COUNT = 16; + const int BATCH_SIZE = 8; + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1); + sp<Surface> surface = new Surface(producer); + sp<ANativeWindow> window(surface); + sp<StubProducerListener> listener = new StubProducerListener(); + + ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener, + /*reportBufferRemoval*/false)); + + ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); + + std::vector<Surface::BatchBuffer> buffers(BATCH_SIZE); + std::vector<Surface::BatchQueuedBuffer> queuedBuffers(BATCH_SIZE); + + // Batch operations are invalid in shared buffer mode + surface->setSharedBufferMode(true); + ASSERT_EQ(INVALID_OPERATION, surface->dequeueBuffers(&buffers)); + ASSERT_EQ(INVALID_OPERATION, surface->cancelBuffers(buffers)); + ASSERT_EQ(INVALID_OPERATION, surface->queueBuffers(queuedBuffers)); + surface->setSharedBufferMode(false); + + ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); +} + } // namespace android diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp index d98ffc6618..1bfe4624f1 100644 --- a/libs/gui/view/Surface.cpp +++ b/libs/gui/view/Surface.cpp @@ -45,7 +45,9 @@ status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const { if (res != OK) return res; } - return IGraphicBufferProducer::exportToParcel(graphicBufferProducer, parcel); + res = IGraphicBufferProducer::exportToParcel(graphicBufferProducer, parcel); + if (res != OK) return res; + return parcel->writeStrongBinder(surfaceControlHandle); } status_t Surface::readFromParcel(const Parcel* parcel) { @@ -68,6 +70,7 @@ status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) { } graphicBufferProducer = IGraphicBufferProducer::createFromParcel(parcel); + surfaceControlHandle = parcel->readStrongBinder(); return OK; } diff --git a/libs/incidentcompanion/Android.bp b/libs/incidentcompanion/Android.bp index ef7f52318b..63411b9698 100644 --- a/libs/incidentcompanion/Android.bp +++ b/libs/incidentcompanion/Android.bp @@ -14,15 +14,6 @@ * limitations under the License. */ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - filegroup { name: "incidentcompanion_aidl", srcs: [ @@ -58,3 +49,4 @@ cc_library_static { "-Wunused-parameter", ], } + diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 393c0f6c15..fce3000a0f 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -14,13 +14,15 @@ // libinput is partially built for the host (used by build time keymap validation tool) -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], +filegroup { + name: "inputconstants_aidl", + srcs: [ + "android/os/BlockUntrustedTouchesMode.aidl", + "android/os/IInputConstants.aidl", + "android/os/InputEventInjectionResult.aidl", + "android/os/InputEventInjectionSync.aidl", + "android/os/TouchOcclusionMode.aidl", + ], } cc_library { @@ -34,11 +36,15 @@ cc_library { srcs: [ "Input.cpp", "InputDevice.cpp", + "InputEventLabels.cpp", "Keyboard.cpp", "KeyCharacterMap.cpp", "KeyLayoutMap.cpp", + "LatencyStatistics.cpp", "PropertyMap.cpp", "TouchVideoFrame.cpp", + "VelocityControl.cpp", + "VelocityTracker.cpp", "VirtualKeyMap.cpp", ], @@ -53,19 +59,32 @@ cc_library { "libcutils", ], + static_libs: [ + "libui-types", + ], + + export_static_lib_headers: [ + "libui-types", + ], + target: { android: { srcs: [ - "IInputFlinger.cpp", - "InputApplication.cpp", "InputTransport.cpp", "InputWindow.cpp", - "ISetInputWindowsListener.cpp", - "LatencyStatistics.cpp", - "VelocityControl.cpp", - "VelocityTracker.cpp", + "android/FocusRequest.aidl", + "android/InputApplicationInfo.aidl", + "android/os/IInputConstants.aidl", + "android/os/InputEventInjectionResult.aidl", + "android/os/InputEventInjectionSync.aidl", + "android/os/TouchOcclusionMode.aidl", + "android/os/BlockUntrustedTouchesMode.aidl", + "android/os/IInputFlinger.aidl", + "android/os/ISetInputWindowsListener.aidl", ], + export_shared_lib_headers: ["libbinder"], + shared_libs: [ "libutils", "libbinder", @@ -80,7 +99,33 @@ cc_library { shared: { enabled: false, }, + include_dirs: [ + "frameworks/native/libs/arect/include", + ], }, + linux_glibc: { + srcs: [ + "InputTransport.cpp", + "InputWindow.cpp", + "android/FocusRequest.aidl", + "android/InputApplicationInfo.aidl", + "android/os/IInputConstants.aidl", + "android/os/IInputFlinger.aidl", + "android/os/ISetInputWindowsListener.aidl", + "android/os/TouchOcclusionMode.aidl", + ], + static_libs: [ + "libhostgraphics", + ], + shared_libs: [ + "libbinder", + ], + }, + }, + + aidl: { + local_include_dirs: ["."], + export_aidl_headers: true, }, } diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp deleted file mode 100644 index 8ec51653a8..0000000000 --- a/libs/input/IInputFlinger.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdint.h> -#include <sys/types.h> - -#include <binder/Parcel.h> -#include <binder/IPCThreadState.h> -#include <binder/IServiceManager.h> - -#include <input/IInputFlinger.h> - -namespace android { - -class BpInputFlinger : public BpInterface<IInputFlinger> { -public: - explicit BpInputFlinger(const sp<IBinder>& impl) : - BpInterface<IInputFlinger>(impl) { } - - virtual void setInputWindows(const std::vector<InputWindowInfo>& inputInfo, - const sp<ISetInputWindowsListener>& setInputWindowsListener) { - Parcel data, reply; - data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); - - data.writeUint32(static_cast<uint32_t>(inputInfo.size())); - for (const auto& info : inputInfo) { - info.write(data); - } - data.writeStrongBinder(IInterface::asBinder(setInputWindowsListener)); - - remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply, - IBinder::FLAG_ONEWAY); - } - - virtual void registerInputChannel(const sp<InputChannel>& channel) { - Parcel data, reply; - data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); - channel->write(data); - remote()->transact(BnInputFlinger::REGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply); - } - - virtual void unregisterInputChannel(const sp<InputChannel>& channel) { - Parcel data, reply; - data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); - channel->write(data); - remote()->transact(BnInputFlinger::UNREGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply); - } -}; - -IMPLEMENT_META_INTERFACE(InputFlinger, "android.input.IInputFlinger"); - -status_t BnInputFlinger::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - switch(code) { - case SET_INPUT_WINDOWS_TRANSACTION: { - CHECK_INTERFACE(IInputFlinger, data, reply); - size_t count = data.readUint32(); - if (count > data.dataSize()) { - return BAD_VALUE; - } - std::vector<InputWindowInfo> handles; - for (size_t i = 0; i < count; i++) { - handles.push_back(InputWindowInfo::read(data)); - } - const sp<ISetInputWindowsListener> setInputWindowsListener = - ISetInputWindowsListener::asInterface(data.readStrongBinder()); - setInputWindows(handles, setInputWindowsListener); - break; - } - case REGISTER_INPUT_CHANNEL_TRANSACTION: { - CHECK_INTERFACE(IInputFlinger, data, reply); - sp<InputChannel> channel = InputChannel::read(data); - registerInputChannel(channel); - break; - } - case UNREGISTER_INPUT_CHANNEL_TRANSACTION: { - CHECK_INTERFACE(IInputFlinger, data, reply); - sp<InputChannel> channel = InputChannel::read(data); - unregisterInputChannel(channel); - break; - } - default: - return BBinder::onTransact(code, data, reply, flags); - } - return NO_ERROR; -} - -}; diff --git a/libs/input/ISetInputWindowsListener.cpp b/libs/input/ISetInputWindowsListener.cpp deleted file mode 100644 index a0330da89e..0000000000 --- a/libs/input/ISetInputWindowsListener.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <input/ISetInputWindowsListener.h> - -namespace android { - -class BpSetInputWindowsListener : public BpInterface<ISetInputWindowsListener> { -public: - explicit BpSetInputWindowsListener(const sp<IBinder>& impl) - : BpInterface<ISetInputWindowsListener>(impl) { - } - - virtual ~BpSetInputWindowsListener() = default; - - virtual void onSetInputWindowsFinished() { - Parcel data, reply; - data.writeInterfaceToken(ISetInputWindowsListener::getInterfaceDescriptor()); - remote()->transact(BnSetInputWindowsListener::ON_SET_INPUT_WINDOWS_FINISHED, data, &reply, - IBinder::FLAG_ONEWAY); - } -}; - -IMPLEMENT_META_INTERFACE(SetInputWindowsListener, "android.input.ISetInputWindowsListener"); - -status_t BnSetInputWindowsListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags) { - switch(code) { - case ON_SET_INPUT_WINDOWS_FINISHED: { - CHECK_INTERFACE(ISetInputWindowsListener, data, reply); - onSetInputWindowsFinished(); - return NO_ERROR; - } - default: { - return BBinder::onTransact(code, data, reply, flags); - } - } -} - -} // namespace android diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 31aa685391..0a00d68556 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -17,19 +17,26 @@ #define LOG_TAG "Input" //#define LOG_NDEBUG 0 +#include <attestation/HmacKeyManager.h> #include <cutils/compiler.h> +#include <inttypes.h> #include <limits.h> #include <string.h> +#include <android-base/stringprintf.h> #include <input/Input.h> #include <input/InputDevice.h> #include <input/InputEventLabels.h> -#ifdef __ANDROID__ +#ifdef __linux__ #include <binder/Parcel.h> +#endif +#ifdef __ANDROID__ #include <sys/random.h> #endif +using android::base::StringPrintf; + namespace android { const char* motionClassificationToString(MotionClassification classification) { @@ -82,6 +89,9 @@ const char* inputEventTypeToString(int32_t type) { case AINPUT_EVENT_TYPE_FOCUS: { return "FOCUS"; } + case AINPUT_EVENT_TYPE_CAPTURE: { + return "CAPTURE"; + } } return "UNKNOWN"; } @@ -135,11 +145,11 @@ int32_t InputEvent::nextId() { // --- KeyEvent --- const char* KeyEvent::getLabel(int32_t keyCode) { - return getLabelByKeyCode(keyCode); + return InputEventLookup::getLabelByKeyCode(keyCode); } int32_t KeyEvent::getKeyCodeFromLabel(const char* label) { - return getKeyCodeByLabel(label); + return InputEventLookup::getKeyCodeByLabel(label); } void KeyEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, @@ -249,7 +259,7 @@ void PointerCoords::applyOffset(float xOffset, float yOffset) { setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset); } -#ifdef __ANDROID__ +#ifdef __linux__ status_t PointerCoords::readFromParcel(Parcel* parcel) { bits = parcel->readInt64(); @@ -301,6 +311,11 @@ void PointerCoords::copyFrom(const PointerCoords& other) { } } +void PointerCoords::transform(const ui::Transform& transform) { + vec2 newCoords = transform.transform(getX(), getY()); + setAxisValue(AMOTION_EVENT_AXIS_X, newCoords.x); + setAxisValue(AMOTION_EVENT_AXIS_Y, newCoords.y); +} // --- PointerProperties --- @@ -320,10 +335,10 @@ void PointerProperties::copyFrom(const PointerProperties& other) { void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, - int32_t buttonState, MotionClassification classification, float xScale, - float yScale, float xOffset, float yOffset, float xPrecision, - float yPrecision, float rawXCursorPosition, float rawYCursorPosition, - nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, + int32_t buttonState, MotionClassification classification, + const ui::Transform& transform, float xPrecision, float yPrecision, + float rawXCursorPosition, float rawYCursorPosition, nsecs_t downTime, + nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { InputEvent::initialize(id, deviceId, source, displayId, hmac); @@ -334,10 +349,7 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int3 mMetaState = metaState; mButtonState = buttonState; mClassification = classification; - mXScale = xScale; - mYScale = yScale; - mXOffset = xOffset; - mYOffset = yOffset; + mTransform = transform; mXPrecision = xPrecision; mYPrecision = yPrecision; mRawXCursorPosition = rawXCursorPosition; @@ -360,10 +372,7 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mMetaState = other->mMetaState; mButtonState = other->mButtonState; mClassification = other->mClassification; - mXScale = other->mXScale; - mYScale = other->mYScale; - mXOffset = other->mXOffset; - mYOffset = other->mYOffset; + mTransform = other->mTransform; mXPrecision = other->mXPrecision; mYPrecision = other->mYPrecision; mRawXCursorPosition = other->mRawXCursorPosition; @@ -376,7 +385,7 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mSamplePointerCoords = other->mSamplePointerCoords; } else { mSampleEventTimes.clear(); - mSampleEventTimes.push(other->getEventTime()); + mSampleEventTimes.push_back(other->getEventTime()); mSamplePointerCoords.clear(); size_t pointerCount = other->getPointerCount(); size_t historySize = other->getHistorySize(); @@ -388,23 +397,25 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { void MotionEvent::addSample( int64_t eventTime, const PointerCoords* pointerCoords) { - mSampleEventTimes.push(eventTime); + mSampleEventTimes.push_back(eventTime); mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); } float MotionEvent::getXCursorPosition() const { - const float rawX = getRawXCursorPosition(); - return rawX * mXScale + mXOffset; + vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition()); + return vals.x; } float MotionEvent::getYCursorPosition() const { - const float rawY = getRawYCursorPosition(); - return rawY * mYScale + mYOffset; + vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition()); + return vals.y; } void MotionEvent::setCursorPosition(float x, float y) { - mRawXCursorPosition = (x - mXOffset) / mXScale; - mRawYCursorPosition = (y - mYOffset) / mYScale; + ui::Transform inverse = mTransform.inverse(); + vec2 vals = inverse.transform(x, y); + mRawXCursorPosition = vals.x; + mRawYCursorPosition = vals.y; } const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { @@ -416,14 +427,7 @@ float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const { } float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { - float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis); - switch (axis) { - case AMOTION_EVENT_AXIS_X: - return value * mXScale + mXOffset; - case AMOTION_EVENT_AXIS_Y: - return value * mYScale + mYOffset; - } - return value; + return getHistoricalAxisValue(axis, pointerIndex, getHistorySize()); } const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( @@ -438,14 +442,23 @@ float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const { - float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); + if (axis != AMOTION_EVENT_AXIS_X && axis != AMOTION_EVENT_AXIS_Y) { + return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); + } + + float rawX = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getX(); + float rawY = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getY(); + vec2 vals = mTransform.transform(rawX, rawY); + switch (axis) { case AMOTION_EVENT_AXIS_X: - return value * mXScale + mXOffset; + return vals.x; case AMOTION_EVENT_AXIS_Y: - return value * mYScale + mYOffset; + return vals.y; } - return value; + + // This should never happen + return 0; } ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const { @@ -459,23 +472,24 @@ ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const { } void MotionEvent::offsetLocation(float xOffset, float yOffset) { - mXOffset += xOffset; - mYOffset += yOffset; + float currXOffset = mTransform.tx(); + float currYOffset = mTransform.ty(); + mTransform.set(currXOffset + xOffset, currYOffset + yOffset); } void MotionEvent::scale(float globalScaleFactor) { - mXOffset *= globalScaleFactor; - mYOffset *= globalScaleFactor; + mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor); mXPrecision *= globalScaleFactor; mYPrecision *= globalScaleFactor; size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { - mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor); + mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor, globalScaleFactor, + globalScaleFactor); } } -static void transformPoint(const float matrix[9], float x, float y, float *outX, float *outY) { +static vec2 transformPoint(const std::array<float, 9>& matrix, float x, float y) { // Apply perspective transform like Skia. float newX = matrix[0] * x + matrix[1] * y + matrix[2]; float newY = matrix[3] * x + matrix[4] * y + matrix[5]; @@ -483,22 +497,25 @@ static void transformPoint(const float matrix[9], float x, float y, float *outX, if (newZ) { newZ = 1.0f / newZ; } - *outX = newX * newZ; - *outY = newY * newZ; + vec2 transformedPoint; + transformedPoint.x = newX * newZ; + transformedPoint.y = newY * newZ; + return transformedPoint; } -static float transformAngle(const float matrix[9], float angleRadians, - float originX, float originY) { +static float transformAngle(const std::array<float, 9>& matrix, float angleRadians, float originX, + float originY) { // Construct and transform a vector oriented at the specified clockwise angle from vertical. // Coordinate system: down is increasing Y, right is increasing X. float x = sinf(angleRadians); float y = -cosf(angleRadians); - transformPoint(matrix, x, y, &x, &y); - x -= originX; - y -= originY; + vec2 transformedPoint = transformPoint(matrix, x, y); + + transformedPoint.x -= originX; + transformedPoint.y -= originY; // Derive the transformed vector's clockwise angle from vertical. - float result = atan2f(x, -y); + float result = atan2f(transformedPoint.x, -transformedPoint.y); if (result < - M_PI_2) { result += M_PI; } else if (result > M_PI_2) { @@ -507,51 +524,51 @@ static float transformAngle(const float matrix[9], float angleRadians, return result; } -void MotionEvent::transform(const float matrix[9]) { - // The tricky part of this implementation is to preserve the value of - // rawX and rawY. So we apply the transformation to the first point - // then derive an appropriate new X/Y offset that will preserve rawX - // and rawY for that point. - float oldXOffset = mXOffset; - float oldYOffset = mYOffset; - float newX, newY; - float scaledRawX = getRawX(0) * mXScale; - float scaledRawY = getRawY(0) * mYScale; - transformPoint(matrix, scaledRawX + oldXOffset, scaledRawY + oldYOffset, &newX, &newY); - mXOffset = newX - scaledRawX; - mYOffset = newY - scaledRawY; +void MotionEvent::transform(const std::array<float, 9>& matrix) { + // We want to preserve the rawX and rawY so we just update the transform + // using the values of the transform passed in + ui::Transform newTransform; + newTransform.set(matrix); + mTransform = newTransform * mTransform; // Determine how the origin is transformed by the matrix so that we // can transform orientation vectors. - float originX, originY; - transformPoint(matrix, 0, 0, &originX, &originY); - - // Apply the transformation to cursor position. - if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) { - float x = mRawXCursorPosition * mXScale + oldXOffset; - float y = mRawYCursorPosition * mYScale + oldYOffset; - transformPoint(matrix, x, y, &x, &y); - mRawXCursorPosition = (x - mXOffset) / mXScale; - mRawYCursorPosition = (y - mYOffset) / mYScale; - } + vec2 origin = transformPoint(matrix, 0, 0); // Apply the transformation to all samples. size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { PointerCoords& c = mSamplePointerCoords.editItemAt(i); - float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) * mXScale + oldXOffset; - float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) * mYScale + oldYOffset; - transformPoint(matrix, x, y, &x, &y); - c.setAxisValue(AMOTION_EVENT_AXIS_X, (x - mXOffset) / mXScale); - c.setAxisValue(AMOTION_EVENT_AXIS_Y, (y - mYOffset) / mYScale); - float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, - transformAngle(matrix, orientation, originX, originY)); + transformAngle(matrix, orientation, origin.x, origin.y)); } } -#ifdef __ANDROID__ +#ifdef __linux__ +static status_t readFromParcel(ui::Transform& transform, const Parcel& parcel) { + float dsdx, dtdx, tx, dtdy, dsdy, ty; + status_t status = parcel.readFloat(&dsdx); + status |= parcel.readFloat(&dtdx); + status |= parcel.readFloat(&tx); + status |= parcel.readFloat(&dtdy); + status |= parcel.readFloat(&dsdy); + status |= parcel.readFloat(&ty); + + transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1}); + return status; +} + +static status_t writeToParcel(const ui::Transform& transform, Parcel& parcel) { + status_t status = parcel.writeFloat(transform.dsdx()); + status |= parcel.writeFloat(transform.dtdx()); + status |= parcel.writeFloat(transform.tx()); + status |= parcel.writeFloat(transform.dtdy()); + status |= parcel.writeFloat(transform.dsdy()); + status |= parcel.writeFloat(transform.ty()); + return status; +} + status_t MotionEvent::readFromParcel(Parcel* parcel) { size_t pointerCount = parcel->readInt32(); size_t sampleCount = parcel->readInt32(); @@ -577,10 +594,11 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mMetaState = parcel->readInt32(); mButtonState = parcel->readInt32(); mClassification = static_cast<MotionClassification>(parcel->readByte()); - mXScale = parcel->readFloat(); - mYScale = parcel->readFloat(); - mXOffset = parcel->readFloat(); - mYOffset = parcel->readFloat(); + + result = android::readFromParcel(mTransform, *parcel); + if (result != OK) { + return result; + } mXPrecision = parcel->readFloat(); mYPrecision = parcel->readFloat(); mRawXCursorPosition = parcel->readFloat(); @@ -590,7 +608,7 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mPointerProperties.clear(); mPointerProperties.setCapacity(pointerCount); mSampleEventTimes.clear(); - mSampleEventTimes.setCapacity(sampleCount); + mSampleEventTimes.reserve(sampleCount); mSamplePointerCoords.clear(); mSamplePointerCoords.setCapacity(sampleCount * pointerCount); @@ -603,7 +621,7 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { while (sampleCount > 0) { sampleCount--; - mSampleEventTimes.push(parcel->readInt64()); + mSampleEventTimes.push_back(parcel->readInt64()); for (size_t i = 0; i < pointerCount; i++) { mSamplePointerCoords.push(); status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel); @@ -635,10 +653,11 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(mMetaState); parcel->writeInt32(mButtonState); parcel->writeByte(static_cast<int8_t>(mClassification)); - parcel->writeFloat(mXScale); - parcel->writeFloat(mYScale); - parcel->writeFloat(mXOffset); - parcel->writeFloat(mYOffset); + + status_t result = android::writeToParcel(mTransform, *parcel); + if (result != OK) { + return result; + } parcel->writeFloat(mXPrecision); parcel->writeFloat(mYPrecision); parcel->writeFloat(mRawXCursorPosition); @@ -653,7 +672,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { const PointerCoords* pc = mSamplePointerCoords.array(); for (size_t h = 0; h < sampleCount; h++) { - parcel->writeInt64(mSampleEventTimes.itemAt(h)); + parcel->writeInt64(mSampleEventTimes[h]); for (size_t i = 0; i < pointerCount; i++) { status_t status = (pc++)->writeToParcel(parcel); if (status) { @@ -683,30 +702,44 @@ bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) { } const char* MotionEvent::getLabel(int32_t axis) { - return getAxisLabel(axis); + return InputEventLookup::getAxisLabel(axis); } int32_t MotionEvent::getAxisFromLabel(const char* label) { - return getAxisByLabel(label); + return InputEventLookup::getAxisByLabel(label); } -const char* MotionEvent::actionToString(int32_t action) { +std::string MotionEvent::actionToString(int32_t action) { // Convert MotionEvent action to string switch (action & AMOTION_EVENT_ACTION_MASK) { case AMOTION_EVENT_ACTION_DOWN: return "DOWN"; - case AMOTION_EVENT_ACTION_MOVE: - return "MOVE"; case AMOTION_EVENT_ACTION_UP: return "UP"; + case AMOTION_EVENT_ACTION_MOVE: + return "MOVE"; case AMOTION_EVENT_ACTION_CANCEL: return "CANCEL"; + case AMOTION_EVENT_ACTION_OUTSIDE: + return "OUTSIDE"; case AMOTION_EVENT_ACTION_POINTER_DOWN: return "POINTER_DOWN"; case AMOTION_EVENT_ACTION_POINTER_UP: return "POINTER_UP"; - } - return "UNKNOWN"; + case AMOTION_EVENT_ACTION_HOVER_MOVE: + return "HOVER_MOVE"; + case AMOTION_EVENT_ACTION_SCROLL: + return "SCROLL"; + case AMOTION_EVENT_ACTION_HOVER_ENTER: + return "HOVER_ENTER"; + case AMOTION_EVENT_ACTION_HOVER_EXIT: + return "HOVER_EXIT"; + case AMOTION_EVENT_ACTION_BUTTON_PRESS: + return "BUTTON_PRESS"; + case AMOTION_EVENT_ACTION_BUTTON_RELEASE: + return "BUTTON_RELEASE"; + } + return android::base::StringPrintf("%" PRId32, action); } // --- FocusEvent --- @@ -724,6 +757,19 @@ void FocusEvent::initialize(const FocusEvent& from) { mInTouchMode = from.mInTouchMode; } +// --- CaptureEvent --- + +void CaptureEvent::initialize(int32_t id, bool pointerCaptureEnabled) { + InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, + ADISPLAY_ID_NONE, INVALID_HMAC); + mPointerCaptureEnabled = pointerCaptureEnabled; +} + +void CaptureEvent::initialize(const CaptureEvent& from) { + InputEvent::initialize(from); + mPointerCaptureEnabled = from.mPointerCaptureEnabled; +} + // --- PooledInputEventFactory --- PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) : @@ -760,6 +806,15 @@ FocusEvent* PooledInputEventFactory::createFocusEvent() { return event; } +CaptureEvent* PooledInputEventFactory::createCaptureEvent() { + if (mCaptureEventPool.empty()) { + return new CaptureEvent(); + } + CaptureEvent* event = mCaptureEventPool.front().release(); + mCaptureEventPool.pop(); + return event; +} + void PooledInputEventFactory::recycle(InputEvent* event) { switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: @@ -780,6 +835,13 @@ void PooledInputEventFactory::recycle(InputEvent* event) { return; } break; + case AINPUT_EVENT_TYPE_CAPTURE: + if (mCaptureEventPool.size() < mMaxPoolSize) { + mCaptureEventPool.push( + std::unique_ptr<CaptureEvent>(static_cast<CaptureEvent*>(event))); + return; + } + break; } delete event; } diff --git a/libs/input/InputApplication.cpp b/libs/input/InputApplication.cpp deleted file mode 100644 index 1d9f8a7091..0000000000 --- a/libs/input/InputApplication.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "InputApplication" - -#include <input/InputApplication.h> - -#include <android/log.h> - -namespace android { - -// --- InputApplicationHandle --- - -InputApplicationHandle::InputApplicationHandle() { -} - -InputApplicationHandle::~InputApplicationHandle() { -} - -InputApplicationInfo InputApplicationInfo::read(const Parcel& from) { - InputApplicationInfo ret; - ret.token = from.readStrongBinder(); - ret.name = from.readString8().c_str(); - ret.dispatchingTimeout = from.readInt64(); - - return ret; -} - -status_t InputApplicationInfo::write(Parcel& output) const { - output.writeStrongBinder(token); - output.writeString8(String8(name.c_str())); - output.writeInt64(dispatchingTimeout); - - return OK; -} - -} // namespace android diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 4db9e06d8d..698cf6eebc 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -23,6 +23,7 @@ #include <android-base/stringprintf.h> #include <input/InputDevice.h> #include <input/InputEventLabels.h> +#include <input/NamedEnum.h> using android::base::StringPrintf; @@ -46,9 +47,9 @@ static bool isValidNameChar(char ch) { static void appendInputDeviceConfigurationFileRelativePath(std::string& path, const std::string& name, InputDeviceConfigurationFileType type) { - path += CONFIGURATION_FILE_DIR[type]; + path += CONFIGURATION_FILE_DIR[static_cast<int32_t>(type)]; path += name; - path += CONFIGURATION_FILE_EXTENSION[type]; + path += CONFIGURATION_FILE_EXTENSION[static_cast<int32_t>(type)]; } std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( @@ -153,14 +154,23 @@ InputDeviceInfo::InputDeviceInfo() { initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false); } -InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) : - mId(other.mId), mGeneration(other.mGeneration), mControllerNumber(other.mControllerNumber), - mIdentifier(other.mIdentifier), mAlias(other.mAlias), mIsExternal(other.mIsExternal), - mHasMic(other.mHasMic), mSources(other.mSources), - mKeyboardType(other.mKeyboardType), mKeyCharacterMap(other.mKeyCharacterMap), - mHasVibrator(other.mHasVibrator), mHasButtonUnderPad(other.mHasButtonUnderPad), - mMotionRanges(other.mMotionRanges) { -} +InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) + : mId(other.mId), + mGeneration(other.mGeneration), + mControllerNumber(other.mControllerNumber), + mIdentifier(other.mIdentifier), + mAlias(other.mAlias), + mIsExternal(other.mIsExternal), + mHasMic(other.mHasMic), + mSources(other.mSources), + mKeyboardType(other.mKeyboardType), + mKeyCharacterMap(other.mKeyCharacterMap), + mHasVibrator(other.mHasVibrator), + mHasBattery(other.mHasBattery), + mHasButtonUnderPad(other.mHasButtonUnderPad), + mHasSensor(other.mHasSensor), + mMotionRanges(other.mMotionRanges), + mSensors(other.mSensors) {} InputDeviceInfo::~InputDeviceInfo() { } @@ -178,8 +188,11 @@ void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t control mSources = 0; mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE; mHasVibrator = false; + mHasBattery = false; mHasButtonUnderPad = false; + mHasSensor = false; mMotionRanges.clear(); + mSensors.clear(); } const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange( @@ -208,4 +221,28 @@ void InputDeviceInfo::addMotionRange(const MotionRange& range) { mMotionRanges.push_back(range); } +void InputDeviceInfo::addSensorInfo(const InputDeviceSensorInfo& info) { + if (mSensors.find(info.type) != mSensors.end()) { + ALOGW("Sensor type %s already exists, will be replaced by new sensor added.", + NamedEnum::string(info.type).c_str()); + } + mSensors.insert_or_assign(info.type, info); +} + +const std::vector<InputDeviceSensorType> InputDeviceInfo::getSensorTypes() { + std::vector<InputDeviceSensorType> types; + for (const auto& [type, info] : mSensors) { + types.push_back(type); + } + return types; +} + +const InputDeviceSensorInfo* InputDeviceInfo::getSensorInfo(InputDeviceSensorType type) { + auto it = mSensors.find(type); + if (it == mSensors.end()) { + return nullptr; + } + return &it->second; +} + } // namespace android diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp new file mode 100644 index 0000000000..c0aa2e26a2 --- /dev/null +++ b/libs/input/InputEventLabels.cpp @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <input/InputEventLabels.h> + +#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key } +#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis } +#define DEFINE_LED(led) { #led, ALED_##led } +#define DEFINE_FLAG(flag) { #flag, POLICY_FLAG_##flag } + +namespace android { + +// NOTE: If you add a new keycode here you must also add it to several other files. +// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. +#define KEYCODES_SEQUENCE \ + DEFINE_KEYCODE(UNKNOWN), \ + DEFINE_KEYCODE(SOFT_LEFT), \ + DEFINE_KEYCODE(SOFT_RIGHT), \ + DEFINE_KEYCODE(HOME), \ + DEFINE_KEYCODE(BACK), \ + DEFINE_KEYCODE(CALL), \ + DEFINE_KEYCODE(ENDCALL), \ + DEFINE_KEYCODE(0), \ + DEFINE_KEYCODE(1), \ + DEFINE_KEYCODE(2), \ + DEFINE_KEYCODE(3), \ + DEFINE_KEYCODE(4), \ + DEFINE_KEYCODE(5), \ + DEFINE_KEYCODE(6), \ + DEFINE_KEYCODE(7), \ + DEFINE_KEYCODE(8), \ + DEFINE_KEYCODE(9), \ + DEFINE_KEYCODE(STAR), \ + DEFINE_KEYCODE(POUND), \ + DEFINE_KEYCODE(DPAD_UP), \ + DEFINE_KEYCODE(DPAD_DOWN), \ + DEFINE_KEYCODE(DPAD_LEFT), \ + DEFINE_KEYCODE(DPAD_RIGHT), \ + DEFINE_KEYCODE(DPAD_CENTER), \ + DEFINE_KEYCODE(VOLUME_UP), \ + DEFINE_KEYCODE(VOLUME_DOWN), \ + DEFINE_KEYCODE(POWER), \ + DEFINE_KEYCODE(CAMERA), \ + DEFINE_KEYCODE(CLEAR), \ + DEFINE_KEYCODE(A), \ + DEFINE_KEYCODE(B), \ + DEFINE_KEYCODE(C), \ + DEFINE_KEYCODE(D), \ + DEFINE_KEYCODE(E), \ + DEFINE_KEYCODE(F), \ + DEFINE_KEYCODE(G), \ + DEFINE_KEYCODE(H), \ + DEFINE_KEYCODE(I), \ + DEFINE_KEYCODE(J), \ + DEFINE_KEYCODE(K), \ + DEFINE_KEYCODE(L), \ + DEFINE_KEYCODE(M), \ + DEFINE_KEYCODE(N), \ + DEFINE_KEYCODE(O), \ + DEFINE_KEYCODE(P), \ + DEFINE_KEYCODE(Q), \ + DEFINE_KEYCODE(R), \ + DEFINE_KEYCODE(S), \ + DEFINE_KEYCODE(T), \ + DEFINE_KEYCODE(U), \ + DEFINE_KEYCODE(V), \ + DEFINE_KEYCODE(W), \ + DEFINE_KEYCODE(X), \ + DEFINE_KEYCODE(Y), \ + DEFINE_KEYCODE(Z), \ + DEFINE_KEYCODE(COMMA), \ + DEFINE_KEYCODE(PERIOD), \ + DEFINE_KEYCODE(ALT_LEFT), \ + DEFINE_KEYCODE(ALT_RIGHT), \ + DEFINE_KEYCODE(SHIFT_LEFT), \ + DEFINE_KEYCODE(SHIFT_RIGHT), \ + DEFINE_KEYCODE(TAB), \ + DEFINE_KEYCODE(SPACE), \ + DEFINE_KEYCODE(SYM), \ + DEFINE_KEYCODE(EXPLORER), \ + DEFINE_KEYCODE(ENVELOPE), \ + DEFINE_KEYCODE(ENTER), \ + DEFINE_KEYCODE(DEL), \ + DEFINE_KEYCODE(GRAVE), \ + DEFINE_KEYCODE(MINUS), \ + DEFINE_KEYCODE(EQUALS), \ + DEFINE_KEYCODE(LEFT_BRACKET), \ + DEFINE_KEYCODE(RIGHT_BRACKET), \ + DEFINE_KEYCODE(BACKSLASH), \ + DEFINE_KEYCODE(SEMICOLON), \ + DEFINE_KEYCODE(APOSTROPHE), \ + DEFINE_KEYCODE(SLASH), \ + DEFINE_KEYCODE(AT), \ + DEFINE_KEYCODE(NUM), \ + DEFINE_KEYCODE(HEADSETHOOK), \ + DEFINE_KEYCODE(FOCUS), \ + DEFINE_KEYCODE(PLUS), \ + DEFINE_KEYCODE(MENU), \ + DEFINE_KEYCODE(NOTIFICATION), \ + DEFINE_KEYCODE(SEARCH), \ + DEFINE_KEYCODE(MEDIA_PLAY_PAUSE), \ + DEFINE_KEYCODE(MEDIA_STOP), \ + DEFINE_KEYCODE(MEDIA_NEXT), \ + DEFINE_KEYCODE(MEDIA_PREVIOUS), \ + DEFINE_KEYCODE(MEDIA_REWIND), \ + DEFINE_KEYCODE(MEDIA_FAST_FORWARD), \ + DEFINE_KEYCODE(MUTE), \ + DEFINE_KEYCODE(PAGE_UP), \ + DEFINE_KEYCODE(PAGE_DOWN), \ + DEFINE_KEYCODE(PICTSYMBOLS), \ + DEFINE_KEYCODE(SWITCH_CHARSET), \ + DEFINE_KEYCODE(BUTTON_A), \ + DEFINE_KEYCODE(BUTTON_B), \ + DEFINE_KEYCODE(BUTTON_C), \ + DEFINE_KEYCODE(BUTTON_X), \ + DEFINE_KEYCODE(BUTTON_Y), \ + DEFINE_KEYCODE(BUTTON_Z), \ + DEFINE_KEYCODE(BUTTON_L1), \ + DEFINE_KEYCODE(BUTTON_R1), \ + DEFINE_KEYCODE(BUTTON_L2), \ + DEFINE_KEYCODE(BUTTON_R2), \ + DEFINE_KEYCODE(BUTTON_THUMBL), \ + DEFINE_KEYCODE(BUTTON_THUMBR), \ + DEFINE_KEYCODE(BUTTON_START), \ + DEFINE_KEYCODE(BUTTON_SELECT), \ + DEFINE_KEYCODE(BUTTON_MODE), \ + DEFINE_KEYCODE(ESCAPE), \ + DEFINE_KEYCODE(FORWARD_DEL), \ + DEFINE_KEYCODE(CTRL_LEFT), \ + DEFINE_KEYCODE(CTRL_RIGHT), \ + DEFINE_KEYCODE(CAPS_LOCK), \ + DEFINE_KEYCODE(SCROLL_LOCK), \ + DEFINE_KEYCODE(META_LEFT), \ + DEFINE_KEYCODE(META_RIGHT), \ + DEFINE_KEYCODE(FUNCTION), \ + DEFINE_KEYCODE(SYSRQ), \ + DEFINE_KEYCODE(BREAK), \ + DEFINE_KEYCODE(MOVE_HOME), \ + DEFINE_KEYCODE(MOVE_END), \ + DEFINE_KEYCODE(INSERT), \ + DEFINE_KEYCODE(FORWARD), \ + DEFINE_KEYCODE(MEDIA_PLAY), \ + DEFINE_KEYCODE(MEDIA_PAUSE), \ + DEFINE_KEYCODE(MEDIA_CLOSE), \ + DEFINE_KEYCODE(MEDIA_EJECT), \ + DEFINE_KEYCODE(MEDIA_RECORD), \ + DEFINE_KEYCODE(F1), \ + DEFINE_KEYCODE(F2), \ + DEFINE_KEYCODE(F3), \ + DEFINE_KEYCODE(F4), \ + DEFINE_KEYCODE(F5), \ + DEFINE_KEYCODE(F6), \ + DEFINE_KEYCODE(F7), \ + DEFINE_KEYCODE(F8), \ + DEFINE_KEYCODE(F9), \ + DEFINE_KEYCODE(F10), \ + DEFINE_KEYCODE(F11), \ + DEFINE_KEYCODE(F12), \ + DEFINE_KEYCODE(NUM_LOCK), \ + DEFINE_KEYCODE(NUMPAD_0), \ + DEFINE_KEYCODE(NUMPAD_1), \ + DEFINE_KEYCODE(NUMPAD_2), \ + DEFINE_KEYCODE(NUMPAD_3), \ + DEFINE_KEYCODE(NUMPAD_4), \ + DEFINE_KEYCODE(NUMPAD_5), \ + DEFINE_KEYCODE(NUMPAD_6), \ + DEFINE_KEYCODE(NUMPAD_7), \ + DEFINE_KEYCODE(NUMPAD_8), \ + DEFINE_KEYCODE(NUMPAD_9), \ + DEFINE_KEYCODE(NUMPAD_DIVIDE), \ + DEFINE_KEYCODE(NUMPAD_MULTIPLY), \ + DEFINE_KEYCODE(NUMPAD_SUBTRACT), \ + DEFINE_KEYCODE(NUMPAD_ADD), \ + DEFINE_KEYCODE(NUMPAD_DOT), \ + DEFINE_KEYCODE(NUMPAD_COMMA), \ + DEFINE_KEYCODE(NUMPAD_ENTER), \ + DEFINE_KEYCODE(NUMPAD_EQUALS), \ + DEFINE_KEYCODE(NUMPAD_LEFT_PAREN), \ + DEFINE_KEYCODE(NUMPAD_RIGHT_PAREN), \ + DEFINE_KEYCODE(VOLUME_MUTE), \ + DEFINE_KEYCODE(INFO), \ + DEFINE_KEYCODE(CHANNEL_UP), \ + DEFINE_KEYCODE(CHANNEL_DOWN), \ + DEFINE_KEYCODE(ZOOM_IN), \ + DEFINE_KEYCODE(ZOOM_OUT), \ + DEFINE_KEYCODE(TV), \ + DEFINE_KEYCODE(WINDOW), \ + DEFINE_KEYCODE(GUIDE), \ + DEFINE_KEYCODE(DVR), \ + DEFINE_KEYCODE(BOOKMARK), \ + DEFINE_KEYCODE(CAPTIONS), \ + DEFINE_KEYCODE(SETTINGS), \ + DEFINE_KEYCODE(TV_POWER), \ + DEFINE_KEYCODE(TV_INPUT), \ + DEFINE_KEYCODE(STB_POWER), \ + DEFINE_KEYCODE(STB_INPUT), \ + DEFINE_KEYCODE(AVR_POWER), \ + DEFINE_KEYCODE(AVR_INPUT), \ + DEFINE_KEYCODE(PROG_RED), \ + DEFINE_KEYCODE(PROG_GREEN), \ + DEFINE_KEYCODE(PROG_YELLOW), \ + DEFINE_KEYCODE(PROG_BLUE), \ + DEFINE_KEYCODE(APP_SWITCH), \ + DEFINE_KEYCODE(BUTTON_1), \ + DEFINE_KEYCODE(BUTTON_2), \ + DEFINE_KEYCODE(BUTTON_3), \ + DEFINE_KEYCODE(BUTTON_4), \ + DEFINE_KEYCODE(BUTTON_5), \ + DEFINE_KEYCODE(BUTTON_6), \ + DEFINE_KEYCODE(BUTTON_7), \ + DEFINE_KEYCODE(BUTTON_8), \ + DEFINE_KEYCODE(BUTTON_9), \ + DEFINE_KEYCODE(BUTTON_10), \ + DEFINE_KEYCODE(BUTTON_11), \ + DEFINE_KEYCODE(BUTTON_12), \ + DEFINE_KEYCODE(BUTTON_13), \ + DEFINE_KEYCODE(BUTTON_14), \ + DEFINE_KEYCODE(BUTTON_15), \ + DEFINE_KEYCODE(BUTTON_16), \ + DEFINE_KEYCODE(LANGUAGE_SWITCH), \ + DEFINE_KEYCODE(MANNER_MODE), \ + DEFINE_KEYCODE(3D_MODE), \ + DEFINE_KEYCODE(CONTACTS), \ + DEFINE_KEYCODE(CALENDAR), \ + DEFINE_KEYCODE(MUSIC), \ + DEFINE_KEYCODE(CALCULATOR), \ + DEFINE_KEYCODE(ZENKAKU_HANKAKU), \ + DEFINE_KEYCODE(EISU), \ + DEFINE_KEYCODE(MUHENKAN), \ + DEFINE_KEYCODE(HENKAN), \ + DEFINE_KEYCODE(KATAKANA_HIRAGANA), \ + DEFINE_KEYCODE(YEN), \ + DEFINE_KEYCODE(RO), \ + DEFINE_KEYCODE(KANA), \ + DEFINE_KEYCODE(ASSIST), \ + DEFINE_KEYCODE(BRIGHTNESS_DOWN), \ + DEFINE_KEYCODE(BRIGHTNESS_UP), \ + DEFINE_KEYCODE(MEDIA_AUDIO_TRACK), \ + DEFINE_KEYCODE(SLEEP), \ + DEFINE_KEYCODE(WAKEUP), \ + DEFINE_KEYCODE(PAIRING), \ + DEFINE_KEYCODE(MEDIA_TOP_MENU), \ + DEFINE_KEYCODE(11), \ + DEFINE_KEYCODE(12), \ + DEFINE_KEYCODE(LAST_CHANNEL), \ + DEFINE_KEYCODE(TV_DATA_SERVICE), \ + DEFINE_KEYCODE(VOICE_ASSIST), \ + DEFINE_KEYCODE(TV_RADIO_SERVICE), \ + DEFINE_KEYCODE(TV_TELETEXT), \ + DEFINE_KEYCODE(TV_NUMBER_ENTRY), \ + DEFINE_KEYCODE(TV_TERRESTRIAL_ANALOG), \ + DEFINE_KEYCODE(TV_TERRESTRIAL_DIGITAL), \ + DEFINE_KEYCODE(TV_SATELLITE), \ + DEFINE_KEYCODE(TV_SATELLITE_BS), \ + DEFINE_KEYCODE(TV_SATELLITE_CS), \ + DEFINE_KEYCODE(TV_SATELLITE_SERVICE), \ + DEFINE_KEYCODE(TV_NETWORK), \ + DEFINE_KEYCODE(TV_ANTENNA_CABLE), \ + DEFINE_KEYCODE(TV_INPUT_HDMI_1), \ + DEFINE_KEYCODE(TV_INPUT_HDMI_2), \ + DEFINE_KEYCODE(TV_INPUT_HDMI_3), \ + DEFINE_KEYCODE(TV_INPUT_HDMI_4), \ + DEFINE_KEYCODE(TV_INPUT_COMPOSITE_1), \ + DEFINE_KEYCODE(TV_INPUT_COMPOSITE_2), \ + DEFINE_KEYCODE(TV_INPUT_COMPONENT_1), \ + DEFINE_KEYCODE(TV_INPUT_COMPONENT_2), \ + DEFINE_KEYCODE(TV_INPUT_VGA_1), \ + DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION), \ + DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_UP), \ + DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_DOWN), \ + DEFINE_KEYCODE(TV_ZOOM_MODE), \ + DEFINE_KEYCODE(TV_CONTENTS_MENU), \ + DEFINE_KEYCODE(TV_MEDIA_CONTEXT_MENU), \ + DEFINE_KEYCODE(TV_TIMER_PROGRAMMING), \ + DEFINE_KEYCODE(HELP), \ + DEFINE_KEYCODE(NAVIGATE_PREVIOUS), \ + DEFINE_KEYCODE(NAVIGATE_NEXT), \ + DEFINE_KEYCODE(NAVIGATE_IN), \ + DEFINE_KEYCODE(NAVIGATE_OUT), \ + DEFINE_KEYCODE(STEM_PRIMARY), \ + DEFINE_KEYCODE(STEM_1), \ + DEFINE_KEYCODE(STEM_2), \ + DEFINE_KEYCODE(STEM_3), \ + DEFINE_KEYCODE(DPAD_UP_LEFT), \ + DEFINE_KEYCODE(DPAD_DOWN_LEFT), \ + DEFINE_KEYCODE(DPAD_UP_RIGHT), \ + DEFINE_KEYCODE(DPAD_DOWN_RIGHT), \ + DEFINE_KEYCODE(MEDIA_SKIP_FORWARD), \ + DEFINE_KEYCODE(MEDIA_SKIP_BACKWARD), \ + DEFINE_KEYCODE(MEDIA_STEP_FORWARD), \ + DEFINE_KEYCODE(MEDIA_STEP_BACKWARD), \ + DEFINE_KEYCODE(SOFT_SLEEP), \ + DEFINE_KEYCODE(CUT), \ + DEFINE_KEYCODE(COPY), \ + DEFINE_KEYCODE(PASTE), \ + DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP), \ + DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN), \ + DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT), \ + DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT), \ + DEFINE_KEYCODE(ALL_APPS), \ + DEFINE_KEYCODE(REFRESH), \ + DEFINE_KEYCODE(THUMBS_UP), \ + DEFINE_KEYCODE(THUMBS_DOWN), \ + DEFINE_KEYCODE(PROFILE_SWITCH) + +// NOTE: If you add a new axis here you must also add it to several other files. +// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. +#define AXES_SEQUENCE \ + DEFINE_AXIS(X), \ + DEFINE_AXIS(Y), \ + DEFINE_AXIS(PRESSURE), \ + DEFINE_AXIS(SIZE), \ + DEFINE_AXIS(TOUCH_MAJOR), \ + DEFINE_AXIS(TOUCH_MINOR), \ + DEFINE_AXIS(TOOL_MAJOR), \ + DEFINE_AXIS(TOOL_MINOR), \ + DEFINE_AXIS(ORIENTATION), \ + DEFINE_AXIS(VSCROLL), \ + DEFINE_AXIS(HSCROLL), \ + DEFINE_AXIS(Z), \ + DEFINE_AXIS(RX), \ + DEFINE_AXIS(RY), \ + DEFINE_AXIS(RZ), \ + DEFINE_AXIS(HAT_X), \ + DEFINE_AXIS(HAT_Y), \ + DEFINE_AXIS(LTRIGGER), \ + DEFINE_AXIS(RTRIGGER), \ + DEFINE_AXIS(THROTTLE), \ + DEFINE_AXIS(RUDDER), \ + DEFINE_AXIS(WHEEL), \ + DEFINE_AXIS(GAS), \ + DEFINE_AXIS(BRAKE), \ + DEFINE_AXIS(DISTANCE), \ + DEFINE_AXIS(TILT), \ + DEFINE_AXIS(SCROLL), \ + DEFINE_AXIS(RELATIVE_X), \ + DEFINE_AXIS(RELATIVE_Y), \ + {"RESERVED_29", 29}, \ + {"RESERVED_30", 30}, \ + {"RESERVED_31", 31}, \ + DEFINE_AXIS(GENERIC_1), \ + DEFINE_AXIS(GENERIC_2), \ + DEFINE_AXIS(GENERIC_3), \ + DEFINE_AXIS(GENERIC_4), \ + DEFINE_AXIS(GENERIC_5), \ + DEFINE_AXIS(GENERIC_6), \ + DEFINE_AXIS(GENERIC_7), \ + DEFINE_AXIS(GENERIC_8), \ + DEFINE_AXIS(GENERIC_9), \ + DEFINE_AXIS(GENERIC_10), \ + DEFINE_AXIS(GENERIC_11), \ + DEFINE_AXIS(GENERIC_12), \ + DEFINE_AXIS(GENERIC_13), \ + DEFINE_AXIS(GENERIC_14), \ + DEFINE_AXIS(GENERIC_15), \ + DEFINE_AXIS(GENERIC_16) + + +// NOTE: If you add new LEDs here, you must also add them to Input.h +#define LEDS_SEQUENCE \ + DEFINE_LED(NUM_LOCK), \ + DEFINE_LED(CAPS_LOCK), \ + DEFINE_LED(SCROLL_LOCK), \ + DEFINE_LED(COMPOSE), \ + DEFINE_LED(KANA), \ + DEFINE_LED(SLEEP), \ + DEFINE_LED(SUSPEND), \ + DEFINE_LED(MUTE), \ + DEFINE_LED(MISC), \ + DEFINE_LED(MAIL), \ + DEFINE_LED(CHARGING), \ + DEFINE_LED(CONTROLLER_1), \ + DEFINE_LED(CONTROLLER_2), \ + DEFINE_LED(CONTROLLER_3), \ + DEFINE_LED(CONTROLLER_4) + +#define FLAGS_SEQUENCE \ + DEFINE_FLAG(VIRTUAL), \ + DEFINE_FLAG(FUNCTION), \ + DEFINE_FLAG(GESTURE), \ + DEFINE_FLAG(WAKE) + +// --- InputEventLookup --- +const std::unordered_map<std::string, int> InputEventLookup::KEYCODES = {KEYCODES_SEQUENCE}; + +const std::vector<InputEventLabel> InputEventLookup::KEY_NAMES = {KEYCODES_SEQUENCE}; + +const std::unordered_map<std::string, int> InputEventLookup::AXES = {AXES_SEQUENCE}; + +const std::vector<InputEventLabel> InputEventLookup::AXES_NAMES = {AXES_SEQUENCE}; + +const std::unordered_map<std::string, int> InputEventLookup::LEDS = {LEDS_SEQUENCE}; + +const std::unordered_map<std::string, int> InputEventLookup::FLAGS = {FLAGS_SEQUENCE}; + +int InputEventLookup::lookupValueByLabel(const std::unordered_map<std::string, int>& map, + const char* literal) { + std::string str(literal); + auto it = map.find(str); + return it != map.end() ? it->second : 0; +} + +const char* InputEventLookup::lookupLabelByValue(const std::vector<InputEventLabel>& vec, + int value) { + if (static_cast<size_t>(value) < vec.size()) { + return vec[value].literal; + } + return nullptr; +} + +int32_t InputEventLookup::getKeyCodeByLabel(const char* label) { + return int32_t(lookupValueByLabel(KEYCODES, label)); +} + +const char* InputEventLookup::getLabelByKeyCode(int32_t keyCode) { + if (keyCode >= 0 && static_cast<size_t>(keyCode) < KEYCODES.size()) { + return lookupLabelByValue(KEY_NAMES, keyCode); + } + return nullptr; +} + +uint32_t InputEventLookup::getKeyFlagByLabel(const char* label) { + return uint32_t(lookupValueByLabel(FLAGS, label)); +} + +int32_t InputEventLookup::getAxisByLabel(const char* label) { + return int32_t(lookupValueByLabel(AXES, label)); +} + +const char* InputEventLookup::getAxisLabel(int32_t axisId) { + return lookupLabelByValue(AXES_NAMES, axisId); +} + +int32_t InputEventLookup::getLedByLabel(const char* label) { + return int32_t(lookupValueByLabel(LEDS, label)); +} + +} // namespace android diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 11af23e1a2..6218fdcac1 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -105,6 +105,8 @@ bool InputMessage::isValid(size_t actualSize) const { return true; case Type::FOCUS: return true; + case Type::CAPTURE: + return true; } } return false; @@ -120,6 +122,8 @@ size_t InputMessage::size() const { return sizeof(Header) + body.finished.size(); case Type::FOCUS: return sizeof(Header) + body.focus.size(); + case Type::CAPTURE: + return sizeof(Header) + body.capture.size(); } return sizeof(Header); } @@ -133,12 +137,11 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { // Write the header msg->header.type = header.type; + msg->header.seq = header.seq; // Write the body switch(header.type) { case InputMessage::Type::KEY: { - // uint32_t seq - msg->body.key.seq = body.key.seq; // int32_t eventId msg->body.key.eventId = body.key.eventId; // nsecs_t eventTime @@ -168,8 +171,6 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { break; } case InputMessage::Type::MOTION: { - // uint32_t seq - msg->body.motion.seq = body.motion.seq; // int32_t eventId msg->body.motion.eventId = body.motion.eventId; // nsecs_t eventTime @@ -198,14 +199,14 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.motion.edgeFlags = body.motion.edgeFlags; // nsecs_t downTime msg->body.motion.downTime = body.motion.downTime; - // float xScale - msg->body.motion.xScale = body.motion.xScale; - // float yScale - msg->body.motion.yScale = body.motion.yScale; - // float xOffset - msg->body.motion.xOffset = body.motion.xOffset; - // float yOffset - msg->body.motion.yOffset = body.motion.yOffset; + + msg->body.motion.dsdx = body.motion.dsdx; + msg->body.motion.dtdx = body.motion.dtdx; + msg->body.motion.dtdy = body.motion.dtdy; + msg->body.motion.dsdy = body.motion.dsdy; + msg->body.motion.tx = body.motion.tx; + msg->body.motion.ty = body.motion.ty; + // float xPrecision msg->body.motion.xPrecision = body.motion.xPrecision; // float yPrecision @@ -232,55 +233,60 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { break; } case InputMessage::Type::FINISHED: { - msg->body.finished.seq = body.finished.seq; msg->body.finished.handled = body.finished.handled; + msg->body.finished.consumeTime = body.finished.consumeTime; break; } case InputMessage::Type::FOCUS: { - msg->body.focus.seq = body.focus.seq; msg->body.focus.eventId = body.focus.eventId; msg->body.focus.hasFocus = body.focus.hasFocus; msg->body.focus.inTouchMode = body.focus.inTouchMode; break; } + case InputMessage::Type::CAPTURE: { + msg->body.capture.eventId = body.capture.eventId; + msg->body.capture.pointerCaptureEnabled = body.capture.pointerCaptureEnabled; + break; + } } } // --- InputChannel --- -sp<InputChannel> InputChannel::create(const std::string& name, android::base::unique_fd fd, - sp<IBinder> token) { +std::unique_ptr<InputChannel> InputChannel::create(const std::string& name, + android::base::unique_fd fd, sp<IBinder> token) { const int result = fcntl(fd, F_SETFL, O_NONBLOCK); if (result != 0) { LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(), strerror(errno)); return nullptr; } - return new InputChannel(name, std::move(fd), token); + // using 'new' to access a non-public constructor + return std::unique_ptr<InputChannel>(new InputChannel(name, std::move(fd), token)); } -InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token) - : mName(name), mFd(std::move(fd)), mToken(token) { +InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token) + : mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) { if (DEBUG_CHANNEL_LIFECYCLE) { - ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), mFd.get()); + ALOGD("Input channel constructed: name='%s', fd=%d", getName().c_str(), getFd().get()); } } InputChannel::~InputChannel() { if (DEBUG_CHANNEL_LIFECYCLE) { - ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get()); + ALOGD("Input channel destroyed: name='%s', fd=%d", getName().c_str(), getFd().get()); } } status_t InputChannel::openInputChannelPair(const std::string& name, - sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) { + std::unique_ptr<InputChannel>& outServerChannel, + std::unique_ptr<InputChannel>& outClientChannel) { int sockets[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { status_t result = -errno; - ALOGE("channel '%s' ~ Could not create socket pair. errno=%d", - name.c_str(), errno); - outServerChannel.clear(); - outClientChannel.clear(); + ALOGE("channel '%s' ~ Could not create socket pair. errno=%d", name.c_str(), errno); + outServerChannel.reset(); + outClientChannel.reset(); return result; } @@ -308,7 +314,7 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { msg->getSanitizedCopy(&cleanMsg); ssize_t nWrite; do { - nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); + nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); if (nWrite < 0) { @@ -343,7 +349,7 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { status_t InputChannel::receiveMessage(InputMessage* msg) { ssize_t nRead; do { - nRead = ::recv(mFd.get(), msg, sizeof(InputMessage), MSG_DONTWAIT); + nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT); } while (nRead == -1 && errno == EINTR); if (nRead < 0) { @@ -380,59 +386,59 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { return OK; } -sp<InputChannel> InputChannel::dup() const { - android::base::unique_fd newFd(::dup(getFd())); - if (!newFd.ok()) { - ALOGE("Could not duplicate fd %i for channel %s: %s", getFd(), mName.c_str(), - strerror(errno)); - const bool hitFdLimit = errno == EMFILE || errno == ENFILE; - // If this process is out of file descriptors, then throwing that might end up exploding - // on the other side of a binder call, which isn't really helpful. - // Better to just crash here and hope that the FD leak is slow. - // Other failures could be client errors, so we still propagate those back to the caller. - LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s", - getName().c_str()); - return nullptr; - } - return InputChannel::create(mName, std::move(newFd), mToken); +std::unique_ptr<InputChannel> InputChannel::dup() const { + base::unique_fd newFd(dupFd()); + return InputChannel::create(getName(), std::move(newFd), getConnectionToken()); } -status_t InputChannel::write(Parcel& out) const { - status_t s = out.writeCString(getName().c_str()); - if (s != OK) { - return s; - } +void InputChannel::copyTo(InputChannel& outChannel) const { + outChannel.mName = getName(); + outChannel.mFd = dupFd(); + outChannel.mToken = getConnectionToken(); +} - s = out.writeStrongBinder(mToken); - if (s != OK) { - return s; +status_t InputChannel::writeToParcel(android::Parcel* parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; } - - s = out.writeUniqueFileDescriptor(mFd); - return s; + return parcel->writeStrongBinder(mToken) + ?: parcel->writeUtf8AsUtf16(mName) ?: parcel->writeUniqueFileDescriptor(mFd); } -sp<InputChannel> InputChannel::read(const Parcel& from) { - std::string name = from.readCString(); - sp<IBinder> token = from.readStrongBinder(); - android::base::unique_fd rawFd; - status_t fdResult = from.readUniqueFileDescriptor(&rawFd); - if (fdResult != OK) { - return nullptr; +status_t InputChannel::readFromParcel(const android::Parcel* parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; } - - return InputChannel::create(name, std::move(rawFd), token); + mToken = parcel->readStrongBinder(); + return parcel->readUtf8FromUtf16(&mName) ?: parcel->readUniqueFileDescriptor(&mFd); } sp<IBinder> InputChannel::getConnectionToken() const { return mToken; } +base::unique_fd InputChannel::dupFd() const { + android::base::unique_fd newFd(::dup(getFd())); + if (!newFd.ok()) { + ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(), + strerror(errno)); + const bool hitFdLimit = errno == EMFILE || errno == ENFILE; + // If this process is out of file descriptors, then throwing that might end up exploding + // on the other side of a binder call, which isn't really helpful. + // Better to just crash here and hope that the FD leak is slow. + // Other failures could be client errors, so we still propagate those back to the caller. + LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s", + getName().c_str()); + return {}; + } + return newFd; +} + // --- InputPublisher --- -InputPublisher::InputPublisher(const sp<InputChannel>& channel) : - mChannel(channel) { -} +InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) : mChannel(channel) {} InputPublisher::~InputPublisher() { } @@ -463,7 +469,7 @@ status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t InputMessage msg; msg.header.type = InputMessage::Type::KEY; - msg.body.key.seq = seq; + msg.header.seq = seq; msg.body.key.eventId = eventId; msg.body.key.deviceId = deviceId; msg.body.key.source = source; @@ -484,10 +490,10 @@ status_t InputPublisher::publishMotionEvent( uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, - MotionClassification classification, float xScale, float yScale, float xOffset, - float yOffset, float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { + MotionClassification classification, const ui::Transform& transform, float xPrecision, + float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, + nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords) { if (ATRACE_ENABLED()) { std::string message = StringPrintf( "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")", @@ -495,17 +501,18 @@ status_t InputPublisher::publishMotionEvent( ATRACE_NAME(message.c_str()); } if (DEBUG_TRANSPORT_ACTIONS) { + std::string transformString; + transform.dump(transformString, "transform", " "); ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " "displayId=%" PRId32 ", " "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " - "metaState=0x%x, buttonState=0x%x, classification=%s, xScale=%.1f, yScale=%.1f, " - "xOffset=%.1f, yOffset=%.1f, " + "metaState=0x%x, buttonState=0x%x, classification=%s," "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " - "pointerCount=%" PRIu32, + "pointerCount=%" PRIu32 " \n%s", mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState, buttonState, - motionClassificationToString(classification), xScale, yScale, xOffset, yOffset, - xPrecision, yPrecision, downTime, eventTime, pointerCount); + motionClassificationToString(classification), xPrecision, yPrecision, downTime, + eventTime, pointerCount, transformString.c_str()); } if (!seq) { @@ -521,7 +528,7 @@ status_t InputPublisher::publishMotionEvent( InputMessage msg; msg.header.type = InputMessage::Type::MOTION; - msg.body.motion.seq = seq; + msg.header.seq = seq; msg.body.motion.eventId = eventId; msg.body.motion.deviceId = deviceId; msg.body.motion.source = source; @@ -534,10 +541,12 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.metaState = metaState; msg.body.motion.buttonState = buttonState; msg.body.motion.classification = classification; - msg.body.motion.xScale = xScale; - msg.body.motion.yScale = yScale; - msg.body.motion.xOffset = xOffset; - msg.body.motion.yOffset = yOffset; + msg.body.motion.dsdx = transform.dsdx(); + msg.body.motion.dtdx = transform.dtdx(); + msg.body.motion.dtdy = transform.dtdy(); + msg.body.motion.dsdy = transform.dsdy(); + msg.body.motion.tx = transform.tx(); + msg.body.motion.ty = transform.ty(); msg.body.motion.xPrecision = xPrecision; msg.body.motion.yPrecision = yPrecision; msg.body.motion.xCursorPosition = xCursorPosition; @@ -565,14 +574,32 @@ status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool h InputMessage msg; msg.header.type = InputMessage::Type::FOCUS; - msg.body.focus.seq = seq; + msg.header.seq = seq; msg.body.focus.eventId = eventId; msg.body.focus.hasFocus = hasFocus ? 1 : 0; msg.body.focus.inTouchMode = inTouchMode ? 1 : 0; return mChannel->sendMessage(&msg); } -status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) { +status_t InputPublisher::publishCaptureEvent(uint32_t seq, int32_t eventId, + bool pointerCaptureEnabled) { + if (ATRACE_ENABLED()) { + std::string message = + StringPrintf("publishCaptureEvent(inputChannel=%s, pointerCaptureEnabled=%s)", + mChannel->getName().c_str(), toString(pointerCaptureEnabled)); + ATRACE_NAME(message.c_str()); + } + + InputMessage msg; + msg.header.type = InputMessage::Type::CAPTURE; + msg.header.seq = seq; + msg.body.capture.eventId = eventId; + msg.body.capture.pointerCaptureEnabled = pointerCaptureEnabled ? 1 : 0; + return mChannel->sendMessage(&msg); +} + +status_t InputPublisher::receiveFinishedSignal( + const std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)>& callback) { if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str()); } @@ -580,8 +607,6 @@ status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandle InputMessage msg; status_t result = mChannel->receiveMessage(&msg); if (result) { - *outSeq = 0; - *outHandled = false; return result; } if (msg.header.type != InputMessage::Type::FINISHED) { @@ -589,17 +614,14 @@ status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandle mChannel->getName().c_str(), msg.header.type); return UNKNOWN_ERROR; } - *outSeq = msg.body.finished.seq; - *outHandled = msg.body.finished.handled == 1; + callback(msg.header.seq, msg.body.finished.handled == 1, msg.body.finished.consumeTime); return OK; } // --- InputConsumer --- -InputConsumer::InputConsumer(const sp<InputChannel>& channel) : - mResampleTouch(isTouchResamplingEnabled()), - mChannel(channel), mMsgDeferred(false) { -} +InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel) + : mResampleTouch(isTouchResamplingEnabled()), mChannel(channel), mMsgDeferred(false) {} InputConsumer::~InputConsumer() { } @@ -628,6 +650,9 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum } else { // Receive a fresh message. status_t result = mChannel->receiveMessage(&mMsg); + if (result == OK) { + mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC)); + } if (result) { // Consume the next batched event unless batches are being held for later. if (consumeBatches || result != WOULD_BLOCK) { @@ -650,7 +675,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum if (!keyEvent) return NO_MEMORY; initializeKeyEvent(keyEvent, &mMsg); - *outSeq = mMsg.body.key.seq; + *outSeq = mMsg.header.seq; *outEvent = keyEvent; if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' consumer ~ consumed key event, seq=%u", @@ -662,9 +687,9 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum case InputMessage::Type::MOTION: { ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); if (batchIndex >= 0) { - Batch& batch = mBatches.editItemAt(batchIndex); + Batch& batch = mBatches[batchIndex]; if (canAddSample(batch, &mMsg)) { - batch.samples.push(mMsg); + batch.samples.push_back(mMsg); if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' consumer ~ appended to batch event", mChannel->getName().c_str()); @@ -675,18 +700,18 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum // No need to process events that we are going to cancel anyways const size_t count = batch.samples.size(); for (size_t i = 0; i < count; i++) { - const InputMessage& msg = batch.samples.itemAt(i); - sendFinishedSignal(msg.body.motion.seq, false); + const InputMessage& msg = batch.samples[i]; + sendFinishedSignal(msg.header.seq, false); } - batch.samples.removeItemsAt(0, count); - mBatches.removeAt(batchIndex); + batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count); + mBatches.erase(mBatches.begin() + batchIndex); } else { // We cannot append to the batch in progress, so we need to consume // the previous batch right now and defer the new message until later. mMsgDeferred = true; status_t result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent); - mBatches.removeAt(batchIndex); + mBatches.erase(mBatches.begin() + batchIndex); if (result) { return result; } @@ -702,9 +727,9 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum // Start a new batch if needed. if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { - mBatches.push(); - Batch& batch = mBatches.editTop(); - batch.samples.push(mMsg); + Batch batch; + batch.samples.push_back(mMsg); + mBatches.push_back(batch); if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' consumer ~ started batch event", mChannel->getName().c_str()); @@ -717,7 +742,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum updateTouchState(mMsg); initializeMotionEvent(motionEvent, &mMsg); - *outSeq = mMsg.body.motion.seq; + *outSeq = mMsg.header.seq; *outEvent = motionEvent; if (DEBUG_TRANSPORT_ACTIONS) { @@ -738,10 +763,20 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum if (!focusEvent) return NO_MEMORY; initializeFocusEvent(focusEvent, &mMsg); - *outSeq = mMsg.body.focus.seq; + *outSeq = mMsg.header.seq; *outEvent = focusEvent; break; } + + case InputMessage::Type::CAPTURE: { + CaptureEvent* captureEvent = factory->createCaptureEvent(); + if (!captureEvent) return NO_MEMORY; + + initializeCaptureEvent(captureEvent, &mMsg); + *outSeq = mMsg.header.seq; + *outEvent = captureEvent; + break; + } } } return OK; @@ -752,10 +787,10 @@ status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, status_t result; for (size_t i = mBatches.size(); i > 0; ) { i--; - Batch& batch = mBatches.editItemAt(i); + Batch& batch = mBatches[i]; if (frameTime < 0) { result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent); - mBatches.removeAt(i); + mBatches.erase(mBatches.begin() + i); return result; } @@ -770,11 +805,11 @@ status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, result = consumeSamples(factory, batch, split + 1, outSeq, outEvent); const InputMessage* next; - if (batch.samples.isEmpty()) { - mBatches.removeAt(i); + if (batch.samples.empty()) { + mBatches.erase(mBatches.begin() + i); next = nullptr; } else { - next = &batch.samples.itemAt(0); + next = &batch.samples[0]; } if (!result && mResampleTouch) { resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next); @@ -792,20 +827,20 @@ status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, uint32_t chain = 0; for (size_t i = 0; i < count; i++) { - InputMessage& msg = batch.samples.editItemAt(i); + InputMessage& msg = batch.samples[i]; updateTouchState(msg); if (i) { SeqChain seqChain; - seqChain.seq = msg.body.motion.seq; + seqChain.seq = msg.header.seq; seqChain.chain = chain; - mSeqChains.push(seqChain); + mSeqChains.push_back(seqChain); addSample(motionEvent, &msg); } else { initializeMotionEvent(motionEvent, &msg); } - chain = msg.body.motion.seq; + chain = msg.header.seq; } - batch.samples.removeItemsAt(0, count); + batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count); *outSeq = chain; *outEvent = motionEvent; @@ -827,10 +862,10 @@ void InputConsumer::updateTouchState(InputMessage& msg) { case AMOTION_EVENT_ACTION_DOWN: { ssize_t index = findTouchState(deviceId, source); if (index < 0) { - mTouchStates.push(); + mTouchStates.push_back({}); index = mTouchStates.size() - 1; } - TouchState& touchState = mTouchStates.editItemAt(index); + TouchState& touchState = mTouchStates[index]; touchState.initialize(deviceId, source); touchState.addHistory(msg); break; @@ -839,7 +874,7 @@ void InputConsumer::updateTouchState(InputMessage& msg) { case AMOTION_EVENT_ACTION_MOVE: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { - TouchState& touchState = mTouchStates.editItemAt(index); + TouchState& touchState = mTouchStates[index]; touchState.addHistory(msg); rewriteMessage(touchState, msg); } @@ -849,7 +884,7 @@ void InputConsumer::updateTouchState(InputMessage& msg) { case AMOTION_EVENT_ACTION_POINTER_DOWN: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { - TouchState& touchState = mTouchStates.editItemAt(index); + TouchState& touchState = mTouchStates[index]; touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); rewriteMessage(touchState, msg); } @@ -859,7 +894,7 @@ void InputConsumer::updateTouchState(InputMessage& msg) { case AMOTION_EVENT_ACTION_POINTER_UP: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { - TouchState& touchState = mTouchStates.editItemAt(index); + TouchState& touchState = mTouchStates[index]; rewriteMessage(touchState, msg); touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); } @@ -869,7 +904,7 @@ void InputConsumer::updateTouchState(InputMessage& msg) { case AMOTION_EVENT_ACTION_SCROLL: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { - TouchState& touchState = mTouchStates.editItemAt(index); + TouchState& touchState = mTouchStates[index]; rewriteMessage(touchState, msg); } break; @@ -879,9 +914,9 @@ void InputConsumer::updateTouchState(InputMessage& msg) { case AMOTION_EVENT_ACTION_CANCEL: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { - TouchState& touchState = mTouchStates.editItemAt(index); + TouchState& touchState = mTouchStates[index]; rewriteMessage(touchState, msg); - mTouchStates.removeAt(index); + mTouchStates.erase(mTouchStates.begin() + index); } break; } @@ -938,7 +973,7 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, return; } - TouchState& touchState = mTouchStates.editItemAt(index); + TouchState& touchState = mTouchStates[index]; if (touchState.historySize < 1) { #if DEBUG_RESAMPLING ALOGD("Not resampled, no history for device."); @@ -1084,11 +1119,11 @@ status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { size_t chainIndex = 0; for (size_t i = seqChainCount; i > 0; ) { i--; - const SeqChain& seqChain = mSeqChains.itemAt(i); + const SeqChain& seqChain = mSeqChains[i]; if (seqChain.seq == currentSeq) { currentSeq = seqChain.chain; chainSeqs[chainIndex++] = currentSeq; - mSeqChains.removeAt(i); + mSeqChains.erase(mSeqChains.begin() + i); } } status_t status = OK; @@ -1102,7 +1137,7 @@ status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { SeqChain seqChain; seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq; seqChain.chain = chainSeqs[chainIndex]; - mSeqChains.push(seqChain); + mSeqChains.push_back(seqChain); if (!chainIndex) break; chainIndex--; } @@ -1114,12 +1149,33 @@ status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { return sendUnchainedFinishedSignal(seq, handled); } +nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const { + auto it = mConsumeTimes.find(seq); + // Consume time will be missing if either 'finishInputEvent' is called twice, or if it was + // called for the wrong (synthetic?) input event. Either way, it is a bug that should be fixed. + LOG_ALWAYS_FATAL_IF(it == mConsumeTimes.end(), "Could not find consume time for seq=%" PRIu32, + seq); + return it->second; +} + +void InputConsumer::popConsumeTime(uint32_t seq) { + mConsumeTimes.erase(seq); +} + status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { InputMessage msg; msg.header.type = InputMessage::Type::FINISHED; - msg.body.finished.seq = seq; + msg.header.seq = seq; msg.body.finished.handled = handled ? 1 : 0; - return mChannel->sendMessage(&msg); + msg.body.finished.consumeTime = getConsumeTime(seq); + status_t result = mChannel->sendMessage(&msg); + if (result == OK) { + // Remove the consume time if the socket write succeeded. We will not need to ack this + // message anymore. If the socket write did not succeed, we will try again and will still + // need consume time. + popConsumeTime(seq); + } + return result; } bool InputConsumer::hasDeferredEvent() const { @@ -1127,23 +1183,23 @@ bool InputConsumer::hasDeferredEvent() const { } bool InputConsumer::hasPendingBatch() const { - return !mBatches.isEmpty(); + return !mBatches.empty(); } int32_t InputConsumer::getPendingBatchSource() const { - if (mBatches.isEmpty()) { + if (mBatches.empty()) { return AINPUT_SOURCE_CLASS_NONE; } - const Batch& batch = mBatches.itemAt(0); - const InputMessage& head = batch.samples.itemAt(0); + const Batch& batch = mBatches[0]; + const InputMessage& head = batch.samples[0]; return head.body.motion.source; } ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { for (size_t i = 0; i < mBatches.size(); i++) { - const Batch& batch = mBatches.itemAt(i); - const InputMessage& head = batch.samples.itemAt(0); + const Batch& batch = mBatches[i]; + const InputMessage& head = batch.samples[0]; if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) { return i; } @@ -1153,7 +1209,7 @@ ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { for (size_t i = 0; i < mTouchStates.size(); i++) { - const TouchState& touchState = mTouchStates.itemAt(i); + const TouchState& touchState = mTouchStates[i]; if (touchState.deviceId == deviceId && touchState.source == source) { return i; } @@ -1174,6 +1230,10 @@ void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg->body.focus.inTouchMode == 1); } +void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg) { + event->initialize(msg->body.capture.eventId, msg->body.capture.pointerCaptureEnabled == 1); +} + void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { uint32_t pointerCount = msg->body.motion.pointerCount; PointerProperties pointerProperties[pointerCount]; @@ -1183,16 +1243,18 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); } + ui::Transform transform; + transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx, + msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1}); event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source, msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action, msg->body.motion.actionButton, msg->body.motion.flags, msg->body.motion.edgeFlags, msg->body.motion.metaState, - msg->body.motion.buttonState, msg->body.motion.classification, - msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset, - msg->body.motion.yOffset, msg->body.motion.xPrecision, - msg->body.motion.yPrecision, msg->body.motion.xCursorPosition, - msg->body.motion.yCursorPosition, msg->body.motion.downTime, - msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); + msg->body.motion.buttonState, msg->body.motion.classification, transform, + msg->body.motion.xPrecision, msg->body.motion.yPrecision, + msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, + msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, + pointerProperties, pointerCoords); } void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { @@ -1207,7 +1269,7 @@ void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { } bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) { - const InputMessage& head = batch.samples.itemAt(0); + const InputMessage& head = batch.samples[0]; uint32_t pointerCount = msg->body.motion.pointerCount; if (head.body.motion.pointerCount != pointerCount || head.body.motion.action != msg->body.motion.action) { @@ -1225,11 +1287,87 @@ bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) { ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) { size_t numSamples = batch.samples.size(); size_t index = 0; - while (index < numSamples - && batch.samples.itemAt(index).body.motion.eventTime <= time) { + while (index < numSamples && batch.samples[index].body.motion.eventTime <= time) { index += 1; } return ssize_t(index) - 1; } +std::string InputConsumer::dump() const { + std::string out; + out = out + "mResampleTouch = " + toString(mResampleTouch) + "\n"; + out = out + "mChannel = " + mChannel->getName() + "\n"; + out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n"; + if (mMsgDeferred) { + out = out + "mMsg : " + InputMessage::typeToString(mMsg.header.type) + "\n"; + } + out += "Batches:\n"; + for (const Batch& batch : mBatches) { + out += " Batch:\n"; + for (const InputMessage& msg : batch.samples) { + out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq, + InputMessage::typeToString(msg.header.type)); + switch (msg.header.type) { + case InputMessage::Type::KEY: { + out += android::base::StringPrintf("action=%s keycode=%" PRId32, + KeyEvent::actionToString( + msg.body.key.action), + msg.body.key.keyCode); + break; + } + case InputMessage::Type::MOTION: { + out = out + "action=" + MotionEvent::actionToString(msg.body.motion.action); + for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { + const float x = msg.body.motion.pointers[i].coords.getX(); + const float y = msg.body.motion.pointers[i].coords.getY(); + out += android::base::StringPrintf("\n Pointer %" PRIu32 + " : x=%.1f y=%.1f", + i, x, y); + } + break; + } + case InputMessage::Type::FINISHED: { + out += android::base::StringPrintf("handled=%s, consumeTime=%" PRId64, + toString(msg.body.finished.handled), + msg.body.finished.consumeTime); + break; + } + case InputMessage::Type::FOCUS: { + out += android::base::StringPrintf("hasFocus=%s inTouchMode=%s", + toString(msg.body.focus.hasFocus), + toString(msg.body.focus.inTouchMode)); + break; + } + case InputMessage::Type::CAPTURE: { + out += android::base::StringPrintf("hasCapture=%s", + toString(msg.body.capture + .pointerCaptureEnabled)); + break; + } + } + out += "\n"; + } + } + if (mBatches.empty()) { + out += " <empty>\n"; + } + out += "mSeqChains:\n"; + for (const SeqChain& chain : mSeqChains) { + out += android::base::StringPrintf(" chain: seq = %" PRIu32 " chain=%" PRIu32, chain.seq, + chain.chain); + } + if (mSeqChains.empty()) { + out += " <empty>\n"; + } + out += "mConsumeTimes:\n"; + for (const auto& [seq, consumeTime] : mConsumeTimes) { + out += android::base::StringPrintf(" seq = %" PRIu32 " consumeTime = %" PRId64, seq, + consumeTime); + } + if (mConsumeTimes.empty()) { + out += " <empty>\n"; + } + return out; +} + } // namespace android diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index 85a2015e43..8546bbbb43 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -14,20 +14,20 @@ * limitations under the License. */ +#include <type_traits> #define LOG_TAG "InputWindow" #define LOG_NDEBUG 0 +#include <android-base/stringprintf.h> #include <binder/Parcel.h> -#include <input/InputWindow.h> #include <input/InputTransport.h> +#include <input/InputWindow.h> #include <log/log.h> -#include <ui/Rect.h> -#include <ui/Region.h> - namespace android { + // --- InputWindowInfo --- void InputWindowInfo::addTouchableRegion(const Rect& region) { touchableRegion.orSelf(region); @@ -42,22 +42,8 @@ bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const { && y >= frameTop && y < frameBottom; } -// TODO(b/155781676): Remove and replace call points with trustedOverlay when that is ready. -bool InputWindowInfo::isTrustedOverlay() const { - return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG || - layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_STATUS_BAR || - layoutParamsType == TYPE_NOTIFICATION_SHADE || - layoutParamsType == TYPE_NAVIGATION_BAR || - layoutParamsType == TYPE_NAVIGATION_BAR_PANEL || - layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY || - layoutParamsType == TYPE_DOCK_DIVIDER || - layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY || - layoutParamsType == TYPE_INPUT_CONSUMER || - layoutParamsType == TYPE_TRUSTED_APPLICATION_OVERLAY; -} - bool InputWindowInfo::supportsSplitTouch() const { - return layoutParamsFlags & FLAG_SPLIT_TOUCH; + return flags.test(Flag::SPLIT_TOUCH); } bool InputWindowInfo::overlaps(const InputWindowInfo* other) const { @@ -65,94 +51,158 @@ bool InputWindowInfo::overlaps(const InputWindowInfo* other) const { && frameTop < other->frameBottom && frameBottom > other->frameTop; } -status_t InputWindowInfo::write(Parcel& output) const { +bool InputWindowInfo::operator==(const InputWindowInfo& info) const { + return info.token == token && info.id == id && info.name == name && info.flags == flags && + info.type == type && info.dispatchingTimeout == dispatchingTimeout && + info.frameLeft == frameLeft && info.frameTop == frameTop && + info.frameRight == frameRight && info.frameBottom == frameBottom && + info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor && + info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) && + info.visible == visible && info.trustedOverlay == trustedOverlay && + info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode && + info.hasWallpaper == hasWallpaper && info.paused == paused && + info.ownerPid == ownerPid && info.ownerUid == ownerUid && + info.packageName == packageName && info.inputFeatures == inputFeatures && + info.displayId == displayId && info.portalToDisplayId == portalToDisplayId && + info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop && + info.applicationInfo == applicationInfo; +} + +status_t InputWindowInfo::writeToParcel(android::Parcel* parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } if (name.empty()) { - output.writeInt32(0); + parcel->writeInt32(0); return OK; } - output.writeInt32(1); - status_t s = output.writeStrongBinder(token); - if (s != OK) return s; - - output.writeInt32(id); - output.writeString8(String8(name.c_str())); - output.writeInt32(layoutParamsFlags); - output.writeInt32(layoutParamsType); - output.writeInt64(dispatchingTimeout); - output.writeInt32(frameLeft); - output.writeInt32(frameTop); - output.writeInt32(frameRight); - output.writeInt32(frameBottom); - output.writeInt32(surfaceInset); - output.writeFloat(globalScaleFactor); - output.writeFloat(windowXScale); - output.writeFloat(windowYScale); - output.writeBool(visible); - output.writeBool(canReceiveKeys); - output.writeBool(hasFocus); - output.writeBool(hasWallpaper); - output.writeBool(paused); - output.writeInt32(ownerPid); - output.writeInt32(ownerUid); - output.writeInt32(inputFeatures); - output.writeInt32(displayId); - output.writeInt32(portalToDisplayId); - applicationInfo.write(output); - output.write(touchableRegion); - output.writeBool(replaceTouchableRegionWithCrop); - output.writeStrongBinder(touchableRegionCropHandle.promote()); - return OK; + parcel->writeInt32(1); + + // clang-format off + status_t status = parcel->writeStrongBinder(token) ?: + parcel->writeInt64(dispatchingTimeout.count()) ?: + parcel->writeInt32(id) ?: + parcel->writeUtf8AsUtf16(name) ?: + parcel->writeInt32(flags.get()) ?: + parcel->writeInt32(static_cast<std::underlying_type_t<InputWindowInfo::Type>>(type)) ?: + parcel->writeInt32(frameLeft) ?: + parcel->writeInt32(frameTop) ?: + parcel->writeInt32(frameRight) ?: + parcel->writeInt32(frameBottom) ?: + parcel->writeInt32(surfaceInset) ?: + parcel->writeFloat(globalScaleFactor) ?: + parcel->writeFloat(alpha) ?: + parcel->writeFloat(transform.dsdx()) ?: + parcel->writeFloat(transform.dtdx()) ?: + parcel->writeFloat(transform.tx()) ?: + parcel->writeFloat(transform.dtdy()) ?: + parcel->writeFloat(transform.dsdy()) ?: + parcel->writeFloat(transform.ty()) ?: + parcel->writeBool(visible) ?: + parcel->writeBool(focusable) ?: + parcel->writeBool(hasWallpaper) ?: + parcel->writeBool(paused) ?: + parcel->writeBool(trustedOverlay) ?: + parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?: + parcel->writeInt32(ownerPid) ?: + parcel->writeInt32(ownerUid) ?: + parcel->writeUtf8AsUtf16(packageName) ?: + parcel->writeInt32(inputFeatures.get()) ?: + parcel->writeInt32(displayId) ?: + parcel->writeInt32(portalToDisplayId) ?: + applicationInfo.writeToParcel(parcel) ?: + parcel->write(touchableRegion) ?: + parcel->writeBool(replaceTouchableRegionWithCrop) ?: + parcel->writeStrongBinder(touchableRegionCropHandle.promote()); + // clang-format on + return status; } -InputWindowInfo InputWindowInfo::read(const Parcel& from) { - InputWindowInfo ret; +status_t InputWindowInfo::readFromParcel(const android::Parcel* parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + if (parcel->readInt32() == 0) { + return OK; + } + + token = parcel->readStrongBinder(); + dispatchingTimeout = static_cast<decltype(dispatchingTimeout)>(parcel->readInt64()); + status_t status = parcel->readInt32(&id) ?: parcel->readUtf8FromUtf16(&name); + if (status != OK) { + return status; + } - if (from.readInt32() == 0) { - return ret; + flags = Flags<Flag>(parcel->readInt32()); + type = static_cast<Type>(parcel->readInt32()); + float dsdx, dtdx, tx, dtdy, dsdy, ty; + int32_t touchOcclusionModeInt; + // clang-format off + status = parcel->readInt32(&frameLeft) ?: + parcel->readInt32(&frameTop) ?: + parcel->readInt32(&frameRight) ?: + parcel->readInt32(&frameBottom) ?: + parcel->readInt32(&surfaceInset) ?: + parcel->readFloat(&globalScaleFactor) ?: + parcel->readFloat(&alpha) ?: + parcel->readFloat(&dsdx) ?: + parcel->readFloat(&dtdx) ?: + parcel->readFloat(&tx) ?: + parcel->readFloat(&dtdy) ?: + parcel->readFloat(&dsdy) ?: + parcel->readFloat(&ty) ?: + parcel->readBool(&visible) ?: + parcel->readBool(&focusable) ?: + parcel->readBool(&hasWallpaper) ?: + parcel->readBool(&paused) ?: + parcel->readBool(&trustedOverlay) ?: + parcel->readInt32(&touchOcclusionModeInt) ?: + parcel->readInt32(&ownerPid) ?: + parcel->readInt32(&ownerUid) ?: + parcel->readUtf8FromUtf16(&packageName); + // clang-format on + + if (status != OK) { + return status; } - ret.token = from.readStrongBinder(); - ret.id = from.readInt32(); - ret.name = from.readString8().c_str(); - ret.layoutParamsFlags = from.readInt32(); - ret.layoutParamsType = from.readInt32(); - ret.dispatchingTimeout = from.readInt64(); - ret.frameLeft = from.readInt32(); - ret.frameTop = from.readInt32(); - ret.frameRight = from.readInt32(); - ret.frameBottom = from.readInt32(); - ret.surfaceInset = from.readInt32(); - ret.globalScaleFactor = from.readFloat(); - ret.windowXScale = from.readFloat(); - ret.windowYScale = from.readFloat(); - ret.visible = from.readBool(); - ret.canReceiveKeys = from.readBool(); - ret.hasFocus = from.readBool(); - ret.hasWallpaper = from.readBool(); - ret.paused = from.readBool(); - ret.ownerPid = from.readInt32(); - ret.ownerUid = from.readInt32(); - ret.inputFeatures = from.readInt32(); - ret.displayId = from.readInt32(); - ret.portalToDisplayId = from.readInt32(); - ret.applicationInfo = InputApplicationInfo::read(from); - from.read(ret.touchableRegion); - ret.replaceTouchableRegionWithCrop = from.readBool(); - ret.touchableRegionCropHandle = from.readStrongBinder(); - - return ret; -} + touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt); + + inputFeatures = Flags<Feature>(parcel->readInt32()); + status = parcel->readInt32(&displayId) ?: + parcel->readInt32(&portalToDisplayId) ?: + applicationInfo.readFromParcel(parcel) ?: + parcel->read(touchableRegion) ?: + parcel->readBool(&replaceTouchableRegionWithCrop); + + if (status != OK) { + return status; + } + + touchableRegionCropHandle = parcel->readStrongBinder(); + transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1}); -InputWindowInfo::InputWindowInfo(const Parcel& from) { - *this = read(from); + return OK; } // --- InputWindowHandle --- -InputWindowHandle::InputWindowHandle() { +InputWindowHandle::InputWindowHandle() {} + +InputWindowHandle::~InputWindowHandle() {} + +InputWindowHandle::InputWindowHandle(const InputWindowHandle& other) : mInfo(other.mInfo) {} + +InputWindowHandle::InputWindowHandle(const InputWindowInfo& other) : mInfo(other) {} + +status_t InputWindowHandle::writeToParcel(android::Parcel* parcel) const { + return mInfo.writeToParcel(parcel); } -InputWindowHandle::~InputWindowHandle() { +status_t InputWindowHandle::readFromParcel(const android::Parcel* parcel) { + return mInfo.readFromParcel(parcel); } void InputWindowHandle::releaseChannel() { @@ -166,5 +216,4 @@ sp<IBinder> InputWindowHandle::getToken() const { void InputWindowHandle::updateFrom(sp<InputWindowHandle> handle) { mInfo = handle->mInfo; } - } // namespace android diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index cb68165433..44f3f34994 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -19,14 +19,14 @@ #include <stdlib.h> #include <string.h> -#ifdef __ANDROID__ +#ifdef __linux__ #include <binder/Parcel.h> #endif - #include <android/keycodes.h> +#include <attestation/HmacKeyManager.h> #include <input/InputEventLabels.h> -#include <input/Keyboard.h> #include <input/KeyCharacterMap.h> +#include <input/Keyboard.h> #include <utils/Log.h> #include <utils/Errors.h> @@ -85,15 +85,12 @@ static String8 toString(const char16_t* chars, size_t numChars) { // --- KeyCharacterMap --- -sp<KeyCharacterMap> KeyCharacterMap::sEmpty = new KeyCharacterMap(); +KeyCharacterMap::KeyCharacterMap() : mType(KeyboardType::UNKNOWN) {} -KeyCharacterMap::KeyCharacterMap() : - mType(KEYBOARD_TYPE_UNKNOWN) { -} - -KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) : - RefBase(), mType(other.mType), mKeysByScanCode(other.mKeysByScanCode), - mKeysByUsageCode(other.mKeysByUsageCode) { +KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) + : mType(other.mType), + mKeysByScanCode(other.mKeysByScanCode), + mKeysByUsageCode(other.mKeysByUsageCode) { for (size_t i = 0; i < other.mKeys.size(); i++) { mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i))); } @@ -106,102 +103,135 @@ KeyCharacterMap::~KeyCharacterMap() { } } -status_t KeyCharacterMap::load(const std::string& filename, - Format format, sp<KeyCharacterMap>* outMap) { - outMap->clear(); +bool KeyCharacterMap::operator==(const KeyCharacterMap& other) const { + if (mType != other.mType) { + return false; + } + if (mKeys.size() != other.mKeys.size() || + mKeysByScanCode.size() != other.mKeysByScanCode.size() || + mKeysByUsageCode.size() != other.mKeysByUsageCode.size()) { + return false; + } + + for (size_t i = 0; i < mKeys.size(); i++) { + if (mKeys.keyAt(i) != other.mKeys.keyAt(i)) { + return false; + } + const Key* key = mKeys.valueAt(i); + const Key* otherKey = other.mKeys.valueAt(i); + if (key->label != otherKey->label || key->number != otherKey->number) { + return false; + } + } + + for (size_t i = 0; i < mKeysByScanCode.size(); i++) { + if (mKeysByScanCode.keyAt(i) != other.mKeysByScanCode.keyAt(i)) { + return false; + } + if (mKeysByScanCode.valueAt(i) != other.mKeysByScanCode.valueAt(i)) { + return false; + } + } + + for (size_t i = 0; i < mKeysByUsageCode.size(); i++) { + if (mKeysByUsageCode.keyAt(i) != other.mKeysByUsageCode.keyAt(i)) { + return false; + } + if (mKeysByUsageCode.valueAt(i) != other.mKeysByUsageCode.valueAt(i)) { + return false; + } + } + + return true; +} +base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename, + Format format) { Tokenizer* tokenizer; status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); if (status) { - ALOGE("Error %d opening key character map file %s.", status, filename.c_str()); - } else { - status = load(tokenizer, format, outMap); - delete tokenizer; + return Errorf("Error {} opening key character map file {}.", status, filename.c_str()); } - return status; + std::unique_ptr<Tokenizer> t(tokenizer); + auto ret = load(t.get(), format); + if (ret.ok()) { + (*ret)->mLoadFileName = filename; + } + return ret; } -status_t KeyCharacterMap::loadContents(const std::string& filename, const char* contents, - Format format, sp<KeyCharacterMap>* outMap) { - outMap->clear(); - +base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::loadContents( + const std::string& filename, const char* contents, Format format) { Tokenizer* tokenizer; status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); if (status) { ALOGE("Error %d opening key character map.", status); - } else { - status = load(tokenizer, format, outMap); - delete tokenizer; + return Errorf("Error {} opening key character map.", status); } - return status; + std::unique_ptr<Tokenizer> t(tokenizer); + auto ret = load(t.get(), format); + if (ret.ok()) { + (*ret)->mLoadFileName = filename; + } + return ret; } -status_t KeyCharacterMap::load(Tokenizer* tokenizer, - Format format, sp<KeyCharacterMap>* outMap) { +base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(Tokenizer* tokenizer, + Format format) { status_t status = OK; - sp<KeyCharacterMap> map = new KeyCharacterMap(); + std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap()); if (!map.get()) { ALOGE("Error allocating key character map."); - status = NO_MEMORY; - } else { + return Errorf("Error allocating key character map."); + } #if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); #endif - Parser parser(map.get(), tokenizer, format); - status = parser.parse(); + Parser parser(map.get(), tokenizer, format); + status = parser.parse(); #if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0); #endif - if (!status) { - *outMap = map; - } + if (status == OK) { + return map; } - return status; -} -sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base, - const sp<KeyCharacterMap>& overlay) { - if (overlay == nullptr) { - return base; - } - if (base == nullptr) { - return overlay; - } + return Errorf("Load KeyCharacterMap failed {}.", status); +} - sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get()); - for (size_t i = 0; i < overlay->mKeys.size(); i++) { - int32_t keyCode = overlay->mKeys.keyAt(i); - Key* key = overlay->mKeys.valueAt(i); - ssize_t oldIndex = map->mKeys.indexOfKey(keyCode); +void KeyCharacterMap::combine(const KeyCharacterMap& overlay) { + for (size_t i = 0; i < overlay.mKeys.size(); i++) { + int32_t keyCode = overlay.mKeys.keyAt(i); + Key* key = overlay.mKeys.valueAt(i); + ssize_t oldIndex = mKeys.indexOfKey(keyCode); if (oldIndex >= 0) { - delete map->mKeys.valueAt(oldIndex); - map->mKeys.editValueAt(oldIndex) = new Key(*key); + delete mKeys.valueAt(oldIndex); + mKeys.editValueAt(oldIndex) = new Key(*key); } else { - map->mKeys.add(keyCode, new Key(*key)); + mKeys.add(keyCode, new Key(*key)); } } - for (size_t i = 0; i < overlay->mKeysByScanCode.size(); i++) { - map->mKeysByScanCode.replaceValueFor(overlay->mKeysByScanCode.keyAt(i), - overlay->mKeysByScanCode.valueAt(i)); + for (size_t i = 0; i < overlay.mKeysByScanCode.size(); i++) { + mKeysByScanCode.replaceValueFor(overlay.mKeysByScanCode.keyAt(i), + overlay.mKeysByScanCode.valueAt(i)); } - for (size_t i = 0; i < overlay->mKeysByUsageCode.size(); i++) { - map->mKeysByUsageCode.replaceValueFor(overlay->mKeysByUsageCode.keyAt(i), - overlay->mKeysByUsageCode.valueAt(i)); + for (size_t i = 0; i < overlay.mKeysByUsageCode.size(); i++) { + mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i), + overlay.mKeysByUsageCode.valueAt(i)); } - return map; + mLoadFileName = overlay.mLoadFileName; } -sp<KeyCharacterMap> KeyCharacterMap::empty() { - return sEmpty; +KeyCharacterMap::KeyboardType KeyCharacterMap::getKeyboardType() const { + return mType; } -int32_t KeyCharacterMap::getKeyboardType() const { - return mType; +const std::string KeyCharacterMap::getLoadFileName() const { + return mLoadFileName; } char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const { @@ -599,10 +629,14 @@ void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents, } } -#ifdef __ANDROID__ -sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { - sp<KeyCharacterMap> map = new KeyCharacterMap(); - map->mType = parcel->readInt32(); +#ifdef __linux__ +std::shared_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return nullptr; + } + std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap()); + map->mType = static_cast<KeyCharacterMap::KeyboardType>(parcel->readInt32()); size_t numKeys = parcel->readInt32(); if (parcel->errorCheck()) { return nullptr; @@ -656,7 +690,11 @@ sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { } void KeyCharacterMap::writeToParcel(Parcel* parcel) const { - parcel->writeInt32(mType); + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return; + } + parcel->writeInt32(static_cast<int32_t>(mType)); size_t numKeys = mKeys.size(); parcel->writeInt32(numKeys); @@ -677,8 +715,7 @@ void KeyCharacterMap::writeToParcel(Parcel* parcel) const { parcel->writeInt32(0); } } -#endif - +#endif // __linux__ // --- KeyCharacterMap::Key --- @@ -782,20 +819,20 @@ status_t KeyCharacterMap::Parser::parse() { return BAD_VALUE; } - if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) { + if (mMap->mType == KeyboardType::UNKNOWN) { ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.", mTokenizer->getLocation().string()); return BAD_VALUE; } - if (mFormat == FORMAT_BASE) { - if (mMap->mType == KEYBOARD_TYPE_OVERLAY) { + if (mFormat == Format::BASE) { + if (mMap->mType == KeyboardType::OVERLAY) { ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.", mTokenizer->getLocation().string()); return BAD_VALUE; } - } else if (mFormat == FORMAT_OVERLAY) { - if (mMap->mType != KEYBOARD_TYPE_OVERLAY) { + } else if (mFormat == Format::OVERLAY) { + if (mMap->mType != KeyboardType::OVERLAY) { ALOGE("%s: Overlay keyboard layout missing required keyboard " "'type OVERLAY' declaration.", mTokenizer->getLocation().string()); @@ -807,7 +844,7 @@ status_t KeyCharacterMap::Parser::parse() { } status_t KeyCharacterMap::Parser::parseType() { - if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) { + if (mMap->mType != KeyboardType::UNKNOWN) { ALOGE("%s: Duplicate keyboard 'type' declaration.", mTokenizer->getLocation().string()); return BAD_VALUE; @@ -816,20 +853,20 @@ status_t KeyCharacterMap::Parser::parseType() { KeyboardType type; String8 typeToken = mTokenizer->nextToken(WHITESPACE); if (typeToken == "NUMERIC") { - type = KEYBOARD_TYPE_NUMERIC; + type = KeyboardType::NUMERIC; } else if (typeToken == "PREDICTIVE") { - type = KEYBOARD_TYPE_PREDICTIVE; + type = KeyboardType::PREDICTIVE; } else if (typeToken == "ALPHA") { - type = KEYBOARD_TYPE_ALPHA; + type = KeyboardType::ALPHA; } else if (typeToken == "FULL") { - type = KEYBOARD_TYPE_FULL; + type = KeyboardType::FULL; } else if (typeToken == "SPECIAL_FUNCTION") { ALOGW("The SPECIAL_FUNCTION type is now declared in the device's IDC file, please set " "the property 'keyboard.specialFunction' to '1' there instead."); // TODO: return BAD_VALUE here in Q - type = KEYBOARD_TYPE_SPECIAL_FUNCTION; + type = KeyboardType::SPECIAL_FUNCTION; } else if (typeToken == "OVERLAY") { - type = KEYBOARD_TYPE_OVERLAY; + type = KeyboardType::OVERLAY; } else { ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(), typeToken.string()); @@ -880,7 +917,7 @@ status_t KeyCharacterMap::Parser::parseMapKey() { mTokenizer->skipDelimiters(WHITESPACE); String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string()); if (!keyCode) { ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), keyCodeToken.string()); @@ -897,7 +934,7 @@ status_t KeyCharacterMap::Parser::parseMapKey() { status_t KeyCharacterMap::Parser::parseKey() { String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string()); if (!keyCode) { ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), keyCodeToken.string()); @@ -1017,7 +1054,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { } else if (token == "fallback") { mTokenizer->skipDelimiters(WHITESPACE); token = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(token.string()); + int32_t keyCode = InputEventLookup::getKeyCodeByLabel(token.string()); if (!keyCode) { ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.", mTokenizer->getLocation().string(), @@ -1034,7 +1071,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { } else if (token == "replace") { mTokenizer->skipDelimiters(WHITESPACE); token = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(token.string()); + int32_t keyCode = InputEventLookup::getKeyCodeByLabel(token.string()); if (!keyCode) { ALOGE("%s: Invalid key code label for replace, got '%s'.", mTokenizer->getLocation().string(), diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index efca68d171..fa5a5412e6 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -20,12 +20,13 @@ #include <android/keycodes.h> #include <input/InputEventLabels.h> -#include <input/Keyboard.h> #include <input/KeyLayoutMap.h> -#include <utils/Log.h> +#include <input/Keyboard.h> +#include <input/NamedEnum.h> #include <utils/Errors.h> -#include <utils/Tokenizer.h> +#include <utils/Log.h> #include <utils/Timers.h> +#include <utils/Tokenizer.h> // Enables debug output for the parser. #define DEBUG_PARSER 0 @@ -41,6 +42,26 @@ namespace android { static const char* WHITESPACE = " \t\r"; +#define SENSOR_ENTRY(type) NamedEnum::string(type), type +static const std::unordered_map<std::string, InputDeviceSensorType> SENSOR_LIST = + {{SENSOR_ENTRY(InputDeviceSensorType::ACCELEROMETER)}, + {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD)}, + {SENSOR_ENTRY(InputDeviceSensorType::ORIENTATION)}, + {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE)}, + {SENSOR_ENTRY(InputDeviceSensorType::LIGHT)}, + {SENSOR_ENTRY(InputDeviceSensorType::PRESSURE)}, + {SENSOR_ENTRY(InputDeviceSensorType::TEMPERATURE)}, + {SENSOR_ENTRY(InputDeviceSensorType::PROXIMITY)}, + {SENSOR_ENTRY(InputDeviceSensorType::GRAVITY)}, + {SENSOR_ENTRY(InputDeviceSensorType::LINEAR_ACCELERATION)}, + {SENSOR_ENTRY(InputDeviceSensorType::ROTATION_VECTOR)}, + {SENSOR_ENTRY(InputDeviceSensorType::RELATIVE_HUMIDITY)}, + {SENSOR_ENTRY(InputDeviceSensorType::AMBIENT_TEMPERATURE)}, + {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED)}, + {SENSOR_ENTRY(InputDeviceSensorType::GAME_ROTATION_VECTOR)}, + {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE_UNCALIBRATED)}, + {SENSOR_ENTRY(InputDeviceSensorType::SIGNIFICANT_MOTION)}}; + // --- KeyLayoutMap --- KeyLayoutMap::KeyLayoutMap() { @@ -49,37 +70,60 @@ KeyLayoutMap::KeyLayoutMap() { KeyLayoutMap::~KeyLayoutMap() { } -status_t KeyLayoutMap::load(const std::string& filename, sp<KeyLayoutMap>* outMap) { - outMap->clear(); +base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename, + const char* contents) { + Tokenizer* tokenizer; + status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); + if (status) { + ALOGE("Error %d opening key layout map.", status); + return Errorf("Error {} opening key layout map file {}.", status, filename.c_str()); + } + std::unique_ptr<Tokenizer> t(tokenizer); + auto ret = load(t.get()); + if (ret.ok()) { + (*ret)->mLoadFileName = filename; + } + return ret; +} +base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename) { Tokenizer* tokenizer; status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); if (status) { ALOGE("Error %d opening key layout map file %s.", status, filename.c_str()); + return Errorf("Error {} opening key layout map file {}.", status, filename.c_str()); + } + std::unique_ptr<Tokenizer> t(tokenizer); + auto ret = load(t.get()); + if (ret.ok()) { + (*ret)->mLoadFileName = filename; + } + return ret; +} + +base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(Tokenizer* tokenizer) { + std::shared_ptr<KeyLayoutMap> map = std::shared_ptr<KeyLayoutMap>(new KeyLayoutMap()); + status_t status = OK; + if (!map.get()) { + ALOGE("Error allocating key layout map."); + return Errorf("Error allocating key layout map."); } else { - sp<KeyLayoutMap> map = new KeyLayoutMap(); - if (!map.get()) { - ALOGE("Error allocating key layout map."); - status = NO_MEMORY; - } else { #if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); #endif - Parser parser(map.get(), tokenizer); - status = parser.parse(); + Parser parser(map.get(), tokenizer); + status = parser.parse(); #if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); #endif - if (!status) { - *outMap = map; - } + if (!status) { + return std::move(map); } - delete tokenizer; } - return status; + return Errorf("Load KeyLayoutMap failed {}.", status); } status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, @@ -104,6 +148,24 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, return NO_ERROR; } +// Return pair of sensor type and sensor data index, for the input device abs code +base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor(int32_t absCode) { + auto it = mSensorsByAbsCode.find(absCode); + if (it == mSensorsByAbsCode.end()) { +#if DEBUG_MAPPING + ALOGD("mapSensor: absCode=%d, ~ Failed.", absCode); +#endif + return Errorf("Can't find abs code {}.", absCode); + } + const Sensor& sensor = it->second; + +#if DEBUG_MAPPING + ALOGD("mapSensor: absCode=%d, sensorType=0x%0x, sensorDataIndex=0x%x.", absCode, + NamedEnum::string(sensor.sensorType), sensor.sensorDataIndex); +#endif + return std::make_pair(sensor.sensorType, sensor.sensorDataIndex); +} + const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { if (usageCode) { ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); @@ -219,6 +281,10 @@ status_t KeyLayoutMap::Parser::parse() { mTokenizer->skipDelimiters(WHITESPACE); status_t status = parseLed(); if (status) return status; + } else if (keywordToken == "sensor") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseSensor(); + if (status) return status; } else { ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), keywordToken.string()); @@ -264,7 +330,7 @@ status_t KeyLayoutMap::Parser::parseKey() { mTokenizer->skipDelimiters(WHITESPACE); String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string()); if (!keyCode) { ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), keyCodeToken.string()); @@ -277,7 +343,7 @@ status_t KeyLayoutMap::Parser::parseKey() { if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break; String8 flagToken = mTokenizer->nextToken(WHITESPACE); - uint32_t flag = getKeyFlagByLabel(flagToken.string()); + uint32_t flag = InputEventLookup::getKeyFlagByLabel(flagToken.string()); if (!flag) { ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(), flagToken.string()); @@ -326,7 +392,7 @@ status_t KeyLayoutMap::Parser::parseAxis() { mTokenizer->skipDelimiters(WHITESPACE); String8 axisToken = mTokenizer->nextToken(WHITESPACE); - axisInfo.axis = getAxisByLabel(axisToken.string()); + axisInfo.axis = InputEventLookup::getAxisByLabel(axisToken.string()); if (axisInfo.axis < 0) { ALOGE("%s: Expected inverted axis label, got '%s'.", mTokenizer->getLocation().string(), axisToken.string()); @@ -346,7 +412,7 @@ status_t KeyLayoutMap::Parser::parseAxis() { mTokenizer->skipDelimiters(WHITESPACE); String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE); - axisInfo.axis = getAxisByLabel(lowAxisToken.string()); + axisInfo.axis = InputEventLookup::getAxisByLabel(lowAxisToken.string()); if (axisInfo.axis < 0) { ALOGE("%s: Expected low axis label, got '%s'.", mTokenizer->getLocation().string(), lowAxisToken.string()); @@ -355,14 +421,14 @@ status_t KeyLayoutMap::Parser::parseAxis() { mTokenizer->skipDelimiters(WHITESPACE); String8 highAxisToken = mTokenizer->nextToken(WHITESPACE); - axisInfo.highAxis = getAxisByLabel(highAxisToken.string()); + axisInfo.highAxis = InputEventLookup::getAxisByLabel(highAxisToken.string()); if (axisInfo.highAxis < 0) { ALOGE("%s: Expected high axis label, got '%s'.", mTokenizer->getLocation().string(), highAxisToken.string()); return BAD_VALUE; } } else { - axisInfo.axis = getAxisByLabel(token.string()); + axisInfo.axis = InputEventLookup::getAxisByLabel(token.string()); if (axisInfo.axis < 0) { ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.", mTokenizer->getLocation().string(), token.string()); @@ -428,7 +494,7 @@ status_t KeyLayoutMap::Parser::parseLed() { mTokenizer->skipDelimiters(WHITESPACE); String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE); - int32_t ledCode = getLedByLabel(ledCodeToken.string()); + int32_t ledCode = InputEventLookup::getLedByLabel(ledCodeToken.string()); if (ledCode < 0) { ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(), ledCodeToken.string()); @@ -445,4 +511,84 @@ status_t KeyLayoutMap::Parser::parseLed() { map.add(code, led); return NO_ERROR; } + +static std::optional<InputDeviceSensorType> getSensorType(const char* token) { + auto it = SENSOR_LIST.find(std::string(token)); + if (it == SENSOR_LIST.end()) { + return std::nullopt; + } + return it->second; +} + +static std::optional<int32_t> getSensorDataIndex(String8 token) { + std::string tokenStr(token.string()); + if (tokenStr == "X") { + return 0; + } else if (tokenStr == "Y") { + return 1; + } else if (tokenStr == "Z") { + return 2; + } + return std::nullopt; +} + +// Parse sensor type and data index mapping, as below format +// sensor <raw abs> <sensor type> <sensor data index> +// raw abs : the linux abs code of the axis +// sensor type : string name of InputDeviceSensorType +// sensor data index : the data index of sensor, out of [X, Y, Z] +// Examples: +// sensor 0x00 ACCELEROMETER X +// sensor 0x01 ACCELEROMETER Y +// sensor 0x02 ACCELEROMETER Z +// sensor 0x03 GYROSCOPE X +// sensor 0x04 GYROSCOPE Y +// sensor 0x05 GYROSCOPE Z +status_t KeyLayoutMap::Parser::parseSensor() { + String8 codeToken = mTokenizer->nextToken(WHITESPACE); + char* end; + int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); + if (*end) { + ALOGE("%s: Expected sensor %s number, got '%s'.", mTokenizer->getLocation().string(), + "abs code", codeToken.string()); + return BAD_VALUE; + } + + std::unordered_map<int32_t, Sensor>& map = mMap->mSensorsByAbsCode; + if (map.find(code) != map.end()) { + ALOGE("%s: Duplicate entry for sensor %s '%s'.", mTokenizer->getLocation().string(), + "abs code", codeToken.string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + String8 sensorTypeToken = mTokenizer->nextToken(WHITESPACE); + std::optional<InputDeviceSensorType> typeOpt = getSensorType(sensorTypeToken.string()); + if (!typeOpt) { + ALOGE("%s: Expected sensor code label, got '%s'.", mTokenizer->getLocation().string(), + sensorTypeToken.string()); + return BAD_VALUE; + } + InputDeviceSensorType sensorType = typeOpt.value(); + mTokenizer->skipDelimiters(WHITESPACE); + String8 sensorDataIndexToken = mTokenizer->nextToken(WHITESPACE); + std::optional<int32_t> indexOpt = getSensorDataIndex(sensorDataIndexToken); + if (!indexOpt) { + ALOGE("%s: Expected sensor data index label, got '%s'.", mTokenizer->getLocation().string(), + sensorDataIndexToken.string()); + return BAD_VALUE; + } + int32_t sensorDataIndex = indexOpt.value(); + +#if DEBUG_PARSER + ALOGD("Parsed sensor: abs code=%d, sensorType=%d, sensorDataIndex=%d.", code, + NamedEnum::string(sensorType).c_str(), sensorDataIndex); +#endif + + Sensor sensor; + sensor.sensorType = sensorType; + sensor.sensorDataIndex = sensorDataIndex; + map.emplace(code, sensor); + return NO_ERROR; +} }; diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp index 56900c129e..f0895b32ef 100644 --- a/libs/input/Keyboard.cpp +++ b/libs/input/Keyboard.cpp @@ -105,35 +105,34 @@ bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const std::string& name) { - std::string path(getPath(deviceIdentifier, name, - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT)); + std::string path(getPath(deviceIdentifier, name, InputDeviceConfigurationFileType::KEY_LAYOUT)); if (path.empty()) { return NAME_NOT_FOUND; } - status_t status = KeyLayoutMap::load(path, &keyLayoutMap); - if (status) { - return status; + base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path); + if (!ret.ok()) { + return ret.error().code(); } - + keyLayoutMap = *ret; keyLayoutFile = path; return OK; } status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& name) { - std::string path = getPath(deviceIdentifier, name, - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP); + std::string path = + getPath(deviceIdentifier, name, InputDeviceConfigurationFileType::KEY_CHARACTER_MAP); if (path.empty()) { return NAME_NOT_FOUND; } - status_t status = KeyCharacterMap::load(path, - KeyCharacterMap::FORMAT_BASE, &keyCharacterMap); - if (status) { - return status; + base::Result<std::shared_ptr<KeyCharacterMap>> ret = + KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE); + if (!ret.ok()) { + return ret.error().code(); } - + keyCharacterMap = *ret; keyCharacterMapFile = path; return OK; } @@ -160,9 +159,9 @@ bool isKeyboardSpecialFunction(const PropertyMap* config) { bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, const PropertyMap* deviceConfiguration, const KeyMap* keyMap) { // TODO: remove the third OR statement (SPECIAL_FUNCTION) in Q - if (!keyMap->haveKeyCharacterMap() || isKeyboardSpecialFunction(deviceConfiguration) - || keyMap->keyCharacterMap->getKeyboardType() - == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) { + if (!keyMap->haveKeyCharacterMap() || isKeyboardSpecialFunction(deviceConfiguration) || + keyMap->keyCharacterMap->getKeyboardType() == + KeyCharacterMap::KeyboardType::SPECIAL_FUNCTION) { return false; } diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp index 4833eb9c05..a842166761 100644 --- a/libs/input/PropertyMap.cpp +++ b/libs/input/PropertyMap.cpp @@ -107,23 +107,22 @@ void PropertyMap::addAll(const PropertyMap* map) { } } -status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) { - *outMap = nullptr; +android::base::Result<std::unique_ptr<PropertyMap>> PropertyMap::load(const char* filename) { + std::unique_ptr<PropertyMap> outMap = std::make_unique<PropertyMap>(); + if (outMap == nullptr) { + return android::base::Error(NO_MEMORY) << "Error allocating property map."; + } - Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); + Tokenizer* rawTokenizer; + status_t status = Tokenizer::open(String8(filename), &rawTokenizer); + std::unique_ptr<Tokenizer> tokenizer(rawTokenizer); if (status) { - ALOGE("Error %d opening property file %s.", status, filename.string()); + ALOGE("Error %d opening property file %s.", status, filename); } else { - PropertyMap* map = new PropertyMap(); - if (!map) { - ALOGE("Error allocating property map."); - status = NO_MEMORY; - } else { #if DEBUG_PARSER_PERFORMANCE nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); #endif - Parser parser(map, tokenizer); + Parser parser(outMap.get(), tokenizer.get()); status = parser.parse(); #if DEBUG_PARSER_PERFORMANCE nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; @@ -132,14 +131,10 @@ status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) { elapsedTime / 1000000.0); #endif if (status) { - delete map; - } else { - *outMap = map; + return android::base::Error(BAD_VALUE) << "Could not parse " << filename; } - } - delete tokenizer; } - return status; + return std::move(outMap); } // --- PropertyMap::Parser --- diff --git a/libs/input/PropertyMap_fuzz.cpp b/libs/input/PropertyMap_fuzz.cpp index 23ead0e469..afb97a1d47 100755 --- a/libs/input/PropertyMap_fuzz.cpp +++ b/libs/input/PropertyMap_fuzz.cpp @@ -42,7 +42,7 @@ static const std::vector<std::function<void(FuzzedDataProvider*, android::Proper android::String8 out; propertyMap.tryGetProperty(key, out); }, - [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void { + [](FuzzedDataProvider* dataProvider, android::PropertyMap /*unused*/) -> void { TemporaryFile tf; // Generate file contents std::string contents = dataProvider->ConsumeRandomLengthString(MAX_FILE_SIZE); @@ -52,8 +52,7 @@ static const std::vector<std::function<void(FuzzedDataProvider*, android::Proper const char* bytes = contents.c_str(); android::base::WriteStringToFd(bytes, tf.fd); } - android::PropertyMap* mapPtr = &propertyMap; - android::PropertyMap::load(android::String8(tf.path), &mapPtr); + android::PropertyMap::load(tf.path); }, [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void { std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN); @@ -65,12 +64,12 @@ static const std::vector<std::function<void(FuzzedDataProvider*, android::Proper }; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { FuzzedDataProvider dataProvider(data, size); - android::PropertyMap proprtyMap = android::PropertyMap(); + android::PropertyMap propertyMap = android::PropertyMap(); int opsRun = 0; while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) { uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1); - operations[op](&dataProvider, proprtyMap); + operations[op](&dataProvider, propertyMap); } return 0; } diff --git a/libs/input/TEST_MAPPING b/libs/input/TEST_MAPPING new file mode 100644 index 0000000000..9626d8dac7 --- /dev/null +++ b/libs/input/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "frameworks/native/services/inputflinger" + } + ] +} diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp index bcf55b0ea3..2c04d420f4 100644 --- a/libs/input/VelocityControl.cpp +++ b/libs/input/VelocityControl.cpp @@ -66,7 +66,7 @@ void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { if (deltaY) { mRawPosition.y += *deltaY; } - mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition); + mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), {mRawPosition}); float vx, vy; float scale = mParameters.scale; diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index c6cc4fc374..a44f0b7fe0 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -104,107 +104,73 @@ static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool r // --- VelocityTracker --- -// The default velocity tracker strategy. -// Although other strategies are available for testing and comparison purposes, -// this is the strategy that applications will actually use. Be very careful -// when adjusting the default strategy because it can dramatically affect -// (often in a bad way) the user experience. -const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2"; - -VelocityTracker::VelocityTracker(const char* strategy) : - mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) { - char value[PROPERTY_VALUE_MAX]; - - // Allow the default strategy to be overridden using a system property for debugging. - if (!strategy) { - int length = property_get("persist.input.velocitytracker.strategy", value, nullptr); - if (length > 0) { - strategy = value; - } else { - strategy = DEFAULT_STRATEGY; - } - } - +VelocityTracker::VelocityTracker(const Strategy strategy) + : mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) { // Configure the strategy. if (!configureStrategy(strategy)) { - ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy); - if (!configureStrategy(DEFAULT_STRATEGY)) { - LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!", - strategy); + ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy); + if (!configureStrategy(VelocityTracker::DEFAULT_STRATEGY)) { + LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%" PRId32 + "'!", + strategy); } } } VelocityTracker::~VelocityTracker() { - delete mStrategy; } -bool VelocityTracker::configureStrategy(const char* strategy) { - mStrategy = createStrategy(strategy); +bool VelocityTracker::configureStrategy(Strategy strategy) { + if (strategy == VelocityTracker::Strategy::DEFAULT) { + mStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY); + } else { + mStrategy = createStrategy(strategy); + } return mStrategy != nullptr; } -VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { - if (!strcmp("impulse", strategy)) { - // Physical model of pushing an object. Quality: VERY GOOD. - // Works with duplicate coordinates, unclean finger liftoff. - return new ImpulseVelocityTrackerStrategy(); - } - if (!strcmp("lsq1", strategy)) { - // 1st order least squares. Quality: POOR. - // Frequently underfits the touch data especially when the finger accelerates - // or changes direction. Often underestimates velocity. The direction - // is overly influenced by historical touch points. - return new LeastSquaresVelocityTrackerStrategy(1); - } - if (!strcmp("lsq2", strategy)) { - // 2nd order least squares. Quality: VERY GOOD. - // Pretty much ideal, but can be confused by certain kinds of touch data, - // particularly if the panel has a tendency to generate delayed, - // duplicate or jittery touch coordinates when the finger is released. - return new LeastSquaresVelocityTrackerStrategy(2); - } - if (!strcmp("lsq3", strategy)) { - // 3rd order least squares. Quality: UNUSABLE. - // Frequently overfits the touch data yielding wildly divergent estimates - // of the velocity when the finger is released. - return new LeastSquaresVelocityTrackerStrategy(3); - } - if (!strcmp("wlsq2-delta", strategy)) { - // 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL - return new LeastSquaresVelocityTrackerStrategy(2, - LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA); - } - if (!strcmp("wlsq2-central", strategy)) { - // 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL - return new LeastSquaresVelocityTrackerStrategy(2, - LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL); - } - if (!strcmp("wlsq2-recent", strategy)) { - // 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL - return new LeastSquaresVelocityTrackerStrategy(2, - LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT); - } - if (!strcmp("int1", strategy)) { - // 1st order integrating filter. Quality: GOOD. - // Not as good as 'lsq2' because it cannot estimate acceleration but it is - // more tolerant of errors. Like 'lsq1', this strategy tends to underestimate - // the velocity of a fling but this strategy tends to respond to changes in - // direction more quickly and accurately. - return new IntegratingVelocityTrackerStrategy(1); - } - if (!strcmp("int2", strategy)) { - // 2nd order integrating filter. Quality: EXPERIMENTAL. - // For comparison purposes only. Unlike 'int1' this strategy can compensate - // for acceleration but it typically overestimates the effect. - return new IntegratingVelocityTrackerStrategy(2); - } - if (!strcmp("legacy", strategy)) { - // Legacy velocity tracker algorithm. Quality: POOR. - // For comparison purposes only. This algorithm is strongly influenced by - // old data points, consistently underestimates velocity and takes a very long - // time to adjust to changes in direction. - return new LegacyVelocityTrackerStrategy(); +std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy( + VelocityTracker::Strategy strategy) { + switch (strategy) { + case VelocityTracker::Strategy::IMPULSE: + return std::make_unique<ImpulseVelocityTrackerStrategy>(); + + case VelocityTracker::Strategy::LSQ1: + return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1); + + case VelocityTracker::Strategy::LSQ2: + return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2); + + case VelocityTracker::Strategy::LSQ3: + return std::make_unique<LeastSquaresVelocityTrackerStrategy>(3); + + case VelocityTracker::Strategy::WLSQ2_DELTA: + return std::make_unique< + LeastSquaresVelocityTrackerStrategy>(2, + LeastSquaresVelocityTrackerStrategy:: + WEIGHTING_DELTA); + case VelocityTracker::Strategy::WLSQ2_CENTRAL: + return std::make_unique< + LeastSquaresVelocityTrackerStrategy>(2, + LeastSquaresVelocityTrackerStrategy:: + WEIGHTING_CENTRAL); + case VelocityTracker::Strategy::WLSQ2_RECENT: + return std::make_unique< + LeastSquaresVelocityTrackerStrategy>(2, + LeastSquaresVelocityTrackerStrategy:: + WEIGHTING_RECENT); + + case VelocityTracker::Strategy::INT1: + return std::make_unique<IntegratingVelocityTrackerStrategy>(1); + + case VelocityTracker::Strategy::INT2: + return std::make_unique<IntegratingVelocityTrackerStrategy>(2); + + case VelocityTracker::Strategy::LEGACY: + return std::make_unique<LegacyVelocityTrackerStrategy>(); + + default: + break; } return nullptr; } @@ -227,7 +193,11 @@ void VelocityTracker::clearPointers(BitSet32 idBits) { mStrategy->clearPointers(idBits); } -void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) { +void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::vector<VelocityTracker::Position>& positions) { + LOG_ALWAYS_FATAL_IF(idBits.count() != positions.size(), + "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu", + idBits.count(), positions.size()); while (idBits.count() > MAX_POINTERS) { idBits.clearLastMarkedBit(); } @@ -319,12 +289,12 @@ void VelocityTracker::addMovement(const MotionEvent* event) { pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i)); } - nsecs_t eventTime; - Position positions[pointerCount]; + std::vector<Position> positions; + positions.resize(pointerCount); size_t historySize = event->getHistorySize(); - for (size_t h = 0; h < historySize; h++) { - eventTime = event->getHistoricalEventTime(h); + for (size_t h = 0; h <= historySize; h++) { + nsecs_t eventTime = event->getHistoricalEventTime(h); for (size_t i = 0; i < pointerCount; i++) { uint32_t index = pointerIndex[i]; positions[index].x = event->getHistoricalX(i, h); @@ -332,14 +302,6 @@ void VelocityTracker::addMovement(const MotionEvent* event) { } addMovement(eventTime, idBits, positions); } - - eventTime = event->getEventTime(); - for (size_t i = 0; i < pointerCount; i++) { - uint32_t index = pointerIndex[i]; - positions[index].x = event->getX(i); - positions[index].y = event->getY(i); - } - addMovement(eventTime, idBits, positions); } bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const { @@ -380,8 +342,9 @@ void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { mMovements[mIndex].idBits = remainingIdBits; } -void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions) { +void LeastSquaresVelocityTrackerStrategy::addMovement( + nsecs_t eventTime, BitSet32 idBits, + const std::vector<VelocityTracker::Position>& positions) { if (mMovements[mIndex].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include @@ -453,13 +416,15 @@ void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet3 * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares * http://en.wikipedia.org/wiki/Gram-Schmidt */ -static bool solveLeastSquares(const float* x, const float* y, - const float* w, uint32_t m, uint32_t n, float* outB, float* outDet) { +static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y, + const std::vector<float>& w, uint32_t n, float* outB, float* outDet) { + const size_t m = x.size(); #if DEBUG_STRATEGY ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n), vectorToString(x, m).c_str(), vectorToString(y, m).c_str(), vectorToString(w, m).c_str()); #endif + LOG_ALWAYS_FATAL_IF(m != y.size() || m != w.size(), "Mismatched vector sizes"); // Expand the X vector to a matrix A, pre-multiplied by the weights. float a[n][m]; // column-major order @@ -576,7 +541,9 @@ static bool solveLeastSquares(const float* x, const float* y, * the default implementation */ static std::optional<std::array<float, 3>> solveUnweightedLeastSquaresDeg2( - const float* x, const float* y, size_t count) { + const std::vector<float>& x, const std::vector<float>& y) { + const size_t count = x.size(); + LOG_ALWAYS_FATAL_IF(count != y.size(), "Mismatching array sizes"); // Solving y = a*x^2 + b*x + c float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0; @@ -628,11 +595,11 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, outEstimator->clear(); // Iterate over movement samples in reverse time order and collect samples. - float x[HISTORY_SIZE]; - float y[HISTORY_SIZE]; - float w[HISTORY_SIZE]; - float time[HISTORY_SIZE]; - uint32_t m = 0; + std::vector<float> x; + std::vector<float> y; + std::vector<float> w; + std::vector<float> time; + uint32_t index = mIndex; const Movement& newestMovement = mMovements[mIndex]; do { @@ -647,13 +614,14 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, } const VelocityTracker::Position& position = movement.getPosition(id); - x[m] = position.x; - y[m] = position.y; - w[m] = chooseWeight(index); - time[m] = -age * 0.000000001f; + x.push_back(position.x); + y.push_back(position.y); + w.push_back(chooseWeight(index)); + time.push_back(-age * 0.000000001f); index = (index == 0 ? HISTORY_SIZE : index) - 1; - } while (++m < HISTORY_SIZE); + } while (x.size() < HISTORY_SIZE); + const size_t m = x.size(); if (m == 0) { return false; // no data } @@ -666,8 +634,8 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, if (degree == 2 && mWeighting == WEIGHTING_NONE) { // Optimize unweighted, quadratic polynomial fit - std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x, m); - std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y, m); + std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x); + std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y); if (xCoeff && yCoeff) { outEstimator->time = newestMovement.eventTime; outEstimator->degree = 2; @@ -682,8 +650,8 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, // General case for an Nth degree polynomial fit float xdet, ydet; uint32_t n = degree + 1; - if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet) - && solveLeastSquares(time, y, w, m, n, outEstimator->yCoeff, &ydet)) { + if (solveLeastSquares(time, x, w, n, outEstimator->xCoeff, &xdet) && + solveLeastSquares(time, y, w, n, outEstimator->yCoeff, &ydet)) { outEstimator->time = newestMovement.eventTime; outEstimator->degree = degree; outEstimator->confidence = xdet * ydet; @@ -792,8 +760,9 @@ void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { mPointerIdBits.value &= ~idBits.value; } -void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions) { +void IntegratingVelocityTrackerStrategy::addMovement( + nsecs_t eventTime, BitSet32 idBits, + const std::vector<VelocityTracker::Position>& positions) { uint32_t index = 0; for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) { uint32_t id = iterIdBits.clearFirstMarkedBit(); @@ -910,8 +879,9 @@ void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { mMovements[mIndex].idBits = remainingIdBits; } -void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions) { +void LegacyVelocityTrackerStrategy::addMovement( + nsecs_t eventTime, BitSet32 idBits, + const std::vector<VelocityTracker::Position>& positions) { if (++mIndex == HISTORY_SIZE) { mIndex = 0; } @@ -1024,8 +994,9 @@ void ImpulseVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { mMovements[mIndex].idBits = remainingIdBits; } -void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions) { +void ImpulseVelocityTrackerStrategy::addMovement( + nsecs_t eventTime, BitSet32 idBits, + const std::vector<VelocityTracker::Position>& positions) { if (mMovements[mIndex].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include diff --git a/libs/input/android/FocusRequest.aidl b/libs/input/android/FocusRequest.aidl new file mode 100644 index 0000000000..8812d34a72 --- /dev/null +++ b/libs/input/android/FocusRequest.aidl @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android; + +/** @hide */ +parcelable FocusRequest { + /** + * Input channel token used to identify the window that should gain focus. + */ + IBinder token; + @utf8InCpp String windowName; + /** + * The token that the caller expects currently to be focused. If the + * specified token does not match the currently focused window, this request will be dropped. + * If the specified focused token matches the currently focused window, the call will succeed. + * Set this to "null" if this call should succeed no matter what the currently focused token + * is. + */ + @nullable IBinder focusedToken; + @utf8InCpp String focusedWindowName; + /** + * SYSTEM_TIME_MONOTONIC timestamp in nanos set by the client (wm) when requesting the focus + * change. This determines which request gets precedence if there is a focus change request + * from another source such as pointer down. + */ + long timestamp; + /** + * Display id associated with this request. + */ + int displayId; +} diff --git a/libs/gui/GuiConfig.cpp b/libs/input/android/InputApplicationInfo.aidl index 3ec20ee0c0..933603916d 100644 --- a/libs/gui/GuiConfig.cpp +++ b/libs/input/android/InputApplicationInfo.aidl @@ -1,11 +1,11 @@ -/* - * Copyright (C) 2012 The Android Open Source Project +/** + * Copyright (c) 2020, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -14,18 +14,10 @@ * limitations under the License. */ -#include <gui/GuiConfig.h> +package android; -namespace android { - -void appendGuiConfigString(std::string& configStr) { - static const char* config = - " [libgui" -#ifdef DONT_USE_FENCE_SYNC - " DONT_USE_FENCE_SYNC" -#endif - "]"; - configStr.append(config); +parcelable InputApplicationInfo { + @nullable IBinder token; + @utf8InCpp String name; + long dispatchingTimeoutMillis; } - -}; // namespace android diff --git a/libs/input/android/InputChannel.aidl b/libs/input/android/InputChannel.aidl new file mode 100644 index 0000000000..c2d1112dd3 --- /dev/null +++ b/libs/input/android/InputChannel.aidl @@ -0,0 +1,20 @@ +/* //device/java/android/android/view/InputChannel.aidl +** +** Copyright 2020, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android; + +parcelable InputChannel cpp_header "input/InputTransport.h"; diff --git a/libs/input/android/InputWindowInfo.aidl b/libs/input/android/InputWindowInfo.aidl new file mode 100644 index 0000000000..eeaf400227 --- /dev/null +++ b/libs/input/android/InputWindowInfo.aidl @@ -0,0 +1,20 @@ +/* //device/java/android/android/view/InputChannel.aidl +** +** Copyright 2020, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android; + +parcelable InputWindowInfo cpp_header "input/InputWindow.h"; diff --git a/libs/ui/include/ui/PhysicalDisplayId.h b/libs/input/android/os/BlockUntrustedTouchesMode.aidl index 1a345acc86..9504e993f8 100644 --- a/libs/ui/include/ui/PhysicalDisplayId.h +++ b/libs/input/android/os/BlockUntrustedTouchesMode.aidl @@ -1,11 +1,11 @@ -/* - * Copyright 2019 The Android Open Source Project +/** + * Copyright (c) 2020, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -14,19 +14,22 @@ * limitations under the License. */ -#pragma once +package android.os; -#include <cinttypes> -#include <cstdint> -#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64 +/** + * Block untrusted touches feature mode. + * + * @hide + */ +@Backing(type="int") +enum BlockUntrustedTouchesMode { + /** Feature is off. */ + DISABLED, -namespace android { + /** Untrusted touches are flagged but not blocked. */ + PERMISSIVE, -using PhysicalDisplayId = uint64_t; - -constexpr uint8_t getPhysicalDisplayPort(PhysicalDisplayId displayId) { - return static_cast<uint8_t>(displayId); + /** Untrusted touches are blocked. */ + BLOCK } - -} // namespace android diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl new file mode 100644 index 0000000000..bce0ec8367 --- /dev/null +++ b/libs/input/android/os/IInputConstants.aidl @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + + +/** @hide */ +interface IInputConstants +{ + const int DEFAULT_DISPATCHING_TIMEOUT_MILLIS = 5000; // 5 seconds + + // Compatibility changes. + /** + * TODO(b/157929241): remove this before closing the bug. This is needed temporarily + * to identify apps that are using this flag. + */ + const long BLOCK_FLAG_SLIPPERY = 157929241; + + // Indicate invalid battery capacity + const int INVALID_BATTERY_CAPACITY = -1; + + /** + * Every input event has an id. This constant value is used when a valid input event id is not + * available. + */ + const int INVALID_INPUT_EVENT_ID = 0; +} diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl new file mode 100644 index 0000000000..1771d192af --- /dev/null +++ b/libs/input/android/os/IInputFlinger.aidl @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.FocusRequest; +import android.InputChannel; +import android.InputWindowInfo; +import android.os.ISetInputWindowsListener; + +/** @hide */ +interface IInputFlinger +{ + // SurfaceFlinger is the caller of this method, it uses the listener callback to ensure the + // ordering when needed. + // SurfaceFlinger calls this only every VSync, so overflow of binder's oneway buffer + // shouldn't be a concern. + oneway void setInputWindows(in InputWindowInfo[] inputHandles, + in @nullable ISetInputWindowsListener setInputWindowsListener); + InputChannel createInputChannel(in @utf8InCpp String name); + void removeInputChannel(in IBinder connectionToken); + /** + * Sets focus to the window identified by the token. This must be called + * after updating any input window handles. + */ + oneway void setFocusedWindow(in FocusRequest request); +} diff --git a/libs/input/android/os/ISetInputWindowsListener.aidl b/libs/input/android/os/ISetInputWindowsListener.aidl new file mode 100644 index 0000000000..bb58fb671b --- /dev/null +++ b/libs/input/android/os/ISetInputWindowsListener.aidl @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** @hide */ +oneway interface ISetInputWindowsListener +{ + void onSetInputWindowsFinished(); +} diff --git a/libs/input/android/os/InputEventInjectionResult.aidl b/libs/input/android/os/InputEventInjectionResult.aidl new file mode 100644 index 0000000000..34f10ec00e --- /dev/null +++ b/libs/input/android/os/InputEventInjectionResult.aidl @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** + * Constants used to report the outcome of input event injection. + * + * @hide + */ +@Backing(type="int") +enum InputEventInjectionResult { + /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */ + PENDING = -1, + + /* Injection succeeded. */ + SUCCEEDED = 0, + + /* Injection failed because the injector did not have permission to inject + * into the application with input focus. */ + PERMISSION_DENIED = 1, + + /* Injection failed because there were no available input targets. */ + FAILED = 2, + + /* Injection failed due to a timeout. */ + TIMED_OUT = 3, +} diff --git a/libs/input/android/os/InputEventInjectionSync.aidl b/libs/input/android/os/InputEventInjectionSync.aidl new file mode 100644 index 0000000000..95d24cb443 --- /dev/null +++ b/libs/input/android/os/InputEventInjectionSync.aidl @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** + * Constants used to specify the input event injection synchronization mode. + * + * @hide + */ +@Backing(type="int") +enum InputEventInjectionSync { + /* Injection is asynchronous and is assumed always to be successful. */ + NONE = 0, + + /* Waits for previous events to be dispatched so that the input dispatcher can determine + * whether input event injection willbe permitted based on the current input focus. + * Does not wait for the input event to finish processing. */ + WAIT_FOR_RESULT = 1, + + /* Waits for the input event to be completely processed. */ + WAIT_FOR_FINISHED = 2, +} diff --git a/libs/input/android/os/TouchOcclusionMode.aidl b/libs/input/android/os/TouchOcclusionMode.aidl new file mode 100644 index 0000000000..106f159a50 --- /dev/null +++ b/libs/input/android/os/TouchOcclusionMode.aidl @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + + +/** + * Touch occlusion modes: These modes represent how windows are taken into + * consideration in order to decide whether to block obscured touches or + * not. + * + * @hide + */ +@Backing(type="int") +enum TouchOcclusionMode { + /** + * Touches that pass through this window will be blocked if they are + * consumed by a different UID and this window is not trusted. + */ + BLOCK_UNTRUSTED, + + /** + * The window's opacity will be taken into consideration for touch + * occlusion rules if the touch passes through it and the window is not + * trusted. + */ + USE_OPACITY, + + /** + * The window won't count for touch occlusion rules if the touch passes + * through it. + */ + ALLOW +} diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index cacce923af..b23aaded78 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -1,16 +1,9 @@ // Build the unit tests. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_test { name: "libinput_tests", srcs: [ + "NamedEnum_test.cpp", + "Flags_test.cpp", "IdGenerator_test.cpp", "InputChannel_test.cpp", "InputDevice_test.cpp", @@ -27,14 +20,18 @@ cc_test { "-Wextra", "-Werror", ], - shared_libs: [ + static_libs: [ "libinput", - "libcutils", - "libutils", + ], + shared_libs: [ + "libbase", "libbinder", + "libcutils", + "liblog", "libui", - "libbase", - ] + "libutils", + ], + test_suites: ["device-tests"], } // NOTE: This is a compile time test, and does not need to be diff --git a/libs/input/tests/Flags_test.cpp b/libs/input/tests/Flags_test.cpp new file mode 100644 index 0000000000..0dbb4cfe32 --- /dev/null +++ b/libs/input/tests/Flags_test.cpp @@ -0,0 +1,222 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <input/Flags.h> + +#include <type_traits> + +namespace android::test { + +using namespace android::flag_operators; + +enum class TestFlags { ONE = 0x1, TWO = 0x2, THREE = 0x4 }; + +TEST(Flags, Test) { + Flags<TestFlags> flags = TestFlags::ONE; + ASSERT_TRUE(flags.test(TestFlags::ONE)); + ASSERT_FALSE(flags.test(TestFlags::TWO)); + ASSERT_FALSE(flags.test(TestFlags::THREE)); +} + +TEST(Flags, Any) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO; + ASSERT_TRUE(flags.any(TestFlags::ONE)); + ASSERT_TRUE(flags.any(TestFlags::TWO)); + ASSERT_FALSE(flags.any(TestFlags::THREE)); + ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO)); + ASSERT_TRUE(flags.any(TestFlags::TWO | TestFlags::THREE)); + ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::THREE)); + ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, All) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO; + ASSERT_TRUE(flags.all(TestFlags::ONE)); + ASSERT_TRUE(flags.all(TestFlags::TWO)); + ASSERT_FALSE(flags.all(TestFlags::THREE)); + ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::TWO)); + ASSERT_FALSE(flags.all(TestFlags::TWO | TestFlags::THREE)); + ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::THREE)); + ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, DefaultConstructor_hasNoFlagsSet) { + Flags<TestFlags> flags; + ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, NotOperator_onEmptyFlagsSetsAllFlags) { + Flags<TestFlags> flags; + flags = ~flags; + ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, NotOperator_onNonEmptyFlagsInvertsFlags) { + Flags<TestFlags> flags = TestFlags::TWO; + flags = ~flags; + ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::THREE)); + ASSERT_FALSE(flags.test(TestFlags::TWO)); +} + +TEST(Flags, OrOperator_withNewFlag) { + Flags<TestFlags> flags = TestFlags::ONE; + Flags<TestFlags> flags2 = flags | TestFlags::TWO; + ASSERT_FALSE(flags2.test(TestFlags::THREE)); + ASSERT_TRUE(flags2.all(TestFlags::ONE | TestFlags::TWO)); +} + +TEST(Flags, OrOperator_withExistingFlag) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE; + Flags<TestFlags> flags2 = flags | TestFlags::THREE; + ASSERT_FALSE(flags2.test(TestFlags::TWO)); + ASSERT_TRUE(flags2.all(TestFlags::ONE | TestFlags::THREE)); +} + +TEST(Flags, OrEqualsOperator_withNewFlag) { + Flags<TestFlags> flags; + flags |= TestFlags::THREE; + ASSERT_TRUE(flags.test(TestFlags::THREE)); + ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO)); +} + +TEST(Flags, OrEqualsOperator_withExistingFlag) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE; + flags |= TestFlags::THREE; + ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::THREE)); + ASSERT_FALSE(flags.test(TestFlags::TWO)); +} + +TEST(Flags, AndOperator_withOneSetFlag) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE; + Flags<TestFlags> andFlags = flags & TestFlags::THREE; + ASSERT_TRUE(andFlags.test(TestFlags::THREE)); + ASSERT_FALSE(andFlags.any(TestFlags::ONE | TestFlags::TWO)); +} + +TEST(Flags, AndOperator_withMultipleSetFlags) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE; + Flags<TestFlags> andFlags = flags & (TestFlags::ONE | TestFlags::THREE); + ASSERT_TRUE(andFlags.all(TestFlags::ONE | TestFlags::THREE)); + ASSERT_FALSE(andFlags.test(TestFlags::TWO)); +} + +TEST(Flags, AndOperator_withNoSetFlags) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE; + Flags<TestFlags> andFlags = flags & TestFlags::TWO; + ASSERT_FALSE(andFlags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, Equality) { + Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO; + Flags<TestFlags> flags2 = TestFlags::ONE | TestFlags::TWO; + ASSERT_EQ(flags1, flags2); +} + +TEST(Flags, Inequality) { + Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO; + Flags<TestFlags> flags2 = TestFlags::ONE | TestFlags::THREE; + ASSERT_NE(flags1, flags2); +} + +TEST(Flags, EqualsOperator) { + Flags<TestFlags> flags; + flags = TestFlags::ONE; + ASSERT_TRUE(flags.test(TestFlags::ONE)); + ASSERT_FALSE(flags.any(TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, EqualsOperator_DontShareState) { + Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO; + Flags<TestFlags> flags2 = flags1; + ASSERT_EQ(flags1, flags2); + + flags1 &= TestFlags::TWO; + ASSERT_NE(flags1, flags2); +} + +TEST(Flags, String_NoFlags) { + Flags<TestFlags> flags; + ASSERT_EQ(flags.string(), "0x0"); +} + +TEST(Flags, String_KnownValues) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO; + ASSERT_EQ(flags.string(), "ONE | TWO"); +} + +TEST(Flags, String_UnknownValues) { + auto flags = Flags<TestFlags>(0b1011); + ASSERT_EQ(flags.string(), "ONE | TWO | 0x00000008"); +} + +TEST(FlagsIterator, IteratesOverAllFlags) { + Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO; + Flags<TestFlags> flags2; + for (TestFlags f : flags1) { + flags2 |= f; + } + ASSERT_EQ(flags2, flags1); +} + +TEST(FlagsIterator, IteratesInExpectedOrder) { + const std::vector<TestFlags> flagOrder = {TestFlags::ONE, TestFlags::TWO}; + Flags<TestFlags> flags; + for (TestFlags f : flagOrder) { + flags |= f; + } + + size_t idx = 0; + auto iter = flags.begin(); + while (iter != flags.end() && idx < flagOrder.size()) { + // Make sure the order is what we expect + ASSERT_EQ(*iter, flagOrder[idx]); + iter++; + idx++; + } + ASSERT_EQ(iter, flags.end()); +} +TEST(FlagsIterator, PostFixIncrement) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO; + auto iter = flags.begin(); + ASSERT_EQ(*(iter++), TestFlags::ONE); + ASSERT_EQ(*iter, TestFlags::TWO); + ASSERT_EQ(*(iter++), TestFlags::TWO); + ASSERT_EQ(iter, flags.end()); +} + +TEST(FlagsIterator, PreFixIncrement) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO; + auto iter = flags.begin(); + ASSERT_EQ(*++iter, TestFlags::TWO); + ASSERT_EQ(++iter, flags.end()); +} + +TEST(FlagNames, RuntimeFlagName) { + TestFlags f = TestFlags::ONE; + ASSERT_EQ(flag_name(f), "ONE"); +} + +TEST(FlagNames, RuntimeUnknownFlagName) { + TestFlags f = static_cast<TestFlags>(0x8); + ASSERT_EQ(flag_name(f), std::nullopt); +} + +TEST(FlagNames, CompileTimeFlagName) { + static_assert(flag_name<TestFlags::TWO>() == "TWO"); +} + +} // namespace android::test
\ No newline at end of file diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp index ada275d014..0661261003 100644 --- a/libs/input/tests/InputChannel_test.cpp +++ b/libs/input/tests/InputChannel_test.cpp @@ -23,6 +23,7 @@ #include <errno.h> #include <binder/Binder.h> +#include <binder/Parcel.h> #include <gtest/gtest.h> #include <input/InputTransport.h> #include <utils/StopWatch.h> @@ -32,9 +33,6 @@ namespace android { class InputChannelTest : public testing::Test { -protected: - virtual void SetUp() { } - virtual void TearDown() { } }; @@ -46,7 +44,7 @@ TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptor android::base::unique_fd sendFd(pipe.sendFd); - sp<InputChannel> inputChannel = + std::unique_ptr<InputChannel> inputChannel = InputChannel::create("channel name", std::move(sendFd), new BBinder()); EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created"; @@ -61,14 +59,14 @@ TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptor TEST_F(InputChannelTest, SetAndGetToken) { Pipe pipe; sp<IBinder> token = new BBinder(); - sp<InputChannel> channel = + std::unique_ptr<InputChannel> channel = InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token); EXPECT_EQ(token, channel->getConnectionToken()); } TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { - sp<InputChannel> serverChannel, clientChannel; + std::unique_ptr<InputChannel> serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); @@ -102,7 +100,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { InputMessage clientReply; memset(&clientReply, 0, sizeof(InputMessage)); clientReply.header.type = InputMessage::Type::FINISHED; - clientReply.body.finished.seq = 0x11223344; + clientReply.header.seq = 0x11223344; clientReply.body.finished.handled = true; EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply)) << "client channel should be able to send message to server channel"; @@ -112,14 +110,14 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { << "server channel should be able to receive message from client channel"; EXPECT_EQ(clientReply.header.type, serverReply.header.type) << "server channel should receive the correct message from client channel"; - EXPECT_EQ(clientReply.body.finished.seq, serverReply.body.finished.seq) + EXPECT_EQ(clientReply.header.seq, serverReply.header.seq) << "server channel should receive the correct message from client channel"; EXPECT_EQ(clientReply.body.finished.handled, serverReply.body.finished.handled) << "server channel should receive the correct message from client channel"; } TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) { - sp<InputChannel> serverChannel, clientChannel; + std::unique_ptr<InputChannel> serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); @@ -133,7 +131,7 @@ TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) { } TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) { - sp<InputChannel> serverChannel, clientChannel; + std::unique_ptr<InputChannel> serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); @@ -141,7 +139,7 @@ TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) { ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; - serverChannel.clear(); // close server channel + serverChannel.reset(); // close server channel InputMessage msg; EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveMessage(&msg)) @@ -149,7 +147,7 @@ TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) { } TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) { - sp<InputChannel> serverChannel, clientChannel; + std::unique_ptr<InputChannel> serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); @@ -157,7 +155,7 @@ TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) { ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; - serverChannel.clear(); // close server channel + serverChannel.reset(); // close server channel InputMessage msg; msg.header.type = InputMessage::Type::KEY; @@ -166,7 +164,7 @@ TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) { } TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { - sp<InputChannel> serverChannel, clientChannel; + std::unique_ptr<InputChannel> serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); ASSERT_EQ(OK, result) @@ -180,7 +178,7 @@ TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { InputMessage serverMsg = {}, clientMsg; serverMsg.header.type = InputMessage::Type::MOTION; - serverMsg.body.motion.seq = 1; + serverMsg.header.seq = 1; serverMsg.body.motion.pointerCount = 1; for (MotionClassification classification : classifications) { @@ -197,5 +195,36 @@ TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { } } +TEST_F(InputChannelTest, InputChannelParcelAndUnparcel) { + std::unique_ptr<InputChannel> serverChannel, clientChannel; + + status_t result = + InputChannel::openInputChannelPair("channel parceling", serverChannel, clientChannel); + + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; + + InputChannel chan; + Parcel parcel; + ASSERT_EQ(OK, serverChannel->writeToParcel(&parcel)); + parcel.setDataPosition(0); + chan.readFromParcel(&parcel); + + EXPECT_EQ(chan == *serverChannel, true) + << "inputchannel should be equal after parceling and unparceling.\n" + << "name " << chan.getName() << " name " << serverChannel->getName(); +} + +TEST_F(InputChannelTest, DuplicateChannelAndAssertEqual) { + std::unique_ptr<InputChannel> serverChannel, clientChannel; + + status_t result = + InputChannel::openInputChannelPair("channel dup", serverChannel, clientChannel); + + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; + + std::unique_ptr<InputChannel> dupChan = serverChannel->dup(); + + EXPECT_EQ(*serverChannel == *dupChan, true) << "inputchannel should be equal after duplication"; +} } // namespace android diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp index c174ae94a6..f8f2f4e931 100644 --- a/libs/input/tests/InputDevice_test.cpp +++ b/libs/input/tests/InputDevice_test.cpp @@ -14,9 +14,12 @@ * limitations under the License. */ - +#include <binder/Binder.h> +#include <binder/Parcel.h> #include <gtest/gtest.h> #include <input/InputDevice.h> +#include <input/KeyLayoutMap.h> +#include <input/Keyboard.h> namespace android { @@ -31,4 +34,52 @@ TEST(InputDeviceIdentifierTest, getCanonicalName) { ASSERT_EQ(std::string("deviceName-123_version_C_"), identifier.getCanonicalName()); } -} // namespace android
\ No newline at end of file +class InputDeviceKeyMapTest : public testing::Test { +protected: + void loadKeyLayout(const char* name) { + std::string path = + getInputDeviceConfigurationFilePathByName(name, + InputDeviceConfigurationFileType:: + KEY_LAYOUT); + ASSERT_FALSE(path.empty()); + base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path); + ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << path; + mKeyMap.keyLayoutMap = std::move(*ret); + mKeyMap.keyLayoutFile = path; + } + + void loadKeyCharacterMap(const char* name) { + InputDeviceIdentifier identifier; + identifier.name = name; + std::string path = + getInputDeviceConfigurationFilePathByName(identifier.getCanonicalName(), + InputDeviceConfigurationFileType:: + KEY_CHARACTER_MAP); + ASSERT_FALSE(path.empty()) << "KeyCharacterMap for " << name << " not found"; + base::Result<std::shared_ptr<KeyCharacterMap>> ret = + KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE); + ASSERT_TRUE(ret.ok()) << "Cannot load KeyCharacterMap at " << path; + mKeyMap.keyCharacterMap = *ret; + mKeyMap.keyCharacterMapFile = path; + } + + virtual void SetUp() override { + loadKeyLayout("Generic"); + loadKeyCharacterMap("Generic"); + } + + virtual void TearDown() override {} + + KeyMap mKeyMap; +}; + +TEST_F(InputDeviceKeyMapTest, keyCharacterMapParcelingTest) { + Parcel parcel; + mKeyMap.keyCharacterMap->writeToParcel(&parcel); + parcel.setDataPosition(0); + std::shared_ptr<KeyCharacterMap> map = KeyCharacterMap::readFromParcel(&parcel); + // Verify the key character map is the same as original + ASSERT_EQ(*map, *mKeyMap.keyCharacterMap); +} + +} // namespace android diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 553dc4c068..601d8dabf7 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -17,6 +17,7 @@ #include <array> #include <math.h> +#include <attestation/HmacKeyManager.h> #include <binder/Parcel.h> #include <gtest/gtest.h> #include <input/Input.h> @@ -225,6 +226,7 @@ protected: static constexpr float Y_OFFSET = 1.1; int32_t mId; + ui::Transform mTransform; void initializeEventWithHistory(MotionEvent* event); void assertEqualsEventWithHistory(const MotionEvent* event); @@ -233,6 +235,7 @@ protected: void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { mId = InputEvent::nextId(); + mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1}); PointerProperties pointerProperties[2]; pointerProperties[0].clear(); @@ -266,7 +269,7 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, - MotionClassification::NONE, X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, + MotionClassification::NONE, mTransform, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); @@ -326,8 +329,7 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(AMETA_ALT_ON, event->getMetaState()); ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState()); ASSERT_EQ(MotionClassification::NONE, event->getClassification()); - EXPECT_EQ(X_SCALE, event->getXScale()); - EXPECT_EQ(Y_SCALE, event->getYScale()); + EXPECT_EQ(mTransform, event->getTransform()); ASSERT_EQ(X_OFFSET, event->getXOffset()); ASSERT_EQ(Y_OFFSET, event->getYOffset()); ASSERT_EQ(2.0f, event->getXPrecision()); @@ -545,7 +547,7 @@ TEST_F(MotionEventTest, Parcel) { ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&outEvent)); } -static void setRotationMatrix(float matrix[9], float angle) { +static void setRotationMatrix(std::array<float, 9>& matrix, float angle) { float sin = sinf(angle); float cos = cosf(angle); matrix[0] = cos; @@ -584,13 +586,14 @@ TEST_F(MotionEventTest, Transform) { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); } MotionEvent event; + ui::Transform identityTransform; event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, - MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/, - 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, - 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, 0 /*downTime*/, - 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); + MotionClassification::NONE, identityTransform, 0 /*xPrecision*/, + 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, + 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, + pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; @@ -606,7 +609,7 @@ TEST_F(MotionEventTest, Transform) { ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); // Apply a rotation about the origin by ROTATION degrees clockwise. - float matrix[9]; + std::array<float, 9> matrix; setRotationMatrix(matrix, ROTATION * PI_180); event.transform(matrix); @@ -648,11 +651,12 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { pointerCoords[i].clear(); } + ui::Transform identityTransform; for (MotionClassification classification : classifications) { event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, 1 /*xScale*/, - 1 /*yScale*/, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, + identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); @@ -670,10 +674,11 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { pointerCoords[i].clear(); } + ui::Transform identityTransform; event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, - AMETA_NONE, 0, MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, - 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, + AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0, + 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); event.offsetLocation(20, 60); ASSERT_EQ(280, event.getRawXCursorPosition()); diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 8e2eec85ed..e7e566dde6 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -20,53 +20,47 @@ #include <sys/mman.h> #include <time.h> +#include <attestation/HmacKeyManager.h> #include <cutils/ashmem.h> #include <gtest/gtest.h> #include <input/InputTransport.h> -#include <utils/Timers.h> #include <utils/StopWatch.h> +#include <utils/Timers.h> namespace android { class InputPublisherAndConsumerTest : public testing::Test { protected: - sp<InputChannel> serverChannel, clientChannel; - InputPublisher* mPublisher; - InputConsumer* mConsumer; + std::shared_ptr<InputChannel> mServerChannel, mClientChannel; + std::unique_ptr<InputPublisher> mPublisher; + std::unique_ptr<InputConsumer> mConsumer; PreallocatedInputEventFactory mEventFactory; - virtual void SetUp() { + void SetUp() override { + std::unique_ptr<InputChannel> serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); ASSERT_EQ(OK, result); + mServerChannel = std::move(serverChannel); + mClientChannel = std::move(clientChannel); - mPublisher = new InputPublisher(serverChannel); - mConsumer = new InputConsumer(clientChannel); - } - - virtual void TearDown() { - if (mPublisher) { - delete mPublisher; - mPublisher = nullptr; - } - - if (mConsumer) { - delete mConsumer; - mConsumer = nullptr; - } - - serverChannel.clear(); - clientChannel.clear(); + mPublisher = std::make_unique<InputPublisher>(mServerChannel); + mConsumer = std::make_unique<InputConsumer>(mClientChannel); } void PublishAndConsumeKeyEvent(); void PublishAndConsumeMotionEvent(); void PublishAndConsumeFocusEvent(); + void PublishAndConsumeCaptureEvent(); }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { - EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get()); - EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get()); + ASSERT_NE(nullptr, mPublisher->getChannel()); + ASSERT_NE(nullptr, mConsumer->getChannel()); + EXPECT_EQ(mServerChannel.get(), mPublisher->getChannel().get()); + EXPECT_EQ(mClientChannel.get(), mConsumer->getChannel().get()); + ASSERT_EQ(mPublisher->getChannel()->getConnectionToken(), + mConsumer->getChannel()->getConnectionToken()); } void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { @@ -88,6 +82,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { constexpr int32_t repeatCount = 1; constexpr nsecs_t downTime = 3; constexpr nsecs_t eventTime = 4; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); status = mPublisher->publishKeyEvent(seq, eventId, deviceId, source, displayId, hmac, action, flags, keyCode, scanCode, metaState, repeatCount, downTime, @@ -128,13 +123,22 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { uint32_t finishedSeq = 0; bool handled = false; - status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); + nsecs_t consumeTime; + status = mPublisher->receiveFinishedSignal( + [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled, + nsecs_t inConsumeTime) -> void { + finishedSeq = inSeq; + handled = inHandled; + consumeTime = inConsumeTime; + }); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; ASSERT_EQ(seq, finishedSeq) << "publisher receiveFinishedSignal should have returned the original sequence number"; ASSERT_TRUE(handled) << "publisher receiveFinishedSignal should have set handled to consumer's reply"; + ASSERT_GE(consumeTime, publishTime) + << "finished signal's consume time should be greater than publish time"; } void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { @@ -166,6 +170,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr nsecs_t downTime = 3; constexpr size_t pointerCount = 3; constexpr nsecs_t eventTime = 4; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; for (size_t i = 0; i < pointerCount; i++) { @@ -185,12 +190,13 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); } + ui::Transform transform; + transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1}); status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action, actionButton, flags, edgeFlags, metaState, buttonState, - classification, xScale, yScale, xOffset, yOffset, - xPrecision, yPrecision, xCursorPosition, - yCursorPosition, downTime, eventTime, pointerCount, - pointerProperties, pointerCoords); + classification, transform, xPrecision, yPrecision, + xCursorPosition, yCursorPosition, downTime, eventTime, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; @@ -218,8 +224,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(metaState, motionEvent->getMetaState()); EXPECT_EQ(buttonState, motionEvent->getButtonState()); EXPECT_EQ(classification, motionEvent->getClassification()); - EXPECT_EQ(xScale, motionEvent->getXScale()); - EXPECT_EQ(yScale, motionEvent->getYScale()); + EXPECT_EQ(transform, motionEvent->getTransform()); EXPECT_EQ(xOffset, motionEvent->getXOffset()); EXPECT_EQ(yOffset, motionEvent->getYOffset()); EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); @@ -268,13 +273,22 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { uint32_t finishedSeq = 0; bool handled = true; - status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); + nsecs_t consumeTime; + status = mPublisher->receiveFinishedSignal( + [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled, + nsecs_t inConsumeTime) -> void { + finishedSeq = inSeq; + handled = inHandled; + consumeTime = inConsumeTime; + }); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; ASSERT_EQ(seq, finishedSeq) << "publisher receiveFinishedSignal should have returned the original sequence number"; ASSERT_FALSE(handled) << "publisher receiveFinishedSignal should have set handled to consumer's reply"; + ASSERT_GE(consumeTime, publishTime) + << "finished signal's consume time should be greater than publish time"; } void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { @@ -284,6 +298,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { int32_t eventId = InputEvent::nextId(); constexpr bool hasFocus = true; constexpr bool inTouchMode = true; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; @@ -308,12 +323,68 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { uint32_t finishedSeq = 0; bool handled = false; - status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); + nsecs_t consumeTime; + status = mPublisher->receiveFinishedSignal( + [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled, + nsecs_t inConsumeTime) -> void { + finishedSeq = inSeq; + handled = inHandled; + consumeTime = inConsumeTime; + }); + ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; + ASSERT_EQ(seq, finishedSeq) + << "publisher receiveFinishedSignal should have returned the original sequence number"; + ASSERT_TRUE(handled) + << "publisher receiveFinishedSignal should have set handled to consumer's reply"; + ASSERT_GE(consumeTime, publishTime) + << "finished signal's consume time should be greater than publish time"; +} + +void InputPublisherAndConsumerTest::PublishAndConsumeCaptureEvent() { + status_t status; + + constexpr uint32_t seq = 42; + int32_t eventId = InputEvent::nextId(); + constexpr bool captureEnabled = true; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + + status = mPublisher->publishCaptureEvent(seq, eventId, captureEnabled); + ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; + + uint32_t consumeSeq; + InputEvent* event; + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); + ASSERT_EQ(OK, status) << "consumer consume should return OK"; + + ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; + ASSERT_EQ(AINPUT_EVENT_TYPE_CAPTURE, event->getType()) + << "consumer should have returned a capture event"; + + const CaptureEvent* captureEvent = static_cast<CaptureEvent*>(event); + EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(eventId, captureEvent->getId()); + EXPECT_EQ(captureEnabled, captureEvent->getPointerCaptureEnabled()); + + status = mConsumer->sendFinishedSignal(seq, true); + ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; + + uint32_t finishedSeq = 0; + bool handled = false; + nsecs_t consumeTime; + status = mPublisher->receiveFinishedSignal( + [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled, + nsecs_t inConsumeTime) -> void { + finishedSeq = inSeq; + handled = inHandled; + consumeTime = inConsumeTime; + }); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; ASSERT_EQ(seq, finishedSeq) << "publisher receiveFinishedSignal should have returned the original sequence number"; ASSERT_TRUE(handled) << "publisher receiveFinishedSignal should have set handled to consumer's reply"; + ASSERT_GE(consumeTime, publishTime) + << "finished signal's consume time should be greater than publish time"; } TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) { @@ -328,6 +399,10 @@ TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent()); } +TEST_F(InputPublisherAndConsumerTest, PublishCaptureEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent()); +} + TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) { status_t status; const size_t pointerCount = 1; @@ -338,10 +413,10 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer pointerCoords[i].clear(); } + ui::Transform identityTransform; status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, - 0, 0, 0, MotionClassification::NONE, 1 /* xScale */, - 1 /* yScale */, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, + 0, 0, 0, MotionClassification::NONE, identityTransform, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) @@ -354,10 +429,10 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; + ui::Transform identityTransform; status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, - 0, 0, 0, MotionClassification::NONE, 1 /* xScale */, - 1 /* yScale */, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, + 0, 0, 0, MotionClassification::NONE, identityTransform, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) @@ -375,10 +450,10 @@ TEST_F(InputPublisherAndConsumerTest, pointerCoords[i].clear(); } + ui::Transform identityTransform; status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, - 0, 0, 0, MotionClassification::NONE, 1 /* xScale */, - 1 /* yScale */, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, + 0, 0, 0, MotionClassification::NONE, identityTransform, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) @@ -392,6 +467,9 @@ TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); } } // namespace android diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp index d1cb527a57..c18a17f1ae 100644 --- a/libs/input/tests/InputWindow_test.cpp +++ b/libs/input/tests/InputWindow_test.cpp @@ -22,17 +22,19 @@ #include <input/InputWindow.h> #include <input/InputTransport.h> +using std::chrono_literals::operator""s; + namespace android { namespace test { TEST(InputWindowInfo, ParcellingWithoutToken) { - InputWindowInfo i; + InputWindowInfo i, i2; i.token = nullptr; Parcel p; - ASSERT_EQ(OK, i.write(p)); + ASSERT_EQ(OK, i.writeToParcel(&p)); p.setDataPosition(0); - InputWindowInfo i2 = InputWindowInfo::read(p); + i2.readFromParcel(&p); ASSERT_TRUE(i2.token == nullptr); } @@ -42,40 +44,44 @@ TEST(InputWindowInfo, Parcelling) { i.token = new BBinder(); i.id = 1; i.name = "Foobar"; - i.layoutParamsFlags = 7; - i.layoutParamsType = 39; - i.dispatchingTimeout = 12; + i.flags = InputWindowInfo::Flag::SLIPPERY; + i.type = InputWindowInfo::Type::INPUT_METHOD; + i.dispatchingTimeout = 12s; i.frameLeft = 93; i.frameTop = 34; i.frameRight = 16; i.frameBottom = 19; i.surfaceInset = 17; i.globalScaleFactor = 0.3; - i.windowXScale = 0.4; - i.windowYScale = 0.5; + i.alpha = 0.7; + i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1}); i.visible = false; - i.canReceiveKeys = false; - i.hasFocus = false; + i.focusable = false; i.hasWallpaper = false; i.paused = false; + i.touchOcclusionMode = TouchOcclusionMode::ALLOW; i.ownerPid = 19; i.ownerUid = 24; - i.inputFeatures = 29; + i.packageName = "com.example.package"; + i.inputFeatures = InputWindowInfo::Feature::DISABLE_USER_ACTIVITY; i.displayId = 34; i.portalToDisplayId = 2; i.replaceTouchableRegionWithCrop = true; i.touchableRegionCropHandle = touchableRegionCropHandle; + i.applicationInfo.name = "ApplicationFooBar"; + i.applicationInfo.token = new BBinder(); + i.applicationInfo.dispatchingTimeoutMillis = 0x12345678ABCD; Parcel p; - i.write(p); - + i.writeToParcel(&p); p.setDataPosition(0); - InputWindowInfo i2 = InputWindowInfo::read(p); + InputWindowInfo i2; + i2.readFromParcel(&p); ASSERT_EQ(i.token, i2.token); ASSERT_EQ(i.id, i2.id); ASSERT_EQ(i.name, i2.name); - ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags); - ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType); + ASSERT_EQ(i.flags, i2.flags); + ASSERT_EQ(i.type, i2.type); ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout); ASSERT_EQ(i.frameLeft, i2.frameLeft); ASSERT_EQ(i.frameTop, i2.frameTop); @@ -83,20 +89,36 @@ TEST(InputWindowInfo, Parcelling) { ASSERT_EQ(i.frameBottom, i2.frameBottom); ASSERT_EQ(i.surfaceInset, i2.surfaceInset); ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor); - ASSERT_EQ(i.windowXScale, i2.windowXScale); - ASSERT_EQ(i.windowYScale, i2.windowYScale); + ASSERT_EQ(i.alpha, i2.alpha); + ASSERT_EQ(i.transform, i2.transform); ASSERT_EQ(i.visible, i2.visible); - ASSERT_EQ(i.canReceiveKeys, i2.canReceiveKeys); - ASSERT_EQ(i.hasFocus, i2.hasFocus); + ASSERT_EQ(i.focusable, i2.focusable); ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper); ASSERT_EQ(i.paused, i2.paused); + ASSERT_EQ(i.touchOcclusionMode, i2.touchOcclusionMode); ASSERT_EQ(i.ownerPid, i2.ownerPid); ASSERT_EQ(i.ownerUid, i2.ownerUid); + ASSERT_EQ(i.packageName, i2.packageName); ASSERT_EQ(i.inputFeatures, i2.inputFeatures); ASSERT_EQ(i.displayId, i2.displayId); ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId); ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop); ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle); + ASSERT_EQ(i.applicationInfo, i2.applicationInfo); +} + +TEST(InputApplicationInfo, Parcelling) { + InputApplicationInfo i; + i.token = new BBinder(); + i.name = "ApplicationFooBar"; + i.dispatchingTimeoutMillis = 0x12345678ABCD; + + Parcel p; + ASSERT_EQ(i.writeToParcel(&p), OK); + p.setDataPosition(0); + InputApplicationInfo i2; + ASSERT_EQ(i2.readFromParcel(&p), OK); + ASSERT_EQ(i, i2); } } // namespace test diff --git a/libs/input/tests/NamedEnum_test.cpp b/libs/input/tests/NamedEnum_test.cpp new file mode 100644 index 0000000000..74a0044387 --- /dev/null +++ b/libs/input/tests/NamedEnum_test.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <input/NamedEnum.h> + +namespace android { + +// Test enum class maximum enum value smaller than default maximum of 8. +enum class TestEnums { ZERO = 0x0, ONE = 0x1, TWO = 0x2, THREE = 0x3, SEVEN = 0x7 }; +// Big enum contains enum values greater than default maximum of 8. +enum class TestBigEnums { ZERO = 0x0, FIFTEEN = 0xF }; + +// Declared to specialize the maximum enum since the enum size exceeds 8 by default. +template <> +constexpr size_t NamedEnum::max<TestBigEnums> = 16; + +namespace test { +using android::TestBigEnums; +using android::TestEnums; + +TEST(NamedEnum, RuntimeNamedEnum) { + TestEnums e = TestEnums::ZERO; + ASSERT_EQ(NamedEnum::enum_name(e), "ZERO"); + + e = TestEnums::ONE; + ASSERT_EQ(NamedEnum::enum_name(e), "ONE"); + + e = TestEnums::THREE; + ASSERT_EQ(NamedEnum::enum_name(e), "THREE"); + + e = TestEnums::SEVEN; + ASSERT_EQ(NamedEnum::enum_name(e), "SEVEN"); +} + +// Test big enum +TEST(NamedEnum, RuntimeBigNamedEnum) { + TestBigEnums e = TestBigEnums::ZERO; + ASSERT_EQ(NamedEnum::enum_name(e), "ZERO"); + + e = TestBigEnums::FIFTEEN; + ASSERT_EQ(NamedEnum::enum_name(e), "FIFTEEN"); +} + +TEST(NamedEnum, RuntimeNamedEnumAsString) { + TestEnums e = TestEnums::ZERO; + ASSERT_EQ(NamedEnum::string(e), "ZERO"); + + e = TestEnums::ONE; + ASSERT_EQ(NamedEnum::string(e), "ONE"); + + e = TestEnums::THREE; + ASSERT_EQ(NamedEnum::string(e), "THREE"); + + e = TestEnums::SEVEN; + ASSERT_EQ(NamedEnum::string(e), "SEVEN"); +} + +TEST(NamedEnum, RuntimeBigNamedEnumAsString) { + TestBigEnums e = TestBigEnums::ZERO; + ASSERT_EQ(NamedEnum::string(e), "ZERO"); + + e = TestBigEnums::FIFTEEN; + ASSERT_EQ(NamedEnum::string(e), "FIFTEEN"); +} + +TEST(NamedEnum, RuntimeUnknownNamedEnum) { + TestEnums e = static_cast<TestEnums>(0x5); + ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt); + e = static_cast<TestEnums>(0x9); + ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt); +} + +TEST(NamedEnum, RuntimeUnknownNamedEnumAsString) { + TestEnums e = static_cast<TestEnums>(0x5); + ASSERT_EQ(NamedEnum::string(e), "05"); + e = static_cast<TestEnums>(0x9); + ASSERT_EQ(NamedEnum::string(e, "0x%08x"), "0x00000009"); +} + +TEST(NamedEnum, CompileTimeFlagName) { + static_assert(NamedEnum::enum_name<TestEnums::TWO>() == "TWO"); + static_assert(NamedEnum::enum_name<TestEnums::THREE>() == "THREE"); +} + +} // namespace test + +} // namespace android diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 1fe7bb90ca..a8865858d3 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -34,8 +34,7 @@ void TestPointerCoordsAlignment() { void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage, body, 8); - CHECK_OFFSET(InputMessage::Body::Key, seq, 0); - CHECK_OFFSET(InputMessage::Body::Key, eventId, 4); + CHECK_OFFSET(InputMessage::Body::Key, eventId, 0); CHECK_OFFSET(InputMessage::Body::Key, eventTime, 8); CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Key, source, 20); @@ -49,8 +48,7 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 80); CHECK_OFFSET(InputMessage::Body::Key, downTime, 88); - CHECK_OFFSET(InputMessage::Body::Motion, seq, 0); - CHECK_OFFSET(InputMessage::Body::Motion, eventId, 4); + CHECK_OFFSET(InputMessage::Body::Motion, eventId, 0); CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8); CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Motion, source, 20); @@ -64,27 +62,33 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, classification, 80); CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 84); CHECK_OFFSET(InputMessage::Body::Motion, downTime, 88); - CHECK_OFFSET(InputMessage::Body::Motion, xScale, 96); - CHECK_OFFSET(InputMessage::Body::Motion, yScale, 100); - CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 104); - CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 108); - CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 112); - CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 116); - CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 120); - CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 124); - CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 128); - CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136); - - CHECK_OFFSET(InputMessage::Body::Focus, seq, 0); - CHECK_OFFSET(InputMessage::Body::Focus, eventId, 4); - CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 12); - CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 14); - - CHECK_OFFSET(InputMessage::Body::Finished, seq, 0); + CHECK_OFFSET(InputMessage::Body::Motion, dsdx, 96); + CHECK_OFFSET(InputMessage::Body::Motion, dtdx, 100); + CHECK_OFFSET(InputMessage::Body::Motion, dtdy, 104); + CHECK_OFFSET(InputMessage::Body::Motion, dsdy, 108); + CHECK_OFFSET(InputMessage::Body::Motion, tx, 112); + CHECK_OFFSET(InputMessage::Body::Motion, ty, 116); + CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 120); + CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124); + CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128); + CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132); + CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 136); + CHECK_OFFSET(InputMessage::Body::Motion, pointers, 144); + + CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0); + CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); + CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6); + + CHECK_OFFSET(InputMessage::Body::Capture, eventId, 0); + CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4); + CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); + CHECK_OFFSET(InputMessage::Body::Finished, consumeTime, 8); } void TestHeaderSize() { + CHECK_OFFSET(InputMessage::Header, type, 0); + CHECK_OFFSET(InputMessage::Header, seq, 4); static_assert(sizeof(InputMessage::Header) == 8); } @@ -97,8 +101,9 @@ void TestBodySize() { static_assert(sizeof(InputMessage::Body::Motion) == offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); - static_assert(sizeof(InputMessage::Body::Finished) == 8); - static_assert(sizeof(InputMessage::Body::Focus) == 16); + static_assert(sizeof(InputMessage::Body::Finished) == 16); + static_assert(sizeof(InputMessage::Body::Focus) == 8); + static_assert(sizeof(InputMessage::Body::Capture) == 8); } // --- VerifiedInputEvent --- diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index bf452c07a3..d049d05ac5 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -21,6 +21,7 @@ #include <math.h> #include <android-base/stringprintf.h> +#include <attestation/HmacKeyManager.h> #include <gtest/gtest.h> #include <input/VelocityTracker.h> @@ -176,12 +177,12 @@ static std::vector<MotionEvent> createMotionEventStream( EXPECT_EQ(pointerIndex, pointerCount); MotionEvent event; + ui::Transform identityTransform; event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, action, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, - MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/, - 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, - AMOTION_EVENT_INVALID_CURSOR_POSITION, + MotionClassification::NONE, identityTransform, 0 /*xPrecision*/, + 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); @@ -191,8 +192,9 @@ static std::vector<MotionEvent> createMotionEventStream( return events; } -static void computeAndCheckVelocity(const char* strategy, - const std::vector<MotionEventEntry>& motions, int32_t axis, float targetVelocity) { +static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy, + const std::vector<MotionEventEntry>& motions, int32_t axis, + float targetVelocity) { VelocityTracker vt(strategy); float Vx, Vy; @@ -217,7 +219,7 @@ static void computeAndCheckVelocity(const char* strategy, static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions, const std::array<float, 3>& coefficients) { - VelocityTracker vt("lsq2"); + VelocityTracker vt(VelocityTracker::Strategy::LSQ2); std::vector<MotionEvent> events = createMotionEventStream(motions); for (MotionEvent event : events) { vt.addMovement(&event); @@ -238,36 +240,34 @@ TEST_F(VelocityTrackerTest, ThreePointsPositiveVelocityTest) { // It is difficult to determine the correct answer here, but at least the direction // of the reported velocity should be positive. std::vector<MotionEventEntry> motions = { - {0ms, {{ 273, NAN}}}, - {12585us, {{293, NAN}}}, - {14730us, {{293, NAN}}}, - {14730us, {{293, NAN}}}, // ACTION_UP + {0ms, {{273, 0}}}, + {12585us, {{293, 0}}}, + {14730us, {{293, 0}}}, + {14730us, {{293, 0}}}, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1600); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 1600); } TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) { // Same coordinate is reported 3 times in a row std::vector<MotionEventEntry> motions = { - { 0ms, {{293, NAN}} }, - { 6132us, {{293, NAN}} }, - { 11283us, {{293, NAN}} }, - { 11283us, {{293, NAN}} }, // ACTION_UP + {0ms, {{293, 0}}}, + {6132us, {{293, 0}}}, + {11283us, {{293, 0}}}, + {11283us, {{293, 0}}}, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 0); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0); } TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) { // Fixed velocity at 5 points per 10 milliseconds std::vector<MotionEventEntry> motions = { - { 0ms, {{0, NAN}} }, - { 10ms, {{5, NAN}} }, - { 20ms, {{10, NAN}} }, - { 20ms, {{10, NAN}} }, // ACTION_UP + {0ms, {{0, 0}}}, {10ms, {{5, 0}}}, {20ms, {{10, 0}}}, {20ms, {{10, 0}}}, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 500); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 500); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 500); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 500); } @@ -297,8 +297,10 @@ TEST_F(VelocityTrackerTest, SwordfishFlingDown) { { 96948871ns, {{274.79245, 428.113159}} }, { 96948871ns, {{274.79245, 428.113159}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 623.577637); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 5970.7309); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 623.577637); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 5970.7309); } // --------------- Recorded by hand on sailfish, generated by a script ----------------------------- @@ -339,10 +341,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) { { 235089162955851ns, {{560.66, 843.82}} }, { 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 872.794617); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 951.698181); - computeAndCheckVelocity("impulse",motions, AMOTION_EVENT_AXIS_Y, -3604.819336); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3044.966064); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 872.794617); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + 951.698181); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -3604.819336); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -3044.966064); } @@ -368,8 +374,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow2) { { 235110660368000ns, {{530.00, 980.00}} }, { 235110660368000ns, {{530.00, 980.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4096.583008); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3455.094238); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -4096.583008); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -3455.094238); } @@ -396,10 +404,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow3) { { 792629200000ns, {{619.00, 1115.00}} }, { 792629200000ns, {{619.00, 1115.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 574.33429); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 617.40564); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -2361.982666); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -2500.055664); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 574.33429); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + 617.40564); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -2361.982666); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -2500.055664); } @@ -426,10 +438,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFaster1) { { 235160520366000ns, {{679.00, 814.00}} }, { 235160520366000ns, {{679.00, 814.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1274.141724); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 1438.53186); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -3001.4348); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3695.859619); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 1274.141724); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + 1438.53186); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -3001.4348); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -3695.859619); } @@ -452,8 +468,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFaster2) { { 847237986000ns, {{610.00, 1095.00}} }, { 847237986000ns, {{610.00, 1095.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4280.07959); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -4241.004395); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -4280.07959); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -4241.004395); } @@ -476,8 +494,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFaster3) { { 235200616933000ns, {{590.00, 844.00}} }, { 235200616933000ns, {{590.00, 844.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -8715.686523); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -7639.026367); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -8715.686523); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -7639.026367); } @@ -499,10 +519,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFast1) { { 920989261000ns, {{715.00, 903.00}} }, { 920989261000ns, {{715.00, 903.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 5670.329102); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 5991.866699); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -13021.101562); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -15093.995117); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 5670.329102); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + 5991.866699); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -13021.101562); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -15093.995117); } @@ -522,8 +546,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFast2) { { 235247220736000ns, {{620.00, 641.00}} }, { 235247220736000ns, {{620.00, 641.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -20286.958984); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -20494.587891); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -20286.958984); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -20494.587891); } @@ -541,8 +567,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFast3) { { 235302613019881ns, {{679.26, 526.73}} }, { 235302613019881ns, {{679.26, 526.73}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -39295.941406); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -36461.421875); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -39295.941406); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -36461.421875); } @@ -569,10 +597,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownSlow1) { { 235655842893000ns, {{563.00, 649.00}} }, { 235655842893000ns, {{563.00, 649.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -419.749695); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -398.303894); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3309.016357); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 3969.099854); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + -419.749695); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + -398.303894); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 3309.016357); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 3969.099854); } @@ -599,10 +631,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownSlow2) { { 235671246532000ns, {{470.00, 799.00}} }, { 235671246532000ns, {{470.00, 799.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -262.80426); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -243.665344); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4215.682129); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4587.986816); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + -262.80426); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + -243.665344); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 4215.682129); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 4587.986816); } @@ -622,10 +658,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownSlow3) { { 171051052000ns, {{536.00, 586.00}} }, { 171051052000ns, {{536.00, 586.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -723.413513); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -651.038452); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 2091.502441); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 1934.517456); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + -723.413513); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + -651.038452); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 2091.502441); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 1934.517456); } @@ -652,8 +692,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFaster1) { { 235695373403000ns, {{564.00, 744.00}} }, { 235695373403000ns, {{564.00, 744.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4254.639648); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4698.415039); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 4254.639648); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 4698.415039); } @@ -677,10 +719,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFaster2) { { 235709710626776ns, {{511.72, 741.85}} }, { 235709710626776ns, {{511.72, 741.85}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -430.440247); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -447.600311); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3953.859375); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4316.155273); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + -430.440247); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + -447.600311); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 3953.859375); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 4316.155273); } @@ -706,8 +752,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFaster3) { { 235727721580000ns, {{516.00, 658.00}} }, { 235727721580000ns, {{516.00, 658.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4484.617676); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4927.92627); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 4484.617676); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 4927.92627); } @@ -725,8 +773,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast1) { { 235762396429369ns, {{404.37, 680.67}} }, { 235762396429369ns, {{404.37, 680.67}} }, //ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 14227.0224); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16064.685547); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 14227.0224); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 16064.685547); } @@ -744,8 +794,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast2) { { 235772537635000ns, {{484.00, 589.00}} }, { 235772537635000ns, {{484.00, 589.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 18660.048828); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16918.439453); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 18660.048828); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 16918.439453); } @@ -764,10 +816,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) { { 507703352649ns, {{443.71, 857.77}} }, { 507703352649ns, {{443.71, 857.77}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -4111.8173); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -6388.48877); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 29765.908203); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 28354.796875); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + -4111.8173); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + -6388.48877); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 29765.908203); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 28354.796875); } /** @@ -789,10 +845,10 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_ThreeFi // Velocity should actually be zero, but we expect 0.016 here instead. // This is close enough to zero, and is likely caused by division by a very small number. - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -0.016); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -0.016); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 0); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, -0.016); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, -0.016); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, 0); } /** diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp index 4e8e840d1c..36f87b8a6a 100644 --- a/libs/input/tests/VerifiedInputEvent_test.cpp +++ b/libs/input/tests/VerifiedInputEvent_test.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <attestation/HmacKeyManager.h> #include <gtest/gtest.h> #include <input/Input.h> @@ -39,13 +40,14 @@ static MotionEvent getMotionEventWithFlags(int32_t flags) { pointerCoords[i].clear(); } + ui::Transform transform; + transform.set({2, 0, 4, 0, 3, 5, 0, 0, 1}); event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, - MotionClassification::NONE, 2 /*xScale*/, 3 /*yScale*/, 4 /*xOffset*/, - 5 /*yOffset*/, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, 280 /*xCursorPosition*/, - 540 /*yCursorPosition*/, 100 /*downTime*/, 200 /*eventTime*/, pointerCount, - pointerProperties, pointerCoords); + MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, + 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 100 /*downTime*/, + 200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); return event; } diff --git a/libs/math/Android.bp b/libs/math/Android.bp index 55955870e3..22d471204a 100644 --- a/libs/math/Android.bp +++ b/libs/math/Android.bp @@ -12,23 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_applicable_licenses: ["frameworks_native_libs_math_license"], -} - -// Added automatically by a large-scale-change -// See: http://go/android-license-faq -license { - name: "frameworks_native_libs_math_license", - visibility: [":__subpackages__"], - license_kinds: [ - "SPDX-license-identifier-Apache-2.0", - ], - license_text: [ - "NOTICE", - ], -} - cc_library_static { name: "libmath", host_supported: true, @@ -42,6 +25,11 @@ cc_library_static { min_sdk_version: "29", export_include_dirs: ["include"], + target: { + windows: { + enabled: true, + } + } } subdirs = ["tests"] diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h index 76829734a4..617a0ab5d2 100644 --- a/libs/math/include/math/half.h +++ b/libs/math/include/math/half.h @@ -82,6 +82,7 @@ class half { }; public: + CONSTEXPR half() noexcept { } CONSTEXPR half(float v) noexcept : mBits(ftoh(v)) { } CONSTEXPR operator float() const noexcept { return htof(mBits); } diff --git a/libs/math/tests/Android.bp b/libs/math/tests/Android.bp index 4a7c4dd8a4..0184f56dc4 100644 --- a/libs/math/tests/Android.bp +++ b/libs/math/tests/Android.bp @@ -14,15 +14,6 @@ // limitations under the License. // -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_libs_math_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_libs_math_license"], -} - cc_test { name: "vec_test", srcs: ["vec_test.cpp"], diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp index 496a7ef56d..604072e557 100644 --- a/libs/math/tests/half_test.cpp +++ b/libs/math/tests/half_test.cpp @@ -35,6 +35,7 @@ TEST_F(HalfTest, Basics) { EXPECT_EQ(2UL, sizeof(half)); // test +/- zero + EXPECT_EQ(0x0000, half().getBits()); EXPECT_EQ(0x0000, half( 0.0f).getBits()); EXPECT_EQ(0x8000, half(-0.0f).getBits()); diff --git a/libs/nativebase/Android.bp b/libs/nativebase/Android.bp index 1a4729c610..8399e8ce0a 100644 --- a/libs/nativebase/Android.bp +++ b/libs/nativebase/Android.bp @@ -12,23 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_applicable_licenses: ["frameworks_native_libs_nativebase_license"], -} - -// Added automatically by a large-scale-change -// See: http://go/android-license-faq -license { - name: "frameworks_native_libs_nativebase_license", - visibility: [":__subpackages__"], - license_kinds: [ - "SPDX-license-identifier-Apache-2.0", - ], - license_text: [ - "NOTICE", - ], -} - cc_library_headers { name: "libnativebase_headers", vendor_available: true, diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index b431cbb6c0..dc2dd297c5 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -21,7 +21,7 @@ #include <gui/DisplayEventDispatcher.h> #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> -#include <nativehelper/JNIHelp.h> +#include <jni.h> #include <private/android/choreographer.h> #include <utils/Looper.h> #include <utils/Timers.h> @@ -128,15 +128,21 @@ public: static Choreographer* getForThread(); virtual ~Choreographer() override EXCLUDES(gChoreographers.lock); + int64_t getVsyncId() const; + int64_t getFrameDeadline() const; + private: Choreographer(const Choreographer&) = delete; - void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override; + void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, + VsyncEventData vsyncEventData) override; void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; - void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId, - nsecs_t vsyncPeriod) override; + void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId, + nsecs_t vsyncPeriod) override; void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override; + void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, + std::vector<FrameRateOverride> overrides) override; void scheduleCallbacks(); @@ -146,6 +152,7 @@ private: std::vector<RefreshRateCallback> mRefreshRateCallbacks; nsecs_t mLatestVsyncPeriod = -1; + VsyncEventData mLastVsyncEventData; const sp<Looper> mLooper; const std::thread::id mThreadId; @@ -170,8 +177,7 @@ Choreographer* Choreographer::getForThread() { } Choreographer::Choreographer(const sp<Looper>& looper) - : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp, - ISurfaceComposer::ConfigChanged::eConfigChangedSuppress), + : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp), mLooper(looper), mThreadId(std::this_thread::get_id()) { std::lock_guard<std::mutex> _l(gChoreographers.lock); @@ -350,7 +356,8 @@ void Choreographer::handleRefreshRateUpdates() { // TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the // internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for // the internal display implicitly. -void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) { +void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, + VsyncEventData vsyncEventData) { std::vector<FrameCallback> callbacks{}; { std::lock_guard<std::mutex> _l{mLock}; @@ -360,6 +367,7 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t mFrameCallbacks.pop(); } } + mLastVsyncEventData = vsyncEventData; for (const auto& cb : callbacks) { if (cb.callback64 != nullptr) { cb.callback64(timestamp, cb.data); @@ -370,21 +378,17 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t } void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) { - ALOGV("choreographer %p ~ received hotplug event (displayId=%" - ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.", - this, displayId, toString(connected)); + ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", + this, to_string(displayId).c_str(), toString(connected)); +} + +void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) { + LOG_ALWAYS_FATAL("dispatchModeChanged was called but was never registered"); } -// TODO(b/74619554): The PhysicalDisplayId is ignored because currently -// Choreographer only supports dispatching VSYNC events for the internal -// display, so as such Choreographer does not support the notion of multiple -// displays. When multi-display choreographer is properly supported, then -// PhysicalDisplayId should no longer be ignored. -void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, int32_t configId, - nsecs_t) { - ALOGV("choreographer %p ~ received config change event " - "(displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%d).", - this, displayId, configId); +void Choreographer::dispatchFrameRateOverrides(nsecs_t, PhysicalDisplayId, + std::vector<FrameRateOverride>) { + LOG_ALWAYS_FATAL("dispatchFrameRateOverrides was called but was never registered"); } void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) { @@ -406,6 +410,14 @@ void Choreographer::handleMessage(const Message& message) { } } +int64_t Choreographer::getVsyncId() const { + return mLastVsyncEventData.id; +} + +int64_t Choreographer::getFrameDeadline() const { + return mLastVsyncEventData.deadlineTimestamp; +} + } // namespace android using namespace android; @@ -413,6 +425,11 @@ static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* cho return reinterpret_cast<Choreographer*>(choreographer); } +static inline const Choreographer* AChoreographer_to_Choreographer( + const AChoreographer* choreographer) { + return reinterpret_cast<const Choreographer*>(choreographer); +} + // Glue for private C api namespace android { void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) { @@ -476,15 +493,18 @@ void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreogra return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data); } +int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer) { + return AChoreographer_to_Choreographer(choreographer)->getVsyncId(); +} + +int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer) { + return AChoreographer_to_Choreographer(choreographer)->getFrameDeadline(); +} + } // namespace android /* Glue for the NDK interface */ -static inline const Choreographer* AChoreographer_to_Choreographer( - const AChoreographer* choreographer) { - return reinterpret_cast<const Choreographer*>(choreographer); -} - static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) { return reinterpret_cast<AChoreographer*>(choreographer); } diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp index 277635cd34..c595aa6309 100644 --- a/libs/nativedisplay/ADisplay.cpp +++ b/libs/nativedisplay/ADisplay.cpp @@ -16,8 +16,8 @@ #include <apex/display.h> #include <gui/SurfaceComposerClient.h> -#include <ui/DisplayConfig.h> #include <ui/DisplayInfo.h> +#include <ui/DisplayMode.h> #include <ui/GraphicTypes.h> #include <ui/PixelFormat.h> @@ -134,8 +134,8 @@ int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { return NO_INIT; } - std::vector<DisplayConfigImpl> configsPerDisplay[size]; - int numConfigs = 0; + std::vector<DisplayConfigImpl> modesPerDisplay[size]; + int numModes = 0; for (int i = 0; i < size; ++i) { const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]); @@ -145,23 +145,23 @@ int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { return status; } - Vector<DisplayConfig> configs; - if (const status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs); + Vector<ui::DisplayMode> modes; + if (const status_t status = SurfaceComposerClient::getDisplayModes(token, &modes); status != OK) { return status; } - if (configs.empty()) { + if (modes.empty()) { return NO_INIT; } - numConfigs += configs.size(); - configsPerDisplay[i].reserve(configs.size()); - for (int j = 0; j < configs.size(); ++j) { - const DisplayConfig& config = configs[j]; - configsPerDisplay[i].emplace_back( - DisplayConfigImpl{config.resolution.getWidth(), config.resolution.getHeight(), - info.density, config.refreshRate, config.sfVsyncOffset, - config.appVsyncOffset}); + numModes += modes.size(); + modesPerDisplay[i].reserve(modes.size()); + for (int j = 0; j < modes.size(); ++j) { + const ui::DisplayMode& mode = modes[j]; + modesPerDisplay[i].emplace_back( + DisplayConfigImpl{mode.resolution.getWidth(), mode.resolution.getHeight(), + info.density, mode.refreshRate, mode.sfVsyncOffset, + mode.appVsyncOffset}); } } @@ -192,7 +192,7 @@ int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { // contiguous block of DisplayConfigImpls specific to that display. DisplayImpl** const impls = reinterpret_cast<DisplayImpl**>( malloc((sizeof(DisplayImpl) + sizeof(DisplayImpl*)) * size + - sizeof(DisplayConfigImpl) * numConfigs)); + sizeof(DisplayConfigImpl) * numModes)); DisplayImpl* const displayData = reinterpret_cast<DisplayImpl*>(impls + size); DisplayConfigImpl* configData = reinterpret_cast<DisplayConfigImpl*>(displayData + size); @@ -200,7 +200,7 @@ int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { 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]; + const std::vector<DisplayConfigImpl>& configs = modesPerDisplay[i]; memcpy(configData, configs.data(), sizeof(DisplayConfigImpl) * configs.size()); displayData[i] = DisplayImpl{id, @@ -257,7 +257,7 @@ int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) { CHECK_NOT_NULL(display); sp<IBinder> token = getToken(display); - const int index = SurfaceComposerClient::getActiveConfig(token); + const int index = SurfaceComposerClient::getActiveDisplayModeId(token); if (index < 0) { return index; } diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp index 2189a429fb..6574ae64f7 100644 --- a/libs/nativedisplay/Android.bp +++ b/libs/nativedisplay/Android.bp @@ -12,25 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_applicable_licenses: [ - "frameworks_native_libs_nativedisplay_license", - ], -} - -// Added automatically by a large-scale-change -// See: http://go/android-license-faq -license { - name: "frameworks_native_libs_nativedisplay_license", - visibility: [":__subpackages__"], - license_kinds: [ - "SPDX-license-identifier-Apache-2.0", - ], - license_text: [ - "NOTICE", - ], -} - cc_library_headers { name: "libnativedisplay_headers", export_include_dirs: ["include",], @@ -72,15 +53,13 @@ cc_library_shared { "libcutils", "libEGL", "libGLESv2", - "libnativehelper", ], - export_shared_lib_headers: [ - "libnativehelper", - ], + export_header_lib_headers: ["jni_headers"], header_libs: [ + "jni_headers", "libnativedisplay_headers", + "libnativehelper_header_only", ], - } diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h index 21649304bf..0f4fd4521f 100644 --- a/libs/nativedisplay/include-private/private/android/choreographer.h +++ b/libs/nativedisplay/include-private/private/android/choreographer.h @@ -18,7 +18,7 @@ #include <apex/choreographer.h> #include <inttypes.h> -#include <nativehelper/JNIHelp.h> +#include <jni.h> namespace android { @@ -29,6 +29,19 @@ void AChoreographer_initJVM(JNIEnv* env); // for consumption by callbacks. void AChoreographer_signalRefreshRateCallbacks(int64_t vsyncPeriod); +// Returns the vsync id of the last frame callback. Client are expected to call +// this function from their frame callback function to get the vsyncId and pass +// it together with a buffer or transaction to the Surface Composer. Calling +// this function from anywhere else will return an undefined value. +int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer); + +// Returns the deadline timestamp (in CLOCK_MONOTONIC) of the last frame callback. +// Client are expected to call this function from their frame callback function +// to get the deadline and use it to know whether a frame is likely to miss +// presentation. Calling this function from anywhere else will return an undefined +// value. +int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer); + // Trampoline functions allowing libandroid.so to define the NDK symbols without including // the entirety of libnativedisplay as a whole static lib. As libnativedisplay // maintains global state, libnativedisplay can never be directly statically diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h index f3716674c5..85fe42f6fd 100644 --- a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h +++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h @@ -19,7 +19,7 @@ #include <EGL/egl.h> #include <EGL/eglext.h> -#include <nativehelper/JNIHelp.h> +#include <jni.h> #include <system/graphics.h> // This file provides a facade API on top of SurfaceTexture, which avoids using diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt index fc59431d08..fda6a20a0b 100644 --- a/libs/nativedisplay/libnativedisplay.map.txt +++ b/libs/nativedisplay/libnativedisplay.map.txt @@ -29,6 +29,8 @@ LIBNATIVEDISPLAY_PLATFORM { android::AChoreographer_routeRegisterRefreshRateCallback*; android::AChoreographer_routeUnregisterRefreshRateCallback*; android::AChoreographer_signalRefreshRateCallbacks*; + android::AChoreographer_getVsyncId*; + android::AChoreographer_getFrameDeadline*; android::ADisplay_acquirePhysicalDisplays*; android::ADisplay_release*; android::ADisplay_getMaxSupportedFps*; diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp index 16afc68b3d..365e788ea6 100644 --- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp @@ -51,7 +51,15 @@ sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace } int slot = item.mSlot; + *outQueueEmpty = false; if (item.mFence->isValid()) { + // If fence is not signaled, that means frame is not ready and + // outQueueEmpty is set to true. By the time the fence is signaled, + // there may be a new buffer queued. This is a proper detection for an + // empty queue and it is needed to avoid infinite loop in + // ASurfaceTexture_dequeueBuffer (see b/159921224). + *outQueueEmpty = item.mFence->getStatus() == Fence::Status::Unsignaled; + // Wait on the producer fence for the buffer to be ready. err = fenceWait(item.mFence->get(), fencePassThroughHandle); if (err != OK) { @@ -112,7 +120,6 @@ sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace st.mCurrentFrameNumber = item.mFrameNumber; st.computeCurrentTransformMatrixLocked(); - *outQueueEmpty = false; *outDataspace = item.mDataSpace; *outSlotid = slot; return st.mSlots[slot].mGraphicBuffer; diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp index ebe4484873..c214ab7718 100644 --- a/libs/nativedisplay/surfacetexture/surface_texture.cpp +++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp @@ -29,8 +29,7 @@ #include <mutex> #include <jni.h> -#include <nativehelper/JNIHelp.h> -#include <nativehelper/ScopedLocalRef.h> +#include <nativehelper/scoped_local_ref.h> struct ASurfaceTexture { android::sp<android::SurfaceTexture> consumer; diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index 1ec73ce961..a375d43a43 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -397,6 +397,16 @@ int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) { return 0; } +int AHardwareBuffer_getId(const AHardwareBuffer* buffer, uint64_t* outId) { + if (!buffer || !outId) return BAD_VALUE; + + const GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); + if (!gb) return BAD_VALUE; + + *outId = gb->getId(); + + return OK; +} // ---------------------------------------------------------------------------- // VNDK functions diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index fd1793b6bc..b406a9c2fe 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -159,10 +159,8 @@ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) { } int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) { - if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) { - return -EINVAL; - } - return native_window_set_frame_rate(window, frameRate, compatibility); + return ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility, + /*shouldBeSeamless*/ true); } void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) { @@ -172,6 +170,13 @@ void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) { window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS); } +int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate, + int8_t compatibility, bool shouldBeSeamless) { + if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) { + return -EINVAL; + } + return native_window_set_frame_rate(window, frameRate, compatibility, shouldBeSeamless); +} /************************************************************************************************** * vndk-stable diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index 8675439938..3011dccf1e 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -12,25 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_applicable_licenses: [ - "frameworks_native_libs_nativewindow_license", - ], -} - -// Added automatically by a large-scale-change -// See: http://go/android-license-faq -license { - name: "frameworks_native_libs_nativewindow_license", - visibility: [":__subpackages__"], - license_kinds: [ - "SPDX-license-identifier-Apache-2.0", - ], - license_text: [ - "NOTICE", - ], -} - ndk_headers { name: "libnativewindow_ndk_headers", from: "include/android", diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h index dcb05b5536..20a1f74792 100644 --- a/libs/nativewindow/include/android/hardware_buffer.h +++ b/libs/nativewindow/include/android/hardware_buffer.h @@ -45,14 +45,14 @@ #ifndef ANDROID_HARDWARE_BUFFER_H #define ANDROID_HARDWARE_BUFFER_H +#include <android/rect.h> #include <inttypes.h> - #include <sys/cdefs.h> -#include <android/rect.h> - __BEGIN_DECLS +// clang-format off + /** * Buffer pixel formats. */ @@ -201,9 +201,9 @@ enum AHardwareBuffer_UsageFlags { AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK = 0xFUL << 4, /// The buffer will be read from by the GPU as a texture. - AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8, + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8, /// The buffer will be written to by the GPU as a framebuffer attachment. - AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER = 1UL << 9, + AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER = 1UL << 9, /** * The buffer will be written to by the GPU as a framebuffer * attachment. @@ -214,7 +214,7 @@ enum AHardwareBuffer_UsageFlags { * attachment should also have this flag. Use the equivalent flag * AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER to avoid this confusion. */ - AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, + AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, /** * The buffer will be used as a composer HAL overlay layer. * @@ -225,7 +225,7 @@ enum AHardwareBuffer_UsageFlags { * directly through AHardwareBuffer_allocate instead of buffers allocated * by the framework. */ - AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY = 1ULL << 11, + AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY = 1ULL << 11, /** * The buffer is protected from direct CPU access or being read by * non-secure hardware, such as video encoders. @@ -236,19 +236,19 @@ enum AHardwareBuffer_UsageFlags { * GL_EXT_protected_textures for more information on how these * buffers are expected to behave. */ - AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = 1UL << 14, + AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = 1UL << 14, /// The buffer will be read by a hardware video encoder. - AHARDWAREBUFFER_USAGE_VIDEO_ENCODE = 1UL << 16, + AHARDWAREBUFFER_USAGE_VIDEO_ENCODE = 1UL << 16, /** * The buffer will be used for direct writes from sensors. * When this flag is present, the format must be AHARDWAREBUFFER_FORMAT_BLOB. */ - AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = 1UL << 23, + AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = 1UL << 23, /** * The buffer will be used as a shader storage or uniform buffer object. * When this flag is present, the format must be AHARDWAREBUFFER_FORMAT_BLOB. */ - AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 1UL << 24, + AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 1UL << 24, /** * The buffer will be used as a cube map texture. * When this flag is present, the buffer must have a layer count @@ -256,13 +256,13 @@ enum AHardwareBuffer_UsageFlags { * bound to OpenGL textures using the extension * GL_EXT_EGL_image_storage instead of GL_KHR_EGL_image. */ - AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP = 1UL << 25, + AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP = 1UL << 25, /** * The buffer contains a complete mipmap hierarchy. * Note that buffers with this flag must be bound to OpenGL textures using * the extension GL_EXT_EGL_image_storage instead of GL_KHR_EGL_image. */ - AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE = 1UL << 26, + AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE = 1UL << 26, AHARDWAREBUFFER_USAGE_VENDOR_0 = 1ULL << 28, AHARDWAREBUFFER_USAGE_VENDOR_1 = 1ULL << 29, @@ -291,8 +291,8 @@ enum AHardwareBuffer_UsageFlags { * parameters of existing ones. */ typedef struct AHardwareBuffer_Desc { - uint32_t width; ///< Width in pixels. - uint32_t height; ///< Height in pixels. + uint32_t width; ///< Width in pixels. + uint32_t height; ///< Height in pixels. /** * Number of images in an image array. AHardwareBuffers with one * layer correspond to regular 2D textures. AHardwareBuffers with @@ -301,21 +301,21 @@ typedef struct AHardwareBuffer_Desc { * AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP is present, the buffer is * a cube map or a cube map array. */ - uint32_t layers; - uint32_t format; ///< One of AHardwareBuffer_Format. - uint64_t usage; ///< Combination of AHardwareBuffer_UsageFlags. - uint32_t stride; ///< Row stride in pixels, ignored for AHardwareBuffer_allocate() - uint32_t rfu0; ///< Initialize to zero, reserved for future use. - uint64_t rfu1; ///< Initialize to zero, reserved for future use. + uint32_t layers; + uint32_t format; ///< One of AHardwareBuffer_Format. + uint64_t usage; ///< Combination of AHardwareBuffer_UsageFlags. + uint32_t stride; ///< Row stride in pixels, ignored for AHardwareBuffer_allocate() + uint32_t rfu0; ///< Initialize to zero, reserved for future use. + uint64_t rfu1; ///< Initialize to zero, reserved for future use. } AHardwareBuffer_Desc; /** * Holds data for a single image plane. */ typedef struct AHardwareBuffer_Plane { - void* data; ///< Points to first byte in plane - uint32_t pixelStride; ///< Distance in bytes from the color channel of one pixel to the next - uint32_t rowStride; ///< Distance in bytes from the first value of one row of the image to + void* _Nullable data; ///< Points to first byte in plane + uint32_t pixelStride; ///< Distance in bytes from the color channel of one pixel to the next + uint32_t rowStride; ///< Distance in bytes from the first value of one row of the image to /// the first value of the next row. } AHardwareBuffer_Plane; @@ -323,8 +323,8 @@ typedef struct AHardwareBuffer_Plane { * Holds all image planes that contain the pixel data. */ typedef struct AHardwareBuffer_Planes { - uint32_t planeCount; ///< Number of distinct planes - AHardwareBuffer_Plane planes[4]; ///< Array of image planes + uint32_t planeCount; ///< Number of distinct planes + AHardwareBuffer_Plane planes[4]; ///< Array of image planes } AHardwareBuffer_Planes; /** @@ -332,6 +332,8 @@ typedef struct AHardwareBuffer_Planes { */ typedef struct AHardwareBuffer AHardwareBuffer; +// clang-format on + /** * Allocates a buffer that matches the passed AHardwareBuffer_Desc. * @@ -345,8 +347,8 @@ typedef struct AHardwareBuffer AHardwareBuffer; * \return 0 on success, or an error number of the allocation fails for * any reason. The returned buffer has a reference count of 1. */ -int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, - AHardwareBuffer** outBuffer) __INTRODUCED_IN(26); +int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* _Nonnull desc, + AHardwareBuffer* _Nullable* _Nonnull outBuffer) __INTRODUCED_IN(26); /** * Acquire a reference on the given AHardwareBuffer object. * @@ -355,7 +357,7 @@ int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, * * Available since API level 26. */ -void AHardwareBuffer_acquire(AHardwareBuffer* buffer) __INTRODUCED_IN(26); +void AHardwareBuffer_acquire(AHardwareBuffer* _Nonnull buffer) __INTRODUCED_IN(26); /** * Remove a reference that was previously acquired with @@ -363,7 +365,7 @@ void AHardwareBuffer_acquire(AHardwareBuffer* buffer) __INTRODUCED_IN(26); * * Available since API level 26. */ -void AHardwareBuffer_release(AHardwareBuffer* buffer) __INTRODUCED_IN(26); +void AHardwareBuffer_release(AHardwareBuffer* _Nonnull buffer) __INTRODUCED_IN(26); /** * Return a description of the AHardwareBuffer in the passed @@ -371,8 +373,8 @@ void AHardwareBuffer_release(AHardwareBuffer* buffer) __INTRODUCED_IN(26); * * Available since API level 26. */ -void AHardwareBuffer_describe(const AHardwareBuffer* buffer, - AHardwareBuffer_Desc* outDesc) __INTRODUCED_IN(26); +void AHardwareBuffer_describe(const AHardwareBuffer* _Nonnull buffer, + AHardwareBuffer_Desc* _Nonnull outDesc) __INTRODUCED_IN(26); /** * Lock the AHardwareBuffer for direct CPU access. @@ -426,38 +428,9 @@ void AHardwareBuffer_describe(const AHardwareBuffer* buffer, * has more than one layer. Error number if the lock fails for any other * reason. */ -int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage, - int32_t fence, const ARect* rect, void** outVirtualAddress) __INTRODUCED_IN(26); - -/** - * Lock a potentially multi-planar AHardwareBuffer for direct CPU access. - * - * This function is similar to AHardwareBuffer_lock, but can lock multi-planar - * formats. The locked planes are returned in the \a outPlanes argument. Note, - * that multi-planar should not be confused with multi-layer images, which this - * locking function does not support. - * - * YUV formats are always represented by three separate planes of data, one for - * each color plane. The order of planes in the array is guaranteed such that - * plane #0 is always Y, plane #1 is always U (Cb), and plane #2 is always V - * (Cr). All other formats are represented by a single plane. - * - * Additional information always accompanies the buffers, describing the row - * stride and the pixel stride for each plane. - * - * In case the buffer cannot be locked, \a outPlanes will contain zero planes. - * - * See the AHardwareBuffer_lock documentation for all other locking semantics. - * - * Available since API level 29. - * - * \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags - * are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer - * has more than one layer. Error number if the lock fails for any other - * reason. - */ -int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage, - int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) __INTRODUCED_IN(29); +int AHardwareBuffer_lock(AHardwareBuffer* _Nonnull buffer, uint64_t usage, int32_t fence, + const ARect* _Nullable rect, void* _Nullable* _Nonnull outVirtualAddress) + __INTRODUCED_IN(26); /** * Unlock the AHardwareBuffer from direct CPU access. @@ -477,7 +450,8 @@ int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage, * \return 0 on success. -EINVAL if \a buffer is NULL. Error number if * the unlock fails for any reason. */ -int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) __INTRODUCED_IN(26); +int AHardwareBuffer_unlock(AHardwareBuffer* _Nonnull buffer, int32_t* _Nullable fence) + __INTRODUCED_IN(26); /** * Send the AHardwareBuffer to an AF_UNIX socket. @@ -487,7 +461,8 @@ int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) __INTRODUCED * \return 0 on success, -EINVAL if \a buffer is NULL, or an error * number if the operation fails for any reason. */ -int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd) __INTRODUCED_IN(26); +int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* _Nonnull buffer, int socketFd) + __INTRODUCED_IN(26); /** * Receive an AHardwareBuffer from an AF_UNIX socket. @@ -497,7 +472,40 @@ int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int so * \return 0 on success, -EINVAL if \a outBuffer is NULL, or an error * number if the operation fails for any reason. */ -int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer) __INTRODUCED_IN(26); +int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, + AHardwareBuffer* _Nullable* _Nonnull outBuffer) + __INTRODUCED_IN(26); + +/** + * Lock a potentially multi-planar AHardwareBuffer for direct CPU access. + * + * This function is similar to AHardwareBuffer_lock, but can lock multi-planar + * formats. The locked planes are returned in the \a outPlanes argument. Note, + * that multi-planar should not be confused with multi-layer images, which this + * locking function does not support. + * + * YUV formats are always represented by three separate planes of data, one for + * each color plane. The order of planes in the array is guaranteed such that + * plane #0 is always Y, plane #1 is always U (Cb), and plane #2 is always V + * (Cr). All other formats are represented by a single plane. + * + * Additional information always accompanies the buffers, describing the row + * stride and the pixel stride for each plane. + * + * In case the buffer cannot be locked, \a outPlanes will contain zero planes. + * + * See the AHardwareBuffer_lock documentation for all other locking semantics. + * + * Available since API level 29. + * + * \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags + * are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer + * has more than one layer. Error number if the lock fails for any other + * reason. + */ +int AHardwareBuffer_lockPlanes(AHardwareBuffer* _Nonnull buffer, uint64_t usage, int32_t fence, + const ARect* _Nullable rect, + AHardwareBuffer_Planes* _Nonnull outPlanes) __INTRODUCED_IN(29); /** * Test whether the given format and usage flag combination is @@ -518,7 +526,7 @@ int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** out * \return 1 if the format and usage flag combination is allocatable, * 0 otherwise. */ -int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) __INTRODUCED_IN(29); +int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* _Nonnull desc) __INTRODUCED_IN(29); /** * Lock an AHardwareBuffer for direct CPU access. @@ -531,9 +539,22 @@ int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) __INTRODUCED_I * * Available since API level 29. */ -int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage, - int32_t fence, const ARect* rect, void** outVirtualAddress, - int32_t* outBytesPerPixel, int32_t* outBytesPerStride) __INTRODUCED_IN(29); +int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* _Nonnull buffer, uint64_t usage, int32_t fence, + const ARect* _Nullable rect, + void* _Nullable* _Nonnull outVirtualAddress, + int32_t* _Nonnull outBytesPerPixel, + int32_t* _Nonnull outBytesPerStride) __INTRODUCED_IN(29); + +/** + * Get the system wide unique id for an AHardwareBuffer. + * + * Available since API level 31. + * + * \return 0 on success, -EINVAL if \a buffer or \a outId is NULL, or an error number if the + * operation fails for any reason. + */ +int AHardwareBuffer_getId(const AHardwareBuffer* _Nonnull buffer, uint64_t* _Nonnull outId) + __INTRODUCED_IN(31); __END_DECLS diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index a3a45e3705..285f2fb7fe 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -34,6 +34,7 @@ #define ANDROID_NATIVE_WINDOW_H #include <stdint.h> +#include <stdbool.h> #include <sys/cdefs.h> #include <android/data_space.h> @@ -246,6 +247,27 @@ enum ANativeWindow_FrameRateCompatibility { }; /** + * Same as ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility, true). + * + * See ANativeWindow_setFrameRateWithSeamlessness(). + * + * Available since API level 30. + */ +int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) + __INTRODUCED_IN(30); + +/** + * Provides a hint to the window that buffers should be preallocated ahead of + * time. Note that the window implementation is not guaranteed to preallocate + * any buffers, for instance if an implementation disallows allocation of new + * buffers, or if there is insufficient memory in the system to preallocate + * additional buffers + * + * Available since API level 30. + */ +void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) __INTRODUCED_IN(30); + +/** * Sets the intended frame rate for this window. * * On devices that are capable of running the display at different refresh @@ -261,7 +283,7 @@ enum ANativeWindow_FrameRateCompatibility { * this ANativeWindow is consumed by something other than the system compositor, * e.g. a media codec, this call has no effect. * - * Available since API level 30. + * Available since API level 31. * * \param frameRate The intended frame rate of this window, in frames per * second. 0 is a special value that indicates the app will accept the system's @@ -274,22 +296,17 @@ enum ANativeWindow_FrameRateCompatibility { * compatibility value may influence the system's choice of display refresh * rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info. * + * \param shouldBeSeamless Whether display refresh rate transitions should be seamless. A + * seamless transition is one that doesn't have any visual interruptions, such as a black + * screen for a second or two. True indicates that any frame rate changes caused by this + * request should be seamless. False indicates that non-seamless refresh rates are also + * acceptable. + * * \return 0 for success, -EINVAL if the window, frame rate, or compatibility * value are invalid. */ -int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) - __INTRODUCED_IN(30); - -/** - * Provides a hint to the window that buffers should be preallocated ahead of - * time. Note that the window implementation is not guaranteed to preallocate - * any buffers, for instance if an implementation disallows allocation of new - * buffers, or if there is insufficient memory in the system to preallocate - * additional buffers - * - * Available since API level 30. - */ -void ANativeWindow_tryAllocateBuffers(ANativeWindow* window); +int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate, + int8_t compatibility, bool shouldBeSeamless) __INTRODUCED_IN(31); #ifdef __cplusplus }; diff --git a/libs/nativewindow/include/apex/window.h b/libs/nativewindow/include/apex/window.h index 2d1354cdf1..0923438eec 100644 --- a/libs/nativewindow/include/apex/window.h +++ b/libs/nativewindow/include/apex/window.h @@ -39,6 +39,19 @@ enum ANativeWindowPerform { // clang-format on }; +/* + * Internal extension of compatibility value for ANativeWindow_setFrameRate. */ +enum ANativeWindow_FrameRateCompatibilityInternal { + /** + * This surface belongs to an app on the High Refresh Rate Deny list, and needs the display + * to operate at the exact frame rate. + * + * This is used internally by the platform and should not be used by apps. + * @hide + */ + ANATIVEWINDOW_FRAME_RATE_EXACT = 100, +}; + /** * Prototype of the function that an ANativeWindow implementation would call * when ANativeWindow_cancelBuffer is called. diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index b78fc5dbbc..7aa2cf4404 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -255,6 +255,8 @@ enum { NATIVE_WINDOW_ALLOCATE_BUFFERS = 45, /* private */ NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER = 46, /* private */ NATIVE_WINDOW_SET_QUERY_INTERCEPTOR = 47, /* private */ + NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO = 48, /* private */ + NATIVE_WINDOW_GET_EXTRA_BUFFER_COUNT = 49, /* private */ // clang-format on }; @@ -1017,9 +1019,21 @@ static inline int native_window_set_auto_prerotation(struct ANativeWindow* windo } static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate, - int8_t compatibility) { + int8_t compatibility, bool shouldBeSeamless) { return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate, - (int)compatibility); + (int)compatibility, (int)shouldBeSeamless); +} + +static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window, + int64_t frameTimelineVsyncId, + int32_t inputEventId) { + return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, + frameTimelineVsyncId, inputEventId); +} + +static inline int native_window_get_extra_buffer_count( + struct ANativeWindow* window, int* extraBuffers) { + return window->perform(window, NATIVE_WINDOW_GET_EXTRA_BUFFER_COUNT, extraBuffers); } // ------------------------------------------------------------------------------------------------ diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h index 3392d7f094..50fe0b7423 100644 --- a/libs/nativewindow/include/vndk/hardware_buffer.h +++ b/libs/nativewindow/include/vndk/hardware_buffer.h @@ -24,7 +24,14 @@ __BEGIN_DECLS -const native_handle_t* AHardwareBuffer_getNativeHandle(const AHardwareBuffer* buffer); +/** + * Get the native handle from an AHardwareBuffer. + * + * \return a non-NULL native handle on success, NULL if \a buffer is nullptr or the operation fails + * for any reason. + */ +const native_handle_t* _Nullable AHardwareBuffer_getNativeHandle( + const AHardwareBuffer* _Nonnull buffer); enum CreateFromHandleMethod { // enum values chosen to match internal GraphicBuffer::HandleWrapMethod @@ -33,9 +40,9 @@ enum CreateFromHandleMethod { }; /** - * Create a AHardwareBuffer from a native handle. + * Create an AHardwareBuffer from a native handle. * - * This function wraps a native handle in a AHardwareBuffer suitable for use by applications or + * This function wraps a native handle in an AHardwareBuffer suitable for use by applications or * other parts of the system. The contents of desc will be returned by AHardwareBuffer_describe(). * * If method is AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_REGISTER, the handle is assumed to be @@ -44,10 +51,13 @@ enum CreateFromHandleMethod { * * If method is AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, the handle will be cloned and the * clone registered. The AHardwareBuffer will own the cloned handle but not the original. + * + * \return 0 on success, -EINVAL if \a desc or \a handle or outBuffer is NULL, or an error number if + * the operation fails for any reason. */ -int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* desc, - const native_handle_t* handle, int32_t method, - AHardwareBuffer** outBuffer); +int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* _Nonnull desc, + const native_handle_t* _Nonnull handle, int32_t method, + AHardwareBuffer* _Nullable* _Nonnull outBuffer); /** * Buffer pixel formats. diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index 1b5d20dff7..24d0e3badc 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -4,6 +4,7 @@ LIBNATIVEWINDOW { AHardwareBuffer_allocate; AHardwareBuffer_createFromHandle; # llndk # apex AHardwareBuffer_describe; + AHardwareBuffer_getId; # introduced=31 AHardwareBuffer_getNativeHandle; # llndk # apex AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; @@ -46,6 +47,7 @@ LIBNATIVEWINDOW { ANativeWindow_setBuffersTransform; ANativeWindow_setDequeueTimeout; # apex # introduced=30 ANativeWindow_setFrameRate; # introduced=30 + ANativeWindow_setFrameRateWithSeamlessness; # introduced=31 ANativeWindow_setSharedBufferMode; # llndk ANativeWindow_setSwapInterval; # llndk ANativeWindow_setUsage; # llndk diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp index 71b1f9f021..ef863b6d67 100644 --- a/libs/nativewindow/tests/AHardwareBufferTest.cpp +++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp @@ -17,12 +17,11 @@ #define LOG_TAG "AHardwareBuffer_test" //#define LOG_NDEBUG 0 -#include <android/hardware_buffer.h> -#include <private/android/AHardwareBufferHelpers.h> #include <android/hardware/graphics/common/1.0/types.h> -#include <vndk/hardware_buffer.h> - #include <gtest/gtest.h> +#include <private/android/AHardwareBufferHelpers.h> +#include <ui/GraphicBuffer.h> +#include <vndk/hardware_buffer.h> using namespace android; using android::hardware::graphics::common::V1_0::BufferUsage; @@ -131,3 +130,43 @@ TEST(AHardwareBufferTest, GetCreateHandleTest) { AHardwareBuffer_release(buffer); AHardwareBuffer_release(otherBuffer); } + +TEST(AHardwareBufferTest, GetIdTest) { + const uint32_t testWidth = 4; + const uint32_t testHeight = 4; + const uint32_t testLayers = 1; + + AHardwareBuffer* ahb1 = nullptr; + uint64_t id1 = 0; + const AHardwareBuffer_Desc desc = { + .width = testWidth, + .height = testHeight, + .layers = testLayers, + .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, + }; + int res = AHardwareBuffer_allocate(&desc, &ahb1); + EXPECT_EQ(NO_ERROR, res); + EXPECT_NE(nullptr, ahb1); + EXPECT_EQ(0, AHardwareBuffer_getId(ahb1, &id1)); + const GraphicBuffer* gb1 = AHardwareBuffer_to_GraphicBuffer(ahb1); + EXPECT_NE(nullptr, gb1); + EXPECT_EQ(id1, gb1->getId()); + EXPECT_NE(id1, 0); + + sp<GraphicBuffer> gb2(new GraphicBuffer(testWidth, + testHeight, + PIXEL_FORMAT_RGBA_8888, + testLayers, + GraphicBuffer::USAGE_SW_READ_RARELY, + std::string("test"))); + EXPECT_NE(nullptr, gb2.get()); + const AHardwareBuffer* ahb2 = AHardwareBuffer_from_GraphicBuffer(gb2.get()); + EXPECT_NE(nullptr, ahb2); + uint64_t id2 = 0; + EXPECT_EQ(0, AHardwareBuffer_getId(ahb2, &id2)); + EXPECT_EQ(id2, gb2->getId()); + EXPECT_NE(id2, 0); + + EXPECT_NE(id1, id2); +} diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp index 27444584d8..2e4bd991e0 100644 --- a/libs/nativewindow/tests/Android.bp +++ b/libs/nativewindow/tests/Android.bp @@ -14,17 +14,6 @@ // limitations under the License. // -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_libs_nativewindow_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: [ - "frameworks_native_libs_nativewindow_license", - ], -} - cc_test { name: "libnativewindow_test", test_suites: [ @@ -35,6 +24,7 @@ cc_test { "liblog", "libnativewindow", "libsync", + "libui", "libutils", "android.hardware.graphics.common@1.0", ], diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index aae1e3157c..00540b8f68 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -1,12 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_defaults { name: "renderengine_defaults", cflags: [ @@ -73,23 +64,48 @@ filegroup { ], } +filegroup { + name: "librenderengine_threaded_sources", + srcs: [ + "threaded/RenderEngineThreaded.cpp", + ], +} + +filegroup { + name: "librenderengine_skia_sources", + srcs: [ + "skia/AutoBackendTexture.cpp", + "skia/ColorSpaces.cpp", + "skia/SkiaRenderEngine.cpp", + "skia/SkiaGLRenderEngine.cpp", + "skia/debug/CaptureTimer.cpp", + "skia/debug/CommonPool.cpp", + "skia/debug/SkiaCapture.cpp", + "skia/filters/BlurFilter.cpp", + "skia/filters/LinearEffect.cpp", + ], +} + cc_library_static { name: "librenderengine", defaults: ["librenderengine_defaults"], - vendor_available: true, - vndk: { - enabled: true, - }, double_loadable: true, clang: true, cflags: [ "-fvisibility=hidden", "-Werror=format", + "-Wno-unused-parameter", ], srcs: [ ":librenderengine_sources", ":librenderengine_gl_sources", + ":librenderengine_threaded_sources", + ":librenderengine_skia_sources", + ], + include_dirs: [ + "external/skia/src/gpu", ], + whole_static_libs: ["libskia"], lto: { thin: true, }, diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 0fdf093b2f..b2ad22d687 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -18,39 +18,69 @@ #include <cutils/properties.h> #include <log/log.h> -#include <private/gui/SyncFeatures.h> #include "gl/GLESRenderEngine.h" +#include "threaded/RenderEngineThreaded.h" + +#include "skia/SkiaGLRenderEngine.h" namespace android { namespace renderengine { -std::unique_ptr<impl::RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) { +std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) { + RenderEngineType renderEngineType = args.renderEngineType; + + // Keep the ability to override by PROPERTIES: char prop[PROPERTY_VALUE_MAX]; - property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles"); + property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, ""); if (strcmp(prop, "gles") == 0) { - ALOGD("RenderEngine GLES Backend"); - return renderengine::gl::GLESRenderEngine::create(args); + renderEngineType = RenderEngineType::GLES; + } + if (strcmp(prop, "threaded") == 0) { + renderEngineType = RenderEngineType::THREADED; + } + if (strcmp(prop, "skiagl") == 0) { + renderEngineType = RenderEngineType::SKIA_GL; + } + if (strcmp(prop, "skiaglthreaded") == 0) { + renderEngineType = RenderEngineType::SKIA_GL_THREADED; } - ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop); - return renderengine::gl::GLESRenderEngine::create(args); -} - -RenderEngine::~RenderEngine() = default; - -namespace impl { - -RenderEngine::RenderEngine(const RenderEngineCreationArgs& args) : mArgs(args) {} - -RenderEngine::~RenderEngine() = default; -bool RenderEngine::useNativeFenceSync() const { - return SyncFeatures::getInstance().useNativeFenceSync(); + switch (renderEngineType) { + case RenderEngineType::THREADED: + ALOGD("Threaded RenderEngine with GLES Backend"); + return renderengine::threaded::RenderEngineThreaded::create( + [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); }); + case RenderEngineType::SKIA_GL: + ALOGD("RenderEngine with SkiaGL Backend"); + return renderengine::skia::SkiaGLRenderEngine::create(args); + case RenderEngineType::SKIA_GL_THREADED: { + // These need to be recreated, since they are a constant reference, and we need to + // let SkiaRE know that it's running as threaded, and all GL operation will happen on + // the same thread. + RenderEngineCreationArgs skiaArgs = + RenderEngineCreationArgs::Builder() + .setPixelFormat(args.pixelFormat) + .setImageCacheSize(args.imageCacheSize) + .setUseColorManagerment(args.useColorManagement) + .setEnableProtectedContext(args.enableProtectedContext) + .setPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly) + .setSupportsBackgroundBlur(args.supportsBackgroundBlur) + .setContextPriority(args.contextPriority) + .setRenderEngineType(RenderEngineType::SKIA_GL_THREADED) + .build(); + ALOGD("Threaded RenderEngine with SkiaGL Backend"); + return renderengine::threaded::RenderEngineThreaded::create([skiaArgs]() { + return android::renderengine::skia::SkiaGLRenderEngine::create(skiaArgs); + }); + } + case RenderEngineType::GLES: + default: + ALOGD("RenderEngine with GLES Backend"); + return renderengine::gl::GLESRenderEngine::create(args); + } } -bool RenderEngine::useWaitSync() const { - return SyncFeatures::getInstance().useWaitSync(); -} +RenderEngine::~RenderEngine() = default; -} // namespace impl } // namespace renderengine } // namespace android diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index 56d470eecd..70ae0b288a 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -15,6 +15,7 @@ */ //#define LOG_NDEBUG 0 +#include "EGL/egl.h" #undef LOG_TAG #define LOG_TAG "RenderEngine" #define ATRACE_TAG ATRACE_TAG_GRAPHICS @@ -51,8 +52,6 @@ #include "ProgramCache.h" #include "filters/BlurFilter.h" -extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); - bool checkGlError(const char* op, int lineNumber) { bool errorFound = false; GLint error = glGetError(); @@ -116,6 +115,28 @@ namespace android { namespace renderengine { namespace gl { +class BindNativeBufferAsFramebuffer { +public: + BindNativeBufferAsFramebuffer(GLESRenderEngine& engine, ANativeWindowBuffer* buffer, + const bool useFramebufferCache) + : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) { + mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(), + useFramebufferCache) + ? mEngine.bindFrameBuffer(mFramebuffer) + : NO_MEMORY; + } + ~BindNativeBufferAsFramebuffer() { + mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true); + mEngine.unbindFrameBuffer(mFramebuffer); + } + status_t getStatus() const { return mStatus; } + +private: + GLESRenderEngine& mEngine; + Framebuffer* mFramebuffer; + status_t mStatus; +}; + using base::StringAppendF; using ui::Dataspace; @@ -201,6 +222,29 @@ static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint render return err; } +std::optional<RenderEngine::ContextPriority> GLESRenderEngine::createContextPriority( + const RenderEngineCreationArgs& args) { + if (!GLExtensions::getInstance().hasContextPriority()) { + return std::nullopt; + } + + switch (args.contextPriority) { + case RenderEngine::ContextPriority::REALTIME: + if (gl::GLExtensions::getInstance().hasRealtimePriority()) { + return RenderEngine::ContextPriority::REALTIME; + } else { + ALOGI("Realtime priority unsupported, degrading gracefully to high priority"); + return RenderEngine::ContextPriority::HIGH; + } + case RenderEngine::ContextPriority::HIGH: + case RenderEngine::ContextPriority::MEDIUM: + case RenderEngine::ContextPriority::LOW: + return args.contextPriority; + default: + return std::nullopt; + } +} + std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCreationArgs& args) { // initialize EGL for the default display EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); @@ -208,16 +252,17 @@ std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCre LOG_ALWAYS_FATAL("failed to initialize EGL"); } - const auto eglVersion = eglQueryStringImplementationANDROID(display, EGL_VERSION); + const auto eglVersion = eglQueryString(display, EGL_VERSION); if (!eglVersion) { checkGlError(__FUNCTION__, __LINE__); - LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_VERSION) failed"); + LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed"); } - const auto eglExtensions = eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS); + // Use the Android impl to grab EGL_NV_context_priority_realtime + const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS); if (!eglExtensions) { checkGlError(__FUNCTION__, __LINE__); - LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_EXTENSIONS) failed"); + LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed"); } GLExtensions& extensions = GLExtensions::getInstance(); @@ -230,17 +275,16 @@ std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCre config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true); } - bool useContextPriority = - extensions.hasContextPriority() && args.contextPriority == ContextPriority::HIGH; + const std::optional<RenderEngine::ContextPriority> priority = createContextPriority(args); EGLContext protectedContext = EGL_NO_CONTEXT; if (args.enableProtectedContext && extensions.hasProtectedContent()) { - protectedContext = createEglContext(display, config, nullptr, useContextPriority, - Protection::PROTECTED); + protectedContext = + createEglContext(display, config, nullptr, priority, Protection::PROTECTED); ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context"); } - EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority, - Protection::UNPROTECTED); + EGLContext ctxt = + createEglContext(display, config, protectedContext, priority, Protection::UNPROTECTED); // if can't create a GL context, we can only abort. LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed"); @@ -290,7 +334,6 @@ std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCre ALOGI("extensions: %s", extensions.getExtensions()); ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize()); ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims()); - return engine; } @@ -336,8 +379,7 @@ EGLConfig GLESRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface stub, EGLContext protectedContext, EGLSurface protectedStub) - : renderengine::impl::RenderEngine(args), - mEGLDisplay(display), + : mEGLDisplay(display), mEGLConfig(config), mEGLContext(ctxt), mStubSurface(stub), @@ -346,7 +388,8 @@ GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisp mVpWidth(0), mVpHeight(0), mFramebufferImageCacheSize(args.imageCacheSize), - mUseColorManagement(args.useColorManagement) { + mUseColorManagement(args.useColorManagement), + mPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly) { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); @@ -426,24 +469,37 @@ GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisp mPlaceholderBuffer, attributes); ALOGE_IF(mPlaceholderImage == EGL_NO_IMAGE_KHR, "Failed to create placeholder image: %#x", eglGetError()); + + mShadowTexture = std::make_unique<GLShadowTexture>(); } GLESRenderEngine::~GLESRenderEngine() { // Destroy the image manager first. mImageManager = nullptr; + mShadowTexture = nullptr; + cleanFramebufferCache(); + ProgramCache::getInstance().purgeCaches(); std::lock_guard<std::mutex> lock(mRenderingMutex); + glDisableVertexAttribArray(Program::position); unbindFrameBuffer(mDrawingBuffer.get()); mDrawingBuffer = nullptr; - while (!mFramebufferImageCache.empty()) { - EGLImageKHR expired = mFramebufferImageCache.front().second; - mFramebufferImageCache.pop_front(); - eglDestroyImageKHR(mEGLDisplay, expired); - DEBUG_EGL_IMAGE_TRACKER_DESTROY(); - } eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage); mImageCache.clear(); + if (mStubSurface != EGL_NO_SURFACE) { + eglDestroySurface(mEGLDisplay, mStubSurface); + } + if (mProtectedStubSurface != EGL_NO_SURFACE) { + eglDestroySurface(mEGLDisplay, mProtectedStubSurface); + } + if (mEGLContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEGLDisplay, mEGLContext); + } + if (mProtectedEGLContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEGLDisplay, mProtectedEGLContext); + } eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglTerminate(mEGLDisplay); + eglReleaseThread(); } std::unique_ptr<Framebuffer> GLESRenderEngine::createFramebuffer() { @@ -460,8 +516,7 @@ Framebuffer* GLESRenderEngine::getFramebufferForDrawing() { void GLESRenderEngine::primeCache() const { ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext, - mArgs.useColorManagement, - mArgs.precacheToneMapperShaderOnly); + mUseColorManagement, mPrecacheToneMapperShaderOnly); } base::unique_fd GLESRenderEngine::flush() { @@ -624,13 +679,8 @@ void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& i } } -status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, - const sp<GraphicBuffer>& buffer, - const sp<Fence>& bufferFence) { - if (buffer == nullptr) { - return BAD_VALUE; - } - +void GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, + const sp<Fence>& bufferFence) { ATRACE_CALL(); bool found = false; @@ -646,7 +696,8 @@ status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, if (!found) { status_t cacheResult = mImageManager->cache(buffer); if (cacheResult != NO_ERROR) { - return cacheResult; + ALOGE("Error with caching buffer: %d", cacheResult); + return; } } @@ -663,7 +714,7 @@ status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, // We failed creating the image if we got here, so bail out. ALOGE("Failed to create an EGLImage when rendering"); bindExternalTextureImage(texName, *createImage()); - return NO_INIT; + return; } bindExternalTextureImage(texName, *cachedImage->second); @@ -676,25 +727,26 @@ status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, base::unique_fd fenceFd(bufferFence->dup()); if (fenceFd == -1) { ALOGE("error dup'ing fence fd: %d", errno); - return -errno; + return; } if (!waitFence(std::move(fenceFd))) { ALOGE("failed to wait on fence fd"); - return UNKNOWN_ERROR; + return; } } else { status_t err = bufferFence->waitForever("RenderEngine::bindExternalTextureBuffer"); if (err != NO_ERROR) { ALOGE("error waiting for fence: %d", err); - return err; + return; } } } - return NO_ERROR; + return; } void GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { + ATRACE_CALL(); mImageManager->cacheAsync(buffer, nullptr); } @@ -725,9 +777,9 @@ status_t GLESRenderEngine::cacheExternalTextureBufferInternal(const sp<GraphicBu bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(), buffer->getUsage() & GRALLOC_USAGE_PROTECTED); if (!created) { - ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", - buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), - buffer->getPixelFormat()); + ALOGE("Failed to create image. id=%" PRIx64 " size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", + buffer->getId(), buffer->getWidth(), buffer->getHeight(), buffer->getStride(), + buffer->getUsage(), buffer->getPixelFormat()); return NO_INIT; } @@ -773,6 +825,12 @@ void GLESRenderEngine::unbindExternalTextureBufferInternal(uint64_t bufferId) { ALOGV("Failed to find image for buffer: %" PRIu64, bufferId); } +int GLESRenderEngine::getContextPriority() { + int value; + eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value); + return value; +} + FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) { // Translate win by the rounded corners rect coordinates, to have all values in // layer coordinate space. @@ -945,6 +1003,7 @@ bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) { // Bind the texture to placeholder so that backing image data can be freed. GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing()); glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer); + // Release the cached fence here, so that we don't churn reallocations when // we could no-op repeated calls of this method instead. mLastDrawFence = nullptr; @@ -952,6 +1011,20 @@ bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) { return true; } +void GLESRenderEngine::cleanFramebufferCache() { + std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); + // Bind the texture to placeholder so that backing image data can be freed. + GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing()); + glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer); + + while (!mFramebufferImageCache.empty()) { + EGLImageKHR expired = mFramebufferImageCache.front().second; + mFramebufferImageCache.pop_front(); + eglDestroyImageKHR(mEGLDisplay, expired); + DEBUG_EGL_IMAGE_TRACKER_DESTROY(); + } +} + void GLESRenderEngine::checkErrors() const { checkErrors(nullptr); } @@ -1028,7 +1101,7 @@ EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, - ANativeWindowBuffer* const buffer, + const sp<GraphicBuffer>& buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) { ATRACE_CALL(); @@ -1065,7 +1138,9 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, const auto blurLayersSize = blurLayers.size(); if (blurLayersSize == 0) { - fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, useFramebufferCache); + fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, + buffer.get()->getNativeBuffer(), + useFramebufferCache); if (fbo->getStatus() != NO_ERROR) { ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", buffer->handle); @@ -1123,7 +1198,9 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, if (blurLayers.size() == 0) { // Done blurring, time to bind the native FBO and render our blur onto it. - fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, + fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, + buffer.get() + ->getNativeBuffer(), useFramebufferCache); status = fbo->getStatus(); setViewportAndProjection(display.physicalDisplay, display.clip); @@ -1190,6 +1267,11 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, texCoords[2] = vec2(1.0, 1.0); texCoords[3] = vec2(1.0, 0.0); setupLayerTexturing(texture); + + // Do not cache protected EGLImage, protected memory is limited. + if (gBuf->getUsage() & GRALLOC_USAGE_PROTECTED) { + unbindExternalTextureBuffer(gBuf->getId()); + } } const half3 solidColor = layer->source.solidColor; @@ -1564,7 +1646,8 @@ GLESRenderEngine::GlesVersion GLESRenderEngine::parseGlesVersion(const char* str } EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config, - EGLContext shareContext, bool useContextPriority, + EGLContext shareContext, + std::optional<ContextPriority> contextPriority, Protection protection) { EGLint renderableType = 0; if (config == EGL_NO_CONFIG) { @@ -1587,9 +1670,23 @@ EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig conf contextAttributes.reserve(7); contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION); contextAttributes.push_back(contextClientVersion); - if (useContextPriority) { + if (contextPriority) { contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG); - contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG); + switch (*contextPriority) { + case ContextPriority::REALTIME: + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_REALTIME_NV); + break; + case ContextPriority::MEDIUM: + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_MEDIUM_IMG); + break; + case ContextPriority::LOW: + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LOW_IMG); + break; + case ContextPriority::HIGH: + default: + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG); + break; + } } if (protection == Protection::PROTECTED) { contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT); @@ -1766,7 +1863,7 @@ void GLESRenderEngine::handleShadow(const FloatRect& casterRect, float casterCor mState.cornerRadius = 0.0f; mState.drawShadows = true; - setupLayerTexturing(mShadowTexture.getTexture()); + setupLayerTexturing(mShadowTexture->getTexture()); drawMesh(mesh); mState.drawShadows = false; } diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index d554041f6c..64d6c6bdd7 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -48,7 +48,7 @@ namespace gl { class GLImage; class BlurFilter; -class GLESRenderEngine : public impl::RenderEngine { +class GLESRenderEngine : public RenderEngine { public: static std::unique_ptr<GLESRenderEngine> create(const RenderEngineCreationArgs& args); @@ -60,22 +60,18 @@ public: void primeCache() const override; void genTextures(size_t count, uint32_t* names) override; void deleteTextures(size_t count, uint32_t const* names) override; - void bindExternalTextureImage(uint32_t texName, const Image& image) override; - status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, - const sp<Fence>& fence) EXCLUDES(mRenderingMutex); void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex); void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex); - status_t bindFrameBuffer(Framebuffer* framebuffer) override; - void unbindFrameBuffer(Framebuffer* framebuffer) override; bool isProtected() const override { return mInProtectedContext; } bool supportsProtectedContent() const override; bool useProtectedContext(bool useProtectedContext) override; status_t drawLayers(const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, - ANativeWindowBuffer* buffer, const bool useFramebufferCache, + const sp<GraphicBuffer>& buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) override; bool cleanupPostRender(CleanupMode mode) override; + int getContextPriority() override; EGLDisplay getEGLDisplay() const { return mEGLDisplay; } // Creates an output image for rendering to @@ -102,13 +98,15 @@ public: std::shared_ptr<ImageManager::Barrier> unbindExternalTextureBufferForTesting(uint64_t bufferId); protected: - Framebuffer* getFramebufferForDrawing() override; + Framebuffer* getFramebufferForDrawing(); void dump(std::string& result) override EXCLUDES(mRenderingMutex) EXCLUDES(mFramebufferImageCacheMutex); size_t getMaxTextureSize() const override; size_t getMaxViewportDims() const override; private: + friend class BindNativeBufferAsFramebuffer; + enum GlesVersion { GLES_VERSION_1_0 = 0x10000, GLES_VERSION_1_1 = 0x10001, @@ -119,8 +117,11 @@ private: static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); static GlesVersion parseGlesVersion(const char* str); static EGLContext createEglContext(EGLDisplay display, EGLConfig config, - EGLContext shareContext, bool useContextPriority, + EGLContext shareContext, + std::optional<ContextPriority> contextPriority, Protection protection); + static std::optional<RenderEngine::ContextPriority> createContextPriority( + const RenderEngineCreationArgs& args); static EGLSurface createStubEglPbufferSurface(EGLDisplay display, EGLConfig config, int hwcFormat, Protection protection); std::unique_ptr<Framebuffer> createFramebuffer(); @@ -133,6 +134,12 @@ private: status_t cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex); void unbindExternalTextureBufferInternal(uint64_t bufferId) EXCLUDES(mRenderingMutex); + status_t bindFrameBuffer(Framebuffer* framebuffer); + void unbindFrameBuffer(Framebuffer* framebuffer); + void bindExternalTextureImage(uint32_t texName, const Image& image); + void bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, + const sp<Fence>& fence) EXCLUDES(mRenderingMutex); + void cleanFramebufferCache() EXCLUDES(mFramebufferImageCacheMutex) override; // A data space is considered HDR data space if it has BT2020 color space // with PQ or HLG transfer function. @@ -190,7 +197,7 @@ private: GLuint mVpWidth; GLuint mVpHeight; Description mState; - GLShadowTexture mShadowTexture; + std::unique_ptr<GLShadowTexture> mShadowTexture = nullptr; mat4 mSrgbToXyz; mat4 mDisplayP3ToXyz; @@ -229,6 +236,10 @@ private: // supports sRGB, DisplayP3 color spaces. const bool mUseColorManagement = false; + // Whether only shaders performing tone mapping from HDR to SDR will be generated on + // primeCache(). + const bool mPrecacheToneMapperShaderOnly = false; + // Cache of GL images that we'll store per GraphicBuffer ID std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex); std::unordered_map<uint32_t, std::optional<uint64_t>> mTextureView; @@ -287,7 +298,7 @@ private: friend class BlurFilter; friend class GenericProgram; std::unique_ptr<FlushTracer> mFlushTracer; - std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this); + std::unique_ptr<ImageManager> mImageManager; }; } // namespace gl diff --git a/libs/renderengine/gl/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp index 2924b0e8b3..3dd534e602 100644 --- a/libs/renderengine/gl/GLExtensions.cpp +++ b/libs/renderengine/gl/GLExtensions.cpp @@ -120,6 +120,10 @@ void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExt if (extensionSet.hasExtension("EGL_KHR_surfaceless_context")) { mHasSurfacelessContext = true; } + + if (extensionSet.hasExtension("EGL_NV_context_priority_realtime")) { + mHasRealtimePriority = true; + } } char const* GLExtensions::getEGLVersion() const { diff --git a/libs/renderengine/gl/GLExtensions.h b/libs/renderengine/gl/GLExtensions.h index ef000090a8..e415ff304a 100644 --- a/libs/renderengine/gl/GLExtensions.h +++ b/libs/renderengine/gl/GLExtensions.h @@ -41,6 +41,7 @@ public: bool hasContextPriority() const { return mHasContextPriority; } bool hasSurfacelessContext() const { return mHasSurfacelessContext; } bool hasProtectedTexture() const { return mHasProtectedTexture; } + bool hasRealtimePriority() const { return mHasRealtimePriority; } void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version, GLubyte const* extensions); @@ -67,6 +68,7 @@ private: bool mHasContextPriority = false; bool mHasSurfacelessContext = false; bool mHasProtectedTexture = false; + bool mHasRealtimePriority = false; String8 mVendor; String8 mRenderer; diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp index 383486b6b8..58d6caa48a 100644 --- a/libs/renderengine/gl/GLFramebuffer.cpp +++ b/libs/renderengine/gl/GLFramebuffer.cpp @@ -38,6 +38,7 @@ GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine) } GLFramebuffer::~GLFramebuffer() { + setNativeWindowBuffer(nullptr, false, false); glDeleteFramebuffers(1, &mFramebufferName); glDeleteTextures(1, &mTextureName); } diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp index a172c562f4..26f6166761 100644 --- a/libs/renderengine/gl/Program.cpp +++ b/libs/renderengine/gl/Program.cpp @@ -82,6 +82,14 @@ Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const c } } +Program::~Program() { + glDetachShader(mProgram, mVertexShader); + glDetachShader(mProgram, mFragmentShader); + glDeleteShader(mVertexShader); + glDeleteShader(mFragmentShader); + glDeleteProgram(mProgram); +} + bool Program::isValid() const { return mInitialized; } diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h index 429264531f..41f1bf865e 100644 --- a/libs/renderengine/gl/Program.h +++ b/libs/renderengine/gl/Program.h @@ -54,7 +54,7 @@ public: }; Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment); - ~Program() = default; + ~Program(); /* whether this object is usable */ bool isValid() const; diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp index 611755effe..5ff92402dc 100644 --- a/libs/renderengine/gl/ProgramCache.cpp +++ b/libs/renderengine/gl/ProgramCache.cpp @@ -181,9 +181,8 @@ ProgramCache::Key ProgramCache::computeKey(const Description& description) { ? Key::OUTPUT_TRANSFORM_MATRIX_ON : Key::OUTPUT_TRANSFORM_MATRIX_OFF) .set(Key::Key::DISPLAY_COLOR_TRANSFORM_MATRIX_MASK, - description.hasDisplayColorMatrix() - ? Key::DISPLAY_COLOR_TRANSFORM_MATRIX_ON - : Key::DISPLAY_COLOR_TRANSFORM_MATRIX_OFF) + description.hasDisplayColorMatrix() ? Key::DISPLAY_COLOR_TRANSFORM_MATRIX_ON + : Key::DISPLAY_COLOR_TRANSFORM_MATRIX_OFF) .set(Key::ROUNDED_CORNERS_MASK, description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF) .set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF); @@ -665,8 +664,7 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) { )__SHADER__"; } - if (needs.hasTransformMatrix() || - (needs.getInputTF() != needs.getOutputTF()) || + if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) || needs.hasDisplayColorMatrix()) { if (needs.needsToneMapping()) { fs << "uniform float displayMaxLuminance;"; @@ -744,8 +742,7 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) { } } - if (needs.hasTransformMatrix() || - (needs.getInputTF() != needs.getOutputTF()) || + if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) || needs.hasDisplayColorMatrix()) { if (!needs.isOpaque() && needs.isPremultiplied()) { // un-premultiply if needed before linearization @@ -753,7 +750,8 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) { fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);"; } fs << "gl_FragColor.rgb = " - "DisplayColorMatrix(OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb))))));"; + "DisplayColorMatrix(OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))" + ")));"; if (!needs.isOpaque() && needs.isPremultiplied()) { // and re-premultiply if needed after gamma correction diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h index b492cb384b..535d21cd52 100644 --- a/libs/renderengine/gl/ProgramCache.h +++ b/libs/renderengine/gl/ProgramCache.h @@ -149,7 +149,8 @@ public: return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON; } inline bool hasDisplayColorMatrix() const { - return (mKey & DISPLAY_COLOR_TRANSFORM_MATRIX_MASK) == DISPLAY_COLOR_TRANSFORM_MATRIX_ON; + return (mKey & DISPLAY_COLOR_TRANSFORM_MATRIX_MASK) == + DISPLAY_COLOR_TRANSFORM_MATRIX_ON; } inline bool hasTransformMatrix() const { return hasInputTransformMatrix() || hasOutputTransformMatrix(); @@ -202,6 +203,8 @@ public: // if none can be found. void useProgram(const EGLContext context, const Description& description); + void purgeCaches() { mCaches.clear(); } + private: // compute a cache Key from a Description static Key computeKey(const Description& description); diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h index ca16d2c727..a637796267 100644 --- a/libs/renderengine/include/renderengine/DisplaySettings.h +++ b/libs/renderengine/include/renderengine/DisplaySettings.h @@ -47,8 +47,8 @@ struct DisplaySettings { // DataSpace::UNKNOWN otherwise. ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN; - // Additional color transform to apply in linear space after transforming - // to the output dataspace. + // Additional color transform to apply after transforming to the output + // dataspace, in non-linear space. mat4 colorTransform = mat4(); // Region that will be cleared to (0, 0, 0, 1) prior to rendering. diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h index 95e9367fea..7661233967 100644 --- a/libs/renderengine/include/renderengine/LayerSettings.h +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -21,12 +21,14 @@ #include <math/mat4.h> #include <math/vec3.h> #include <renderengine/Texture.h> +#include <ui/BlurRegion.h> #include <ui/Fence.h> #include <ui/FloatRect.h> #include <ui/GraphicBuffer.h> #include <ui/GraphicTypes.h> #include <ui/Rect.h> #include <ui/Region.h> +#include <ui/StretchEffect.h> #include <ui/Transform.h> namespace android { @@ -151,6 +153,13 @@ struct LayerSettings { ShadowSettings shadow; int backgroundBlurRadius = 0; + + std::vector<BlurRegion> blurRegions; + + StretchEffect stretchEffect; + + // Name associated with the layer for debugging purposes. + std::string name; }; // Keep in sync with custom comparison function in @@ -182,7 +191,29 @@ static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& r lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent; } +static inline bool operator==(const BlurRegion& lhs, const BlurRegion& rhs) { + return lhs.alpha == rhs.alpha && lhs.cornerRadiusTL == rhs.cornerRadiusTL && + lhs.cornerRadiusTR == rhs.cornerRadiusTR && lhs.cornerRadiusBL == rhs.cornerRadiusBL && + lhs.cornerRadiusBR == rhs.cornerRadiusBR && lhs.blurRadius == rhs.blurRadius && + lhs.left == rhs.left && lhs.top == rhs.top && lhs.right == rhs.right && + lhs.bottom == rhs.bottom; +} + +static inline bool operator!=(const BlurRegion& lhs, const BlurRegion& rhs) { + return !(lhs == rhs); +} + static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) { + if (lhs.blurRegions.size() != rhs.blurRegions.size()) { + return false; + } + const auto size = lhs.blurRegions.size(); + for (size_t i = 0; i < size; i++) { + if (lhs.blurRegions[i] != rhs.blurRegions[i]) { + return false; + } + } + return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha && lhs.sourceDataspace == rhs.sourceDataspace && lhs.colorTransform == rhs.colorTransform && @@ -249,6 +280,10 @@ static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) { *os << "\n .colorTransform = " << settings.colorTransform; *os << "\n .disableBlending = " << settings.disableBlending; *os << "\n .backgroundBlurRadius = " << settings.backgroundBlurRadius; + for (auto blurRegion : settings.blurRegions) { + *os << "\n"; + PrintTo(blurRegion, os); + } *os << "\n .shadow = "; PrintTo(settings.shadow, os); *os << "\n}"; diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 74bc44b22f..506f81ecc2 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -31,10 +31,17 @@ #include <ui/Transform.h> /** - * Allows to set RenderEngine backend to GLES (default) or Vulkan (NOT yet supported). + * Allows to set RenderEngine backend to GLES (default) or SkiaGL (NOT yet supported). */ #define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend" +/** + * Turns on recording of skia commands in SkiaGL version of the RE. This property + * defines number of milliseconds for the recording to take place. A non zero value + * turns on the recording. + */ +#define PROPERTY_DEBUG_RENDERENGINE_CAPTURE_SKIA_MS "debug.renderengine.capture_skia_ms" + struct ANativeWindowBuffer; namespace android { @@ -44,12 +51,15 @@ class Region; namespace renderengine { -class BindNativeBufferAsFramebuffer; class Image; class Mesh; class Texture; struct RenderEngineCreationArgs; +namespace threaded { +class RenderEngineThreaded; +} + namespace impl { class RenderEngine; } @@ -65,9 +75,17 @@ public: LOW = 1, MEDIUM = 2, HIGH = 3, + REALTIME = 4, + }; + + enum class RenderEngineType { + GLES = 1, + THREADED = 2, + SKIA_GL = 3, + SKIA_GL_THREADED = 4, }; - static std::unique_ptr<impl::RenderEngine> create(const RenderEngineCreationArgs& args); + static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args); virtual ~RenderEngine() = 0; @@ -80,16 +98,8 @@ public: // dump the extension strings. always call the base class. virtual void dump(std::string& result) = 0; - virtual bool useNativeFenceSync() const = 0; - virtual bool useWaitSync() const = 0; virtual void genTextures(size_t count, uint32_t* names) = 0; virtual void deleteTextures(size_t count, uint32_t const* names) = 0; - virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0; - // Legacy public method used by devices that don't support native fence - // synchronization in their GPU driver, as this method provides implicit - // synchronization for latching buffers. - virtual status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, - const sp<Fence>& fence) = 0; // Caches Image resources for this buffer, but does not bind the buffer to // a particular texture. // Note that work is deferred to an additional thread, i.e. this call @@ -107,10 +117,6 @@ public: // a buffer should never occur before binding the buffer if the caller // called {bind, cache}ExternalTextureBuffer before calling unbind. virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0; - // When binding a native buffer, it must be done before setViewportAndProjection - // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation. - virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0; - virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0; enum class CleanupMode { CLEAN_OUTPUT_RESOURCES, @@ -174,17 +180,16 @@ public: // now, this always returns NO_ERROR. virtual status_t drawLayers(const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, - ANativeWindowBuffer* buffer, const bool useFramebufferCache, + const sp<GraphicBuffer>& buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0; + virtual void cleanFramebufferCache() = 0; + // Returns the priority this context was actually created with. Note: this may not be + // the same as specified at context creation time, due to implementation limits on the + // number of contexts that can be created at a specific priority level in the system. + virtual int getContextPriority() = 0; protected: - // Gets a framebuffer to render to. This framebuffer may or may not be - // cached depending on the implementation. - // - // Note that this method does not transfer ownership, so the caller most not - // live longer than RenderEngine. - virtual Framebuffer* getFramebufferForDrawing() = 0; - friend class BindNativeBufferAsFramebuffer; + friend class threaded::RenderEngineThreaded; }; struct RenderEngineCreationArgs { @@ -195,26 +200,25 @@ struct RenderEngineCreationArgs { bool precacheToneMapperShaderOnly; bool supportsBackgroundBlur; RenderEngine::ContextPriority contextPriority; + RenderEngine::RenderEngineType renderEngineType; struct Builder; private: // must be created by Builder via constructor with full argument list - RenderEngineCreationArgs( - int _pixelFormat, - uint32_t _imageCacheSize, - bool _useColorManagement, - bool _enableProtectedContext, - bool _precacheToneMapperShaderOnly, - bool _supportsBackgroundBlur, - RenderEngine::ContextPriority _contextPriority) - : pixelFormat(_pixelFormat) - , imageCacheSize(_imageCacheSize) - , useColorManagement(_useColorManagement) - , enableProtectedContext(_enableProtectedContext) - , precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly) - , supportsBackgroundBlur(_supportsBackgroundBlur) - , contextPriority(_contextPriority) {} + RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize, bool _useColorManagement, + bool _enableProtectedContext, bool _precacheToneMapperShaderOnly, + bool _supportsBackgroundBlur, + RenderEngine::ContextPriority _contextPriority, + RenderEngine::RenderEngineType _renderEngineType) + : pixelFormat(_pixelFormat), + imageCacheSize(_imageCacheSize), + useColorManagement(_useColorManagement), + enableProtectedContext(_enableProtectedContext), + precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly), + supportsBackgroundBlur(_supportsBackgroundBlur), + contextPriority(_contextPriority), + renderEngineType(_renderEngineType) {} RenderEngineCreationArgs() = delete; }; @@ -249,10 +253,14 @@ struct RenderEngineCreationArgs::Builder { this->contextPriority = contextPriority; return *this; } + Builder& setRenderEngineType(RenderEngine::RenderEngineType renderEngineType) { + this->renderEngineType = renderEngineType; + return *this; + } RenderEngineCreationArgs build() const { return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement, enableProtectedContext, precacheToneMapperShaderOnly, - supportsBackgroundBlur, contextPriority); + supportsBackgroundBlur, contextPriority, renderEngineType); } private: @@ -264,46 +272,9 @@ private: bool precacheToneMapperShaderOnly = false; bool supportsBackgroundBlur = false; RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM; + RenderEngine::RenderEngineType renderEngineType = RenderEngine::RenderEngineType::GLES; }; -class BindNativeBufferAsFramebuffer { -public: - BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer, - const bool useFramebufferCache) - : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) { - mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(), - useFramebufferCache) - ? mEngine.bindFrameBuffer(mFramebuffer) - : NO_MEMORY; - } - ~BindNativeBufferAsFramebuffer() { - mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true); - mEngine.unbindFrameBuffer(mFramebuffer); - } - status_t getStatus() const { return mStatus; } - -private: - RenderEngine& mEngine; - Framebuffer* mFramebuffer; - status_t mStatus; -}; - -namespace impl { - -// impl::RenderEngine contains common implementation that is graphics back-end agnostic. -class RenderEngine : public renderengine::RenderEngine { -public: - virtual ~RenderEngine() = 0; - - bool useNativeFenceSync() const override; - bool useWaitSync() const override; - -protected: - RenderEngine(const RenderEngineCreationArgs& args); - const RenderEngineCreationArgs mArgs; -}; - -} // namespace impl } // namespace renderengine } // namespace android diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index 101cbb70fd..2c34da40f8 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -35,21 +35,12 @@ public: RenderEngine(); ~RenderEngine() override; - MOCK_METHOD0(getFramebufferForDrawing, Framebuffer*()); MOCK_CONST_METHOD0(primeCache, void()); MOCK_METHOD1(dump, void(std::string&)); - MOCK_CONST_METHOD0(useNativeFenceSync, bool()); - MOCK_CONST_METHOD0(useWaitSync, bool()); - MOCK_CONST_METHOD0(isCurrent, bool()); MOCK_METHOD2(genTextures, void(size_t, uint32_t*)); MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*)); - MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&)); MOCK_METHOD1(cacheExternalTextureBuffer, void(const sp<GraphicBuffer>&)); - MOCK_METHOD3(bindExternalTextureBuffer, - status_t(uint32_t, const sp<GraphicBuffer>&, const sp<Fence>&)); MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t)); - MOCK_METHOD1(bindFrameBuffer, status_t(renderengine::Framebuffer*)); - MOCK_METHOD1(unbindFrameBuffer, void(renderengine::Framebuffer*)); MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&)); MOCK_CONST_METHOD0(getMaxTextureSize, size_t()); MOCK_CONST_METHOD0(getMaxViewportDims, size_t()); @@ -59,7 +50,10 @@ public: MOCK_METHOD1(cleanupPostRender, bool(CleanupMode mode)); MOCK_METHOD6(drawLayers, status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&, - ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*)); + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, + base::unique_fd*)); + MOCK_METHOD0(cleanFramebufferCache, void()); + MOCK_METHOD0(getContextPriority, int()); }; } // namespace mock diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp new file mode 100644 index 0000000000..c535597aea --- /dev/null +++ b/libs/renderengine/skia/AutoBackendTexture.cpp @@ -0,0 +1,122 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AutoBackendTexture.h" + +#undef LOG_TAG +#define LOG_TAG "RenderEngine" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "ColorSpaces.h" +#include "log/log_main.h" +#include "utils/Trace.h" + +namespace android { +namespace renderengine { +namespace skia { + +AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, + bool isRender) { + ATRACE_CALL(); + AHardwareBuffer_Desc desc; + AHardwareBuffer_describe(buffer, &desc); + bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT); + GrBackendFormat backendFormat = + GrAHardwareBufferUtils::GetBackendFormat(context, buffer, desc.format, false); + mBackendTexture = + GrAHardwareBufferUtils::MakeBackendTexture(context, buffer, desc.width, desc.height, + &mDeleteProc, &mUpdateProc, &mImageCtx, + createProtectedImage, backendFormat, + isRender); + mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format); +} + +void AutoBackendTexture::unref(bool releaseLocalResources) { + if (releaseLocalResources) { + mSurface = nullptr; + mImage = nullptr; + } + + mUsageCount--; + if (mUsageCount <= 0) { + if (mBackendTexture.isValid()) { + mDeleteProc(mImageCtx); + mBackendTexture = {}; + } + delete this; + } +} + +// releaseSurfaceProc is invoked by SkSurface, when the texture is no longer in use. +// "releaseContext" contains an "AutoBackendTexture*". +void AutoBackendTexture::releaseSurfaceProc(SkSurface::ReleaseContext releaseContext) { + AutoBackendTexture* textureRelease = reinterpret_cast<AutoBackendTexture*>(releaseContext); + textureRelease->unref(false); +} + +// releaseImageProc is invoked by SkImage, when the texture is no longer in use. +// "releaseContext" contains an "AutoBackendTexture*". +void AutoBackendTexture::releaseImageProc(SkImage::ReleaseContext releaseContext) { + AutoBackendTexture* textureRelease = reinterpret_cast<AutoBackendTexture*>(releaseContext); + textureRelease->unref(false); +} + +sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaType alphaType, + GrDirectContext* context) { + ATRACE_CALL(); + + if (mBackendTexture.isValid()) { + mUpdateProc(mImageCtx, context); + } + + sk_sp<SkImage> image = + SkImage::MakeFromTexture(context, mBackendTexture, kTopLeft_GrSurfaceOrigin, mColorType, + alphaType, toSkColorSpace(dataspace), releaseImageProc, this); + if (image.get()) { + // The following ref will be counteracted by releaseProc, when SkImage is discarded. + ref(); + } + + mImage = image; + mDataspace = dataspace; + LOG_ALWAYS_FATAL_IF(mImage == nullptr, "Unable to generate SkImage from buffer"); + return mImage; +} + +sk_sp<SkSurface> AutoBackendTexture::getOrCreateSurface(ui::Dataspace dataspace, + GrDirectContext* context) { + ATRACE_CALL(); + if (!mSurface.get() || mDataspace != dataspace) { + sk_sp<SkSurface> surface = + SkSurface::MakeFromBackendTexture(context, mBackendTexture, + kTopLeft_GrSurfaceOrigin, 0, mColorType, + toSkColorSpace(dataspace), nullptr, + releaseSurfaceProc, this); + if (surface.get()) { + // The following ref will be counteracted by releaseProc, when SkSurface is discarded. + ref(); + } + mSurface = surface; + } + + mDataspace = dataspace; + LOG_ALWAYS_FATAL_IF(mSurface == nullptr, "Unable to generate SkSurface"); + return mSurface; +} + +} // namespace skia +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h new file mode 100644 index 0000000000..bb758780e1 --- /dev/null +++ b/libs/renderengine/skia/AutoBackendTexture.h @@ -0,0 +1,111 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <GrAHardwareBufferUtils.h> +#include <GrDirectContext.h> +#include <SkImage.h> +#include <SkSurface.h> +#include <sys/types.h> + +#include "android-base/macros.h" +#include "ui/GraphicTypes.h" + +namespace android { +namespace renderengine { +namespace skia { + +/** + * AutoBackendTexture manages GPU image lifetime. It is a ref-counted object + * that keeps GPU resources alive until the last SkImage or SkSurface object using them is + * destroyed. + */ +class AutoBackendTexture { +public: + // Local reference that supports RAII-style management of an AutoBackendTexture + // AutoBackendTexture by itself can't be managed in a similar fashion because + // of shared ownership with Skia objects, so we wrap it here instead. + class LocalRef { + public: + LocalRef() {} + + ~LocalRef() { + // Destroying the texture is the same as setting it to null + setTexture(nullptr); + } + + // Sets the texture to locally ref-track. + void setTexture(AutoBackendTexture* texture) { + if (mTexture != nullptr) { + mTexture->unref(true); + } + + mTexture = texture; + if (mTexture != nullptr) { + mTexture->ref(); + } + } + + AutoBackendTexture* getTexture() const { return mTexture; } + + DISALLOW_COPY_AND_ASSIGN(LocalRef); + + private: + AutoBackendTexture* mTexture = nullptr; + }; + + // Creates a GrBackendTexture whose contents come from the provided buffer. + AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isRender); + + void ref() { mUsageCount++; } + + // releaseLocalResources is true if the underlying SkImage and SkSurface + // should be deleted from local tracking. + void unref(bool releaseLocalResources); + + // Makes a new SkImage from the texture content. + // As SkImages are immutable but buffer content is not, we create + // a new SkImage every time. + sk_sp<SkImage> makeImage(ui::Dataspace dataspace, SkAlphaType alphaType, + GrDirectContext* context); + + // Makes a new SkSurface from the texture content, if needed. + sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace, GrDirectContext* context); + +private: + // The only way to invoke dtor is with unref, when mUsageCount is 0. + ~AutoBackendTexture() {} + + GrBackendTexture mBackendTexture; + GrAHardwareBufferUtils::DeleteImageProc mDeleteProc; + GrAHardwareBufferUtils::UpdateImageProc mUpdateProc; + GrAHardwareBufferUtils::TexImageCtx mImageCtx; + + static void releaseSurfaceProc(SkSurface::ReleaseContext releaseContext); + static void releaseImageProc(SkImage::ReleaseContext releaseContext); + + int mUsageCount = 0; + + sk_sp<SkImage> mImage = nullptr; + sk_sp<SkSurface> mSurface = nullptr; + ui::Dataspace mDataspace = ui::Dataspace::UNKNOWN; + SkColorType mColorType = kUnknown_SkColorType; +}; + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/ColorSpaces.cpp b/libs/renderengine/skia/ColorSpaces.cpp new file mode 100644 index 0000000000..ff4d348f0d --- /dev/null +++ b/libs/renderengine/skia/ColorSpaces.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2021 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 "ColorSpaces.h" + +namespace android { +namespace renderengine { +namespace skia { + +sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) { + skcms_Matrix3x3 gamut; + switch (dataspace & HAL_DATASPACE_STANDARD_MASK) { + case HAL_DATASPACE_STANDARD_BT709: + gamut = SkNamedGamut::kSRGB; + break; + case HAL_DATASPACE_STANDARD_BT2020: + gamut = SkNamedGamut::kRec2020; + break; + case HAL_DATASPACE_STANDARD_DCI_P3: + gamut = SkNamedGamut::kDisplayP3; + break; + default: + gamut = SkNamedGamut::kSRGB; + break; + } + + switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_LINEAR: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut); + case HAL_DATASPACE_TRANSFER_SRGB: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut); + case HAL_DATASPACE_TRANSFER_ST2084: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut); + case HAL_DATASPACE_TRANSFER_HLG: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut); + default: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut); + } +} + +} // namespace skia +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/skia/ColorSpaces.h b/libs/renderengine/skia/ColorSpaces.h new file mode 100644 index 0000000000..2cbdeb8fcf --- /dev/null +++ b/libs/renderengine/skia/ColorSpaces.h @@ -0,0 +1,38 @@ +/* + * Copyright 2021 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 "SkColorSpace.h" +#include "ui/GraphicTypes.h" + +namespace android { +namespace renderengine { +namespace skia { + +// Converts an android dataspace to a supported SkColorSpace +// Supported dataspaces are +// 1. sRGB +// 2. Display P3 +// 3. BT2020 PQ +// 4. BT2020 HLG +// Unknown primaries are mapped to BT709, and unknown transfer functions +// are mapped to sRGB. +sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace); + +} // namespace skia +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp new file mode 100644 index 0000000000..55ec6ad351 --- /dev/null +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -0,0 +1,1166 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "RenderEngine" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "SkiaGLRenderEngine.h" + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GrContextOptions.h> +#include <SkCanvas.h> +#include <SkColorFilter.h> +#include <SkColorMatrix.h> +#include <SkColorSpace.h> +#include <SkImage.h> +#include <SkImageFilters.h> +#include <SkRegion.h> +#include <SkShadowUtils.h> +#include <SkSurface.h> +#include <android-base/stringprintf.h> +#include <gl/GrGLInterface.h> +#include <sync/sync.h> +#include <ui/BlurRegion.h> +#include <ui/DebugUtils.h> +#include <ui/GraphicBuffer.h> +#include <utils/Trace.h> + +#include <cmath> +#include <cstdint> +#include <memory> + +#include "../gl/GLExtensions.h" +#include "ColorSpaces.h" +#include "SkBlendMode.h" +#include "SkImageInfo.h" +#include "filters/BlurFilter.h" +#include "filters/LinearEffect.h" +#include "log/log_main.h" +#include "skia/debug/SkiaCapture.h" +#include "system/graphics-base-v1.0.h" + +bool checkGlError(const char* op, int lineNumber); + +namespace android { +namespace renderengine { +namespace skia { + +using base::StringAppendF; + +static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute, + EGLint wanted, EGLConfig* outConfig) { + EGLint numConfigs = -1, n = 0; + eglGetConfigs(dpy, nullptr, 0, &numConfigs); + std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR); + eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n); + configs.resize(n); + + if (!configs.empty()) { + if (attribute != EGL_NONE) { + for (EGLConfig config : configs) { + EGLint value = 0; + eglGetConfigAttrib(dpy, config, attribute, &value); + if (wanted == value) { + *outConfig = config; + return NO_ERROR; + } + } + } else { + // just pick the first one + *outConfig = configs[0]; + return NO_ERROR; + } + } + + return NAME_NOT_FOUND; +} + +static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType, + EGLConfig* config) { + // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if + // it is to be used with WIFI displays + status_t err; + EGLint wantedAttribute; + EGLint wantedAttributeValue; + + std::vector<EGLint> attribs; + if (renderableType) { + const ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(format); + const bool is1010102 = pixelFormat == ui::PixelFormat::RGBA_1010102; + + // Default to 8 bits per channel. + const EGLint tmpAttribs[] = { + EGL_RENDERABLE_TYPE, + renderableType, + EGL_RECORDABLE_ANDROID, + EGL_TRUE, + EGL_SURFACE_TYPE, + EGL_WINDOW_BIT | EGL_PBUFFER_BIT, + EGL_FRAMEBUFFER_TARGET_ANDROID, + EGL_TRUE, + EGL_RED_SIZE, + is1010102 ? 10 : 8, + EGL_GREEN_SIZE, + is1010102 ? 10 : 8, + EGL_BLUE_SIZE, + is1010102 ? 10 : 8, + EGL_ALPHA_SIZE, + is1010102 ? 2 : 8, + EGL_NONE, + }; + std::copy(tmpAttribs, tmpAttribs + (sizeof(tmpAttribs) / sizeof(EGLint)), + std::back_inserter(attribs)); + wantedAttribute = EGL_NONE; + wantedAttributeValue = EGL_NONE; + } else { + // if no renderable type specified, fallback to a simplified query + wantedAttribute = EGL_NATIVE_VISUAL_ID; + wantedAttributeValue = format; + } + + err = selectConfigForAttribute(display, attribs.data(), wantedAttribute, wantedAttributeValue, + config); + if (err == NO_ERROR) { + EGLint caveat; + if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat)) + ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!"); + } + + return err; +} + +std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create( + const RenderEngineCreationArgs& args) { + // initialize EGL for the default display + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!eglInitialize(display, nullptr, nullptr)) { + LOG_ALWAYS_FATAL("failed to initialize EGL"); + } + + const auto eglVersion = eglQueryString(display, EGL_VERSION); + if (!eglVersion) { + checkGlError(__FUNCTION__, __LINE__); + LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed"); + } + + const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS); + if (!eglExtensions) { + checkGlError(__FUNCTION__, __LINE__); + LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed"); + } + + auto& extensions = gl::GLExtensions::getInstance(); + extensions.initWithEGLStrings(eglVersion, eglExtensions); + + // The code assumes that ES2 or later is available if this extension is + // supported. + EGLConfig config = EGL_NO_CONFIG_KHR; + if (!extensions.hasNoConfigContext()) { + config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true); + } + + EGLContext protectedContext = EGL_NO_CONTEXT; + const std::optional<RenderEngine::ContextPriority> priority = createContextPriority(args); + if (args.enableProtectedContext && extensions.hasProtectedContent()) { + protectedContext = + createEglContext(display, config, nullptr, priority, Protection::PROTECTED); + ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context"); + } + + EGLContext ctxt = + createEglContext(display, config, protectedContext, priority, Protection::UNPROTECTED); + + // if can't create a GL context, we can only abort. + LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed"); + + EGLSurface placeholder = EGL_NO_SURFACE; + if (!extensions.hasSurfacelessContext()) { + placeholder = createPlaceholderEglPbufferSurface(display, config, args.pixelFormat, + Protection::UNPROTECTED); + LOG_ALWAYS_FATAL_IF(placeholder == EGL_NO_SURFACE, "can't create placeholder pbuffer"); + } + EGLBoolean success = eglMakeCurrent(display, placeholder, placeholder, ctxt); + LOG_ALWAYS_FATAL_IF(!success, "can't make placeholder pbuffer current"); + extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER), + glGetString(GL_VERSION), glGetString(GL_EXTENSIONS)); + + EGLSurface protectedPlaceholder = EGL_NO_SURFACE; + if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) { + protectedPlaceholder = createPlaceholderEglPbufferSurface(display, config, args.pixelFormat, + Protection::PROTECTED); + ALOGE_IF(protectedPlaceholder == EGL_NO_SURFACE, + "can't create protected placeholder pbuffer"); + } + + // initialize the renderer while GL is current + std::unique_ptr<SkiaGLRenderEngine> engine = + std::make_unique<SkiaGLRenderEngine>(args, display, ctxt, placeholder, protectedContext, + protectedPlaceholder); + + ALOGI("OpenGL ES informations:"); + ALOGI("vendor : %s", extensions.getVendor()); + ALOGI("renderer : %s", extensions.getRenderer()); + ALOGI("version : %s", extensions.getVersion()); + ALOGI("extensions: %s", extensions.getExtensions()); + ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize()); + ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims()); + + return engine; +} + +EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) { + status_t err; + EGLConfig config; + + // First try to get an ES3 config + err = selectEGLConfig(display, format, EGL_OPENGL_ES3_BIT, &config); + if (err != NO_ERROR) { + // If ES3 fails, try to get an ES2 config + err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config); + if (err != NO_ERROR) { + // If ES2 still doesn't work, probably because we're on the emulator. + // try a simplified query + ALOGW("no suitable EGLConfig found, trying a simpler query"); + err = selectEGLConfig(display, format, 0, &config); + if (err != NO_ERROR) { + // this EGL is too lame for android + LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"); + } + } + } + + if (logConfig) { + // print some debugging info + EGLint r, g, b, a; + eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r); + eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g); + eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b); + eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a); + ALOGI("EGL information:"); + ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); + ALOGI("version : %s", eglQueryString(display, EGL_VERSION)); + ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS)); + ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported"); + ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); + } + + return config; +} + +SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, + EGLContext ctxt, EGLSurface placeholder, + EGLContext protectedContext, EGLSurface protectedPlaceholder) + : mEGLDisplay(display), + mEGLContext(ctxt), + mPlaceholderSurface(placeholder), + mProtectedEGLContext(protectedContext), + mProtectedPlaceholderSurface(protectedPlaceholder), + mUseColorManagement(args.useColorManagement), + mRenderEngineType(args.renderEngineType) { + sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface()); + LOG_ALWAYS_FATAL_IF(!glInterface.get()); + + GrContextOptions options; + options.fPreferExternalImagesOverES3 = true; + options.fDisableDistanceFieldPaths = true; + mGrContext = GrDirectContext::MakeGL(glInterface, options); + if (useProtectedContext(true)) { + mProtectedGrContext = GrDirectContext::MakeGL(glInterface, options); + useProtectedContext(false); + } + + if (args.supportsBackgroundBlur) { + ALOGD("Background Blurs Enabled"); + mBlurFilter = new BlurFilter(); + } + mCapture = std::make_unique<SkiaCapture>(); +} + +SkiaGLRenderEngine::~SkiaGLRenderEngine() { + std::lock_guard<std::mutex> lock(mRenderingMutex); + mRuntimeEffects.clear(); + mProtectedTextureCache.clear(); + mTextureCache.clear(); + + if (mBlurFilter) { + delete mBlurFilter; + } + + mCapture = nullptr; + + mGrContext->flushAndSubmit(true); + mGrContext->abandonContext(); + + if (mProtectedGrContext) { + mProtectedGrContext->flushAndSubmit(true); + mProtectedGrContext->abandonContext(); + } + + if (mPlaceholderSurface != EGL_NO_SURFACE) { + eglDestroySurface(mEGLDisplay, mPlaceholderSurface); + } + if (mProtectedPlaceholderSurface != EGL_NO_SURFACE) { + eglDestroySurface(mEGLDisplay, mProtectedPlaceholderSurface); + } + if (mEGLContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEGLDisplay, mEGLContext); + } + if (mProtectedEGLContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEGLDisplay, mProtectedEGLContext); + } + eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(mEGLDisplay); + eglReleaseThread(); +} + +bool SkiaGLRenderEngine::supportsProtectedContent() const { + return mProtectedEGLContext != EGL_NO_CONTEXT; +} + +bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) { + if (useProtectedContext == mInProtectedContext) { + return true; + } + if (useProtectedContext && !supportsProtectedContent()) { + return false; + } + const EGLSurface surface = + useProtectedContext ? mProtectedPlaceholderSurface : mPlaceholderSurface; + const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext; + const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE; + + if (success) { + mInProtectedContext = useProtectedContext; + } + return success; +} + +base::unique_fd SkiaGLRenderEngine::flush() { + ATRACE_CALL(); + if (!gl::GLExtensions::getInstance().hasNativeFenceSync()) { + return base::unique_fd(); + } + + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + ALOGW("failed to create EGL native fence sync: %#x", eglGetError()); + return base::unique_fd(); + } + + // native fence fd will not be populated until flush() is done. + glFlush(); + + // get the fence fd + base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync)); + eglDestroySyncKHR(mEGLDisplay, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + ALOGW("failed to dup EGL native fence sync: %#x", eglGetError()); + } + + return fenceFd; +} + +bool SkiaGLRenderEngine::waitFence(base::unique_fd fenceFd) { + if (!gl::GLExtensions::getInstance().hasNativeFenceSync() || + !gl::GLExtensions::getInstance().hasWaitSync()) { + return false; + } + + // release the fd and transfer the ownership to EGLSync + EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), EGL_NONE}; + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + ALOGE("failed to create EGL native fence sync: %#x", eglGetError()); + return false; + } + + // 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(mEGLDisplay, sync, 0); + EGLint error = eglGetError(); + eglDestroySyncKHR(mEGLDisplay, sync); + if (error != EGL_SUCCESS) { + ALOGE("failed to wait for EGL native fence sync: %#x", error); + return false; + } + + return true; +} + +static bool hasUsage(const AHardwareBuffer_Desc& desc, uint64_t usage) { + return !!(desc.usage & usage); +} + +static float toDegrees(uint32_t transform) { + switch (transform) { + case ui::Transform::ROT_90: + return 90.0; + case ui::Transform::ROT_180: + return 180.0; + case ui::Transform::ROT_270: + return 270.0; + default: + return 0.0; + } +} + +static SkColorMatrix toSkColorMatrix(const mat4& matrix) { + return SkColorMatrix(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], 0, matrix[0][1], + matrix[1][1], matrix[2][1], matrix[3][1], 0, matrix[0][2], matrix[1][2], + matrix[2][2], matrix[3][2], 0, matrix[0][3], matrix[1][3], matrix[2][3], + matrix[3][3], 0); +} + +static bool needsToneMapping(ui::Dataspace sourceDataspace, ui::Dataspace destinationDataspace) { + int64_t sourceTransfer = sourceDataspace & HAL_DATASPACE_TRANSFER_MASK; + int64_t destTransfer = destinationDataspace & HAL_DATASPACE_TRANSFER_MASK; + + // Treat unsupported dataspaces as srgb + if (destTransfer != HAL_DATASPACE_TRANSFER_LINEAR && + destTransfer != HAL_DATASPACE_TRANSFER_HLG && + destTransfer != HAL_DATASPACE_TRANSFER_ST2084) { + destTransfer = HAL_DATASPACE_TRANSFER_SRGB; + } + + if (sourceTransfer != HAL_DATASPACE_TRANSFER_LINEAR && + sourceTransfer != HAL_DATASPACE_TRANSFER_HLG && + sourceTransfer != HAL_DATASPACE_TRANSFER_ST2084) { + sourceTransfer = HAL_DATASPACE_TRANSFER_SRGB; + } + + const bool isSourceLinear = sourceTransfer == HAL_DATASPACE_TRANSFER_LINEAR; + const bool isSourceSRGB = sourceTransfer == HAL_DATASPACE_TRANSFER_SRGB; + const bool isDestLinear = destTransfer == HAL_DATASPACE_TRANSFER_LINEAR; + const bool isDestSRGB = destTransfer == HAL_DATASPACE_TRANSFER_SRGB; + + return !(isSourceLinear && isDestSRGB) && !(isSourceSRGB && isDestLinear) && + sourceTransfer != destTransfer; +} + +static bool needsLinearEffect(const mat4& colorTransform, ui::Dataspace sourceDataspace, + ui::Dataspace destinationDataspace) { + return colorTransform != mat4() || needsToneMapping(sourceDataspace, destinationDataspace); +} + +void SkiaGLRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { + // Only run this if RE is running on its own thread. This way the access to GL + // operations is guaranteed to be happening on the same thread. + if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) { + return; + } + ATRACE_CALL(); + + std::lock_guard<std::mutex> lock(mRenderingMutex); + auto iter = mTextureCache.find(buffer->getId()); + if (iter != mTextureCache.end()) { + ALOGV("Texture already exists in cache."); + return; + } else { + std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = + std::make_shared<AutoBackendTexture::LocalRef>(); + imageTextureRef->setTexture( + new AutoBackendTexture(mGrContext.get(), buffer->toAHardwareBuffer(), false)); + mTextureCache.insert({buffer->getId(), imageTextureRef}); + } +} + +void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) { + ATRACE_CALL(); + std::lock_guard<std::mutex> lock(mRenderingMutex); + mTextureCache.erase(bufferId); + mProtectedTextureCache.erase(bufferId); +} + +sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader, + const LayerSettings* layer, + const DisplaySettings& display, + bool undoPremultipliedAlpha) { + if (layer->stretchEffect.hasEffect()) { + // TODO: Implement + } + if (mUseColorManagement && + needsLinearEffect(layer->colorTransform, layer->sourceDataspace, display.outputDataspace)) { + LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace, + .outputDataspace = display.outputDataspace, + .undoPremultipliedAlpha = undoPremultipliedAlpha}; + + auto effectIter = mRuntimeEffects.find(effect); + sk_sp<SkRuntimeEffect> runtimeEffect = nullptr; + if (effectIter == mRuntimeEffects.end()) { + runtimeEffect = buildRuntimeEffect(effect); + mRuntimeEffects.insert({effect, runtimeEffect}); + } else { + runtimeEffect = effectIter->second; + } + return createLinearEffectShader(shader, effect, runtimeEffect, layer->colorTransform, + display.maxLuminance, + layer->source.buffer.maxMasteringLuminance, + layer->source.buffer.maxContentLuminance); + } + return shader; +} + +void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) { + if (CC_UNLIKELY(mCapture->isCaptureRunning())) { + // Record display settings when capture is running. + std::stringstream displaySettings; + PrintTo(display, &displaySettings); + // Store the DisplaySettings in additional information. + canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings", + SkData::MakeWithCString(displaySettings.str().c_str())); + } + + // Before doing any drawing, let's make sure that we'll start at the origin of the display. + // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual + // displays might have different scaling when compared to the physical screen. + + canvas->clipRect(getSkRect(display.physicalDisplay)); + canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top); + + const auto clipWidth = display.clip.width(); + const auto clipHeight = display.clip.height(); + auto rotatedClipWidth = clipWidth; + auto rotatedClipHeight = clipHeight; + // Scale is contingent on the rotation result. + if (display.orientation & ui::Transform::ROT_90) { + std::swap(rotatedClipWidth, rotatedClipHeight); + } + const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) / + static_cast<SkScalar>(rotatedClipWidth); + const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) / + static_cast<SkScalar>(rotatedClipHeight); + canvas->scale(scaleX, scaleY); + + // Canvas rotation is done by centering the clip window at the origin, rotating, translating + // back so that the top left corner of the clip is at (0, 0). + canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2); + canvas->rotate(toDegrees(display.orientation)); + canvas->translate(-clipWidth / 2, -clipHeight / 2); + canvas->translate(-display.clip.left, -display.clip.top); +} + +status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, + const std::vector<const LayerSettings*>& layers, + const sp<GraphicBuffer>& buffer, + const bool useFramebufferCache, + base::unique_fd&& bufferFence, base::unique_fd* drawFence) { + ATRACE_NAME("SkiaGL::drawLayers"); + + std::lock_guard<std::mutex> lock(mRenderingMutex); + if (layers.empty()) { + ALOGV("Drawing empty layer stack"); + return NO_ERROR; + } + + if (bufferFence.get() >= 0) { + // Duplicate the fence for passing to waitFence. + base::unique_fd bufferFenceDup(dup(bufferFence.get())); + if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) { + ATRACE_NAME("Waiting before draw"); + sync_wait(bufferFence.get(), -1); + } + } + if (buffer == nullptr) { + ALOGE("No output buffer provided. Aborting GPU composition."); + return BAD_VALUE; + } + + auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext; + auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache; + AHardwareBuffer_Desc bufferDesc; + AHardwareBuffer_describe(buffer->toAHardwareBuffer(), &bufferDesc); + LOG_ALWAYS_FATAL_IF(!hasUsage(bufferDesc, AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE), + "missing usage"); + + std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef = nullptr; + if (useFramebufferCache) { + auto iter = cache.find(buffer->getId()); + if (iter != cache.end()) { + ALOGV("Cache hit!"); + ATRACE_NAME("Cache hit"); + surfaceTextureRef = iter->second; + } + } + + if (surfaceTextureRef == nullptr || surfaceTextureRef->getTexture() == nullptr) { + ATRACE_NAME("Cache miss"); + surfaceTextureRef = std::make_shared<AutoBackendTexture::LocalRef>(); + surfaceTextureRef->setTexture( + new AutoBackendTexture(grContext.get(), buffer->toAHardwareBuffer(), true)); + if (useFramebufferCache) { + ALOGD("Adding to cache"); + cache.insert({buffer->getId(), surfaceTextureRef}); + } + } + + sk_sp<SkSurface> dstSurface = + surfaceTextureRef->getTexture()->getOrCreateSurface(mUseColorManagement + ? display.outputDataspace + : ui::Dataspace::UNKNOWN, + grContext.get()); + + SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get()); + if (dstCanvas == nullptr) { + ALOGE("Cannot acquire canvas from Skia."); + return BAD_VALUE; + } + + // Find if any layers have requested blur, we'll use that info to decide when to render to an + // offscreen buffer and when to render to the native buffer. + sk_sp<SkSurface> activeSurface(dstSurface); + SkCanvas* canvas = dstCanvas; + SkiaCapture::OffscreenState offscreenCaptureState; + const LayerSettings* blurCompositionLayer = nullptr; + if (mBlurFilter) { + bool requiresCompositionLayer = false; + for (const auto& layer : layers) { + if (layer->backgroundBlurRadius > 0) { + // when skbug.com/11208 and b/176903027 are resolved we can add the additional + // restriction for layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius + requiresCompositionLayer = true; + } + for (auto region : layer->blurRegions) { + if (region.blurRadius < BlurFilter::kMaxCrossFadeRadius) { + requiresCompositionLayer = true; + } + } + if (requiresCompositionLayer) { + activeSurface = dstSurface->makeSurface(dstSurface->imageInfo()); + canvas = mCapture->tryOffscreenCapture(activeSurface.get(), &offscreenCaptureState); + blurCompositionLayer = layer; + break; + } + } + } + + canvas->save(); + // Clear the entire canvas with a transparent black to prevent ghost images. + canvas->clear(SK_ColorTRANSPARENT); + initCanvas(canvas, display); + + // TODO: clearRegion was required for SurfaceView when a buffer is not yet available but the + // view is still on-screen. The clear region could be re-specified as a black color layer, + // however. + if (!display.clearRegion.isEmpty()) { + ATRACE_NAME("ClearRegion"); + size_t numRects = 0; + Rect const* rects = display.clearRegion.getArray(&numRects); + SkIRect skRects[numRects]; + for (int i = 0; i < numRects; ++i) { + skRects[i] = + SkIRect::MakeLTRB(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom); + } + SkRegion clearRegion; + SkPaint paint; + sk_sp<SkShader> shader = + SkShaders::Color(SkColor4f{.fR = 0., .fG = 0., .fB = 0., .fA = 1.0}, + toSkColorSpace(mUseColorManagement ? display.outputDataspace + : ui::Dataspace::UNKNOWN)); + paint.setShader(shader); + clearRegion.setRects(skRects, numRects); + canvas->drawRegion(clearRegion, paint); + } + + for (const auto& layer : layers) { + ATRACE_NAME("DrawLayer"); + + sk_sp<SkImage> blurInput; + if (blurCompositionLayer == layer) { + LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface); + LOG_ALWAYS_FATAL_IF(canvas == dstCanvas); + + // save a snapshot of the activeSurface to use as input to the blur shaders + blurInput = activeSurface->makeImageSnapshot(); + + // TODO we could skip this step if we know the blur will cover the entire image + // blit the offscreen framebuffer into the destination AHB + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); + if (CC_UNLIKELY(mCapture->isCaptureRunning())) { + uint64_t id = mCapture->endOffscreenCapture(&offscreenCaptureState); + dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()), + String8::format("SurfaceID|%" PRId64, id).c_str(), + nullptr); + dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint); + } else { + activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint); + } + + // assign dstCanvas to canvas and ensure that the canvas state is up to date + canvas = dstCanvas; + canvas->save(); + initCanvas(canvas, display); + + LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getSaveCount() != + dstSurface->getCanvas()->getSaveCount()); + LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getTotalMatrix() != + dstSurface->getCanvas()->getTotalMatrix()); + + // assign dstSurface to activeSurface + activeSurface = dstSurface; + } + + canvas->save(); + if (CC_UNLIKELY(mCapture->isCaptureRunning())) { + // Record the name of the layer if the capture is running. + std::stringstream layerSettings; + PrintTo(*layer, &layerSettings); + // Store the LayerSettings in additional information. + canvas->drawAnnotation(SkRect::MakeEmpty(), layer->name.c_str(), + SkData::MakeWithCString(layerSettings.str().c_str())); + } + // Layers have a local transform that should be applied to them + canvas->concat(getSkM44(layer->geometry.positionTransform).asM33()); + + const auto bounds = getSkRect(layer->geometry.boundaries); + if (mBlurFilter && layerHasBlur(layer)) { + std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs; + + // if multiple layers have blur, then we need to take a snapshot now because + // only the lowest layer will have blurImage populated earlier + if (!blurInput) { + blurInput = activeSurface->makeImageSnapshot(); + } + // rect to be blurred in the coordinate space of blurInput + const auto blurRect = canvas->getTotalMatrix().mapRect(bounds); + + if (layer->backgroundBlurRadius > 0) { + ATRACE_NAME("BackgroundBlur"); + auto blurredImage = + mBlurFilter->generate(grContext.get(), layer->backgroundBlurRadius, + blurInput, blurRect); + + cachedBlurs[layer->backgroundBlurRadius] = blurredImage; + + mBlurFilter->drawBlurRegion(canvas, getBlurRegion(layer), blurRect, blurredImage, + blurInput); + } + for (auto region : layer->blurRegions) { + if (cachedBlurs[region.blurRadius] != nullptr) { + ATRACE_NAME("BlurRegion"); + cachedBlurs[region.blurRadius] = + mBlurFilter->generate(grContext.get(), region.blurRadius, blurInput, + blurRect); + } + + mBlurFilter->drawBlurRegion(canvas, region, blurRect, + cachedBlurs[region.blurRadius], blurInput); + } + } + + const ui::Dataspace targetDataspace = mUseColorManagement + ? (needsLinearEffect(layer->colorTransform, layer->sourceDataspace, + display.outputDataspace) + // If we need to map to linear space, then mark the source image with the + // same colorspace as the destination surface so that Skia's color + // management is a no-op. + ? display.outputDataspace + : layer->sourceDataspace) + : ui::Dataspace::UNKNOWN; + + SkPaint paint; + if (layer->source.buffer.buffer) { + ATRACE_NAME("DrawImage"); + const auto& item = layer->source.buffer; + std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr; + auto iter = mTextureCache.find(item.buffer->getId()); + if (iter != mTextureCache.end()) { + imageTextureRef = iter->second; + } else { + imageTextureRef = std::make_shared<AutoBackendTexture::LocalRef>(); + imageTextureRef->setTexture(new AutoBackendTexture(grContext.get(), + item.buffer->toAHardwareBuffer(), + false)); + mTextureCache.insert({item.buffer->getId(), imageTextureRef}); + } + + sk_sp<SkImage> image = + imageTextureRef->getTexture()->makeImage(targetDataspace, + item.usePremultipliedAlpha + ? kPremul_SkAlphaType + : kUnpremul_SkAlphaType, + grContext.get()); + + auto texMatrix = getSkM44(item.textureTransform).asM33(); + // textureTansform was intended to be passed directly into a shader, so when + // building the total matrix with the textureTransform we need to first + // normalize it, then apply the textureTransform, then scale back up. + texMatrix.preScale(1.0f / bounds.width(), 1.0f / bounds.height()); + texMatrix.postScale(image->width(), image->height()); + + SkMatrix matrix; + if (!texMatrix.invert(&matrix)) { + matrix = texMatrix; + } + // The shader does not respect the translation, so we add it to the texture + // transform for the SkImage. This will make sure that the correct layer contents + // are drawn in the correct part of the screen. + matrix.postTranslate(layer->geometry.boundaries.left, layer->geometry.boundaries.top); + + sk_sp<SkShader> shader; + + if (layer->source.buffer.useTextureFiltering) { + shader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, + SkSamplingOptions( + {SkFilterMode::kLinear, SkMipmapMode::kNone}), + &matrix); + } else { + shader = image->makeShader(SkSamplingOptions(), matrix); + } + + // Handle opaque images - it's a little nonstandard how we do this. + // Fundamentally we need to support SurfaceControl.Builder#setOpaque: + // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean) + // The important language is that when isOpaque is set, opacity is not sampled from the + // alpha channel, but blending may still be supported on a transaction via setAlpha. So, + // here's the conundrum: + // 1. We can't force the SkImage alpha type to kOpaque_SkAlphaType, because it's treated + // as an internal hint - composition is undefined when there are alpha bits present. + // 2. We can try to lie about the pixel layout, but that only works for RGBA8888 + // buffers, i.e., treating them as RGBx8888 instead. But we can't do the same for + // RGBA1010102 because RGBx1010102 is not supported as a pixel layout for SkImages. It's + // also not clear what to use for F16 either, and lying about the pixel layout is a bit + // of a hack anyways. + // 3. We can't change the blendmode to src, because while this satisfies the requirement + // for ignoring the alpha channel, it doesn't quite satisfy the blending requirement + // because src always clobbers the destination content. + // + // So, what we do here instead is an additive blend mode where we compose the input + // image with a solid black. This might need to be reassess if this does not support + // FP16 incredibly well, but FP16 end-to-end isn't well supported anyway at the moment. + if (item.isOpaque) { + shader = SkShaders::Blend(SkBlendMode::kPlus, shader, + SkShaders::Color(SkColors::kBlack, + toSkColorSpace(targetDataspace))); + } + + paint.setShader( + createRuntimeEffectShader(shader, layer, display, + !item.isOpaque && item.usePremultipliedAlpha)); + paint.setAlphaf(layer->alpha); + } else { + ATRACE_NAME("DrawColor"); + const auto color = layer->source.solidColor; + sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r, + .fG = color.g, + .fB = color.b, + .fA = layer->alpha}, + toSkColorSpace(targetDataspace)); + paint.setShader(createRuntimeEffectShader(shader, layer, display, + /* undoPremultipliedAlpha */ false)); + } + + sk_sp<SkColorFilter> filter = + SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)); + + paint.setColorFilter(filter); + + if (layer->shadow.length > 0) { + const auto rect = layer->geometry.roundedCornersRadius > 0 + ? getSkRect(layer->geometry.roundedCornersCrop) + : bounds; + drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow); + } else { + // Shadows are assumed to live only on their own layer - it's not valid + // to draw the boundary retangles when there is already a caster shadow + // TODO(b/175915334): consider relaxing this restriction to enable more flexible + // composition - using a well-defined invalid color is long-term less error-prone. + // Push the clipRRect onto the clip stack. Draw the image. Pop the clip. + if (layer->geometry.roundedCornersRadius > 0) { + canvas->clipRRect(getRoundedRect(layer), true); + } + canvas->drawRect(bounds, paint); + } + canvas->restore(); + } + canvas->restore(); + mCapture->endCapture(); + { + ATRACE_NAME("flush surface"); + LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface); + activeSurface->flush(); + } + + if (drawFence != nullptr) { + *drawFence = flush(); + } + + // If flush failed or we don't support native fences, we need to force the + // gl command stream to be executed. + bool requireSync = drawFence == nullptr || drawFence->get() < 0; + if (requireSync) { + ATRACE_BEGIN("Submit(sync=true)"); + } else { + ATRACE_BEGIN("Submit(sync=false)"); + } + bool success = grContext->submit(requireSync); + ATRACE_END(); + if (!success) { + ALOGE("Failed to flush RenderEngine commands"); + // Chances are, something illegal happened (either the caller passed + // us bad parameters, or we messed up our shader generation). + return INVALID_OPERATION; + } + + // checkErrors(); + return NO_ERROR; +} + +inline SkRect SkiaGLRenderEngine::getSkRect(const FloatRect& rect) { + return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); +} + +inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) { + return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); +} + +inline SkRRect SkiaGLRenderEngine::getRoundedRect(const LayerSettings* layer) { + const auto rect = getSkRect(layer->geometry.roundedCornersCrop); + const auto cornerRadius = layer->geometry.roundedCornersRadius; + return SkRRect::MakeRectXY(rect, cornerRadius, cornerRadius); +} + +inline BlurRegion SkiaGLRenderEngine::getBlurRegion(const LayerSettings* layer) { + const auto rect = getSkRect(layer->geometry.boundaries); + const auto cornersRadius = layer->geometry.roundedCornersRadius; + return BlurRegion{.blurRadius = static_cast<uint32_t>(layer->backgroundBlurRadius), + .cornerRadiusTL = cornersRadius, + .cornerRadiusTR = cornersRadius, + .cornerRadiusBL = cornersRadius, + .cornerRadiusBR = cornersRadius, + .alpha = 1, + .left = static_cast<int>(rect.fLeft), + .top = static_cast<int>(rect.fTop), + .right = static_cast<int>(rect.fRight), + .bottom = static_cast<int>(rect.fBottom)}; +} + +inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer) { + return layer->backgroundBlurRadius > 0 || layer->blurRegions.size(); +} + +inline SkColor SkiaGLRenderEngine::getSkColor(const vec4& color) { + return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255); +} + +inline SkM44 SkiaGLRenderEngine::getSkM44(const mat4& matrix) { + return SkM44(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], + matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1], + matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2], + matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]); +} + +inline SkPoint3 SkiaGLRenderEngine::getSkPoint3(const vec3& vector) { + return SkPoint3::Make(vector.x, vector.y, vector.z); +} + +size_t SkiaGLRenderEngine::getMaxTextureSize() const { + return mGrContext->maxTextureSize(); +} + +size_t SkiaGLRenderEngine::getMaxViewportDims() const { + return mGrContext->maxRenderTargetSize(); +} + +void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRect& casterRect, float cornerRadius, + const ShadowSettings& settings) { + ATRACE_CALL(); + const float casterZ = settings.length / 2.0f; + const auto shadowShape = cornerRadius > 0 + ? SkPath::RRect(SkRRect::MakeRectXY(casterRect, cornerRadius, cornerRadius)) + : SkPath::Rect(casterRect); + const auto flags = + settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag; + + SkShadowUtils::DrawShadow(canvas, shadowShape, SkPoint3::Make(0, 0, casterZ), + getSkPoint3(settings.lightPos), settings.lightRadius, + getSkColor(settings.ambientColor), getSkColor(settings.spotColor), + flags); +} + +EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config, + EGLContext shareContext, + std::optional<ContextPriority> contextPriority, + Protection protection) { + EGLint renderableType = 0; + if (config == EGL_NO_CONFIG_KHR) { + renderableType = EGL_OPENGL_ES3_BIT; + } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) { + LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE"); + } + EGLint contextClientVersion = 0; + if (renderableType & EGL_OPENGL_ES3_BIT) { + contextClientVersion = 3; + } else if (renderableType & EGL_OPENGL_ES2_BIT) { + contextClientVersion = 2; + } else if (renderableType & EGL_OPENGL_ES_BIT) { + contextClientVersion = 1; + } else { + LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs"); + } + + std::vector<EGLint> contextAttributes; + contextAttributes.reserve(7); + contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION); + contextAttributes.push_back(contextClientVersion); + if (contextPriority) { + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG); + switch (*contextPriority) { + case ContextPriority::REALTIME: + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_REALTIME_NV); + break; + case ContextPriority::MEDIUM: + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_MEDIUM_IMG); + break; + case ContextPriority::LOW: + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LOW_IMG); + break; + case ContextPriority::HIGH: + default: + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG); + break; + } + } + if (protection == Protection::PROTECTED) { + contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT); + contextAttributes.push_back(EGL_TRUE); + } + contextAttributes.push_back(EGL_NONE); + + EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data()); + + if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) { + // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus + // EGL_NO_CONTEXT so that we can abort. + if (config != EGL_NO_CONFIG_KHR) { + return context; + } + // If |config| is EGL_NO_CONFIG_KHR, we speculatively try to create GLES 3 context, so we + // should try to fall back to GLES 2. + contextAttributes[1] = 2; + context = eglCreateContext(display, config, shareContext, contextAttributes.data()); + } + + return context; +} + +std::optional<RenderEngine::ContextPriority> SkiaGLRenderEngine::createContextPriority( + const RenderEngineCreationArgs& args) { + if (!gl::GLExtensions::getInstance().hasContextPriority()) { + return std::nullopt; + } + + switch (args.contextPriority) { + case RenderEngine::ContextPriority::REALTIME: + if (gl::GLExtensions::getInstance().hasRealtimePriority()) { + return RenderEngine::ContextPriority::REALTIME; + } else { + ALOGI("Realtime priority unsupported, degrading gracefully to high priority"); + return RenderEngine::ContextPriority::HIGH; + } + case RenderEngine::ContextPriority::HIGH: + case RenderEngine::ContextPriority::MEDIUM: + case RenderEngine::ContextPriority::LOW: + return args.contextPriority; + default: + return std::nullopt; + } +} + +EGLSurface SkiaGLRenderEngine::createPlaceholderEglPbufferSurface(EGLDisplay display, + EGLConfig config, int hwcFormat, + Protection protection) { + EGLConfig placeholderConfig = config; + if (placeholderConfig == EGL_NO_CONFIG_KHR) { + placeholderConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); + } + std::vector<EGLint> attributes; + attributes.reserve(7); + attributes.push_back(EGL_WIDTH); + attributes.push_back(1); + attributes.push_back(EGL_HEIGHT); + attributes.push_back(1); + if (protection == Protection::PROTECTED) { + attributes.push_back(EGL_PROTECTED_CONTENT_EXT); + attributes.push_back(EGL_TRUE); + } + attributes.push_back(EGL_NONE); + + return eglCreatePbufferSurface(display, placeholderConfig, attributes.data()); +} + +void SkiaGLRenderEngine::cleanFramebufferCache() {} + +int SkiaGLRenderEngine::getContextPriority() { + int value; + eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value); + return value; +} + +void SkiaGLRenderEngine::dump(std::string& result) { + const gl::GLExtensions& extensions = gl::GLExtensions::getInstance(); + + StringAppendF(&result, "\n ------------RE-----------------\n"); + StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion()); + StringAppendF(&result, "%s\n", extensions.getEGLExtensions()); + StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(), + extensions.getVersion()); + StringAppendF(&result, "%s\n", extensions.getExtensions()); + StringAppendF(&result, "RenderEngine supports protected context: %d\n", + supportsProtectedContent()); + StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext); + + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + StringAppendF(&result, "RenderEngine texture cache size: %zu\n", mTextureCache.size()); + StringAppendF(&result, "Dumping buffer ids...\n"); + // TODO(178539829): It would be nice to know which layer these are coming from and what + // the texture sizes are. + for (const auto& [id, unused] : mTextureCache) { + StringAppendF(&result, "- 0x%" PRIx64 "\n", id); + } + StringAppendF(&result, "\n"); + StringAppendF(&result, "RenderEngine protected texture cache size: %zu\n", + mProtectedTextureCache.size()); + StringAppendF(&result, "Dumping buffer ids...\n"); + for (const auto& [id, unused] : mProtectedTextureCache) { + StringAppendF(&result, "- 0x%" PRIx64 "\n", id); + } + StringAppendF(&result, "\n"); + StringAppendF(&result, "RenderEngine runtime effects: %zu\n", mRuntimeEffects.size()); + for (const auto& [linearEffect, unused] : mRuntimeEffects) { + StringAppendF(&result, "- inputDataspace: %s\n", + dataspaceDetails( + static_cast<android_dataspace>(linearEffect.inputDataspace)) + .c_str()); + StringAppendF(&result, "- outputDataspace: %s\n", + dataspaceDetails( + static_cast<android_dataspace>(linearEffect.outputDataspace)) + .c_str()); + StringAppendF(&result, "undoPremultipliedAlpha: %s\n", + linearEffect.undoPremultipliedAlpha ? "true" : "false"); + } + } + StringAppendF(&result, "\n"); +} + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h new file mode 100644 index 0000000000..8ef73599a4 --- /dev/null +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -0,0 +1,141 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SF_SKIAGLRENDERENGINE_H_ +#define SF_SKIAGLRENDERENGINE_H_ + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <GrDirectContext.h> +#include <SkSurface.h> +#include <android-base/thread_annotations.h> +#include <renderengine/RenderEngine.h> +#include <sys/types.h> + +#include <mutex> +#include <unordered_map> + +#include "AutoBackendTexture.h" +#include "EGL/egl.h" +#include "SkImageInfo.h" +#include "SkiaRenderEngine.h" +#include "android-base/macros.h" +#include "debug/SkiaCapture.h" +#include "filters/BlurFilter.h" +#include "filters/LinearEffect.h" + +namespace android { +namespace renderengine { +namespace skia { + +class SkiaGLRenderEngine : public skia::SkiaRenderEngine { +public: + static std::unique_ptr<SkiaGLRenderEngine> create(const RenderEngineCreationArgs& args); + SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt, + EGLSurface placeholder, EGLContext protectedContext, + EGLSurface protectedPlaceholder); + ~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex); + + void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override; + void unbindExternalTextureBuffer(uint64_t bufferId) override; + status_t drawLayers(const DisplaySettings& display, + const std::vector<const LayerSettings*>& layers, + const sp<GraphicBuffer>& buffer, const bool useFramebufferCache, + base::unique_fd&& bufferFence, base::unique_fd* drawFence) override; + void cleanFramebufferCache() override; + int getContextPriority() override; + bool isProtected() const override { return mInProtectedContext; } + bool supportsProtectedContent() const override; + bool useProtectedContext(bool useProtectedContext) override; + +protected: + void dump(std::string& result) override; + size_t getMaxTextureSize() const override; + size_t getMaxViewportDims() const override; + +private: + static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); + static EGLContext createEglContext(EGLDisplay display, EGLConfig config, + EGLContext shareContext, + std::optional<ContextPriority> contextPriority, + Protection protection); + static std::optional<RenderEngine::ContextPriority> createContextPriority( + const RenderEngineCreationArgs& args); + static EGLSurface createPlaceholderEglPbufferSurface(EGLDisplay display, EGLConfig config, + int hwcFormat, Protection protection); + inline SkRect getSkRect(const FloatRect& layer); + inline SkRect getSkRect(const Rect& layer); + inline SkRRect getRoundedRect(const LayerSettings* layer); + inline BlurRegion getBlurRegion(const LayerSettings* layer); + inline bool layerHasBlur(const LayerSettings* layer); + inline SkColor getSkColor(const vec4& color); + inline SkM44 getSkM44(const mat4& matrix); + inline SkPoint3 getSkPoint3(const vec3& vector); + + base::unique_fd flush(); + bool waitFence(base::unique_fd fenceFd); + void initCanvas(SkCanvas* canvas, const DisplaySettings& display); + void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius, + const ShadowSettings& shadowSettings); + // If mUseColorManagement is correct and layer needsLinearEffect, it returns a linear runtime + // shader. Otherwise it returns the input shader. + sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings* layer, + const DisplaySettings& display, + bool undoPremultipliedAlpha); + + EGLDisplay mEGLDisplay; + EGLContext mEGLContext; + EGLSurface mPlaceholderSurface; + EGLContext mProtectedEGLContext; + EGLSurface mProtectedPlaceholderSurface; + BlurFilter* mBlurFilter = nullptr; + + const bool mUseColorManagement; + + // Cache of GL textures that we'll store per GraphicBuffer ID + std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache + GUARDED_BY(mRenderingMutex); + std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>> + mProtectedTextureCache GUARDED_BY(mRenderingMutex); + std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects; + // Mutex guarding rendering operations, so that: + // 1. GL operations aren't interleaved, and + // 2. Internal state related to rendering that is potentially modified by + // multiple threads is guaranteed thread-safe. + std::mutex mRenderingMutex; + + sp<Fence> mLastDrawFence; + + // Graphics context used for creating surfaces and submitting commands + sk_sp<GrDirectContext> mGrContext; + // Same as above, but for protected content (eg. DRM) + sk_sp<GrDirectContext> mProtectedGrContext; + + bool mInProtectedContext = false; + // Object to capture commands send to Skia. + std::unique_ptr<SkiaCapture> mCapture; + + // Keep this information as a local variable to determine whether the access of the GL + // operations is working on the same threads. + const RenderEngineType mRenderEngineType = RenderEngineType::SKIA_GL; +}; + +} // namespace skia +} // namespace renderengine +} // namespace android + +#endif /* SF_GLESRENDERENGINE_H_ */ diff --git a/libs/ui/UiConfig.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 0ac863d718..81f0b6f970 100644 --- a/libs/ui/UiConfig.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,13 @@ * limitations under the License. */ -#include <ui/UiConfig.h> +//#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "RenderEngine" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS namespace android { - -void appendUiConfigString(std::string& configStr) { - static const char* config = - " [libui]"; - configStr.append(config); -} - - -}; // namespace android +namespace renderengine { +namespace skia {} // namespace skia +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h new file mode 100644 index 0000000000..12b8586ad7 --- /dev/null +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -0,0 +1,66 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SF_SKIARENDERENGINE_H_ +#define SF_SKIARENDERENGINE_H_ + +#include <renderengine/RenderEngine.h> +#include <sys/types.h> + +namespace android { + +namespace renderengine { + +class Mesh; +class Texture; + +namespace skia { + +class BlurFilter; + +// TODO: Put common skia stuff here that can be shared between the GL & Vulkan backends +// Currently mostly just handles all the no-op / missing APIs +class SkiaRenderEngine : public RenderEngine { +public: + static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args); + ~SkiaRenderEngine() override {} + + virtual void primeCache() const override{}; + virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{}; + virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{}; + virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/){}; + virtual void unbindExternalTextureBuffer(uint64_t /*bufferId*/){}; + + virtual bool isProtected() const override { return false; } // mInProtectedContext; } + virtual bool supportsProtectedContent() const override { return false; }; + virtual bool useProtectedContext(bool /*useProtectedContext*/) override { return false; }; + virtual status_t drawLayers(const DisplaySettings& /*display*/, + const std::vector<const LayerSettings*>& /*layers*/, + const sp<GraphicBuffer>& /*buffer*/, + const bool /*useFramebufferCache*/, + base::unique_fd&& /*bufferFence*/, + base::unique_fd* /*drawFence*/) override { + return 0; + }; + virtual bool cleanupPostRender(CleanupMode) override { return true; }; + virtual int getContextPriority() override { return 0; } +}; + +} // namespace skia +} // namespace renderengine +} // namespace android + +#endif /* SF_GLESRENDERENGINE_H_ */
\ No newline at end of file diff --git a/libs/renderengine/skia/debug/CaptureTimer.cpp b/libs/renderengine/skia/debug/CaptureTimer.cpp new file mode 100644 index 0000000000..11bcdb8996 --- /dev/null +++ b/libs/renderengine/skia/debug/CaptureTimer.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CaptureTimer.h" + +#undef LOG_TAG +#define LOG_TAG "RenderEngine" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "CommonPool.h" + +#include <thread> + +namespace android { +namespace renderengine { +namespace skia { + +void CaptureTimer::setTimeout(TimeoutCallback function, std::chrono::milliseconds delay) { + this->clear = false; + CommonPool::post([=]() { + if (this->clear) return; + std::this_thread::sleep_for(delay); + if (this->clear) return; + function(); + }); +} + +void CaptureTimer::stop() { + this->clear = true; +} + +} // namespace skia +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/skia/debug/CaptureTimer.h b/libs/renderengine/skia/debug/CaptureTimer.h new file mode 100644 index 0000000000..a0aa302734 --- /dev/null +++ b/libs/renderengine/skia/debug/CaptureTimer.h @@ -0,0 +1,43 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <chrono> +#include <functional> + +namespace android { +namespace renderengine { +namespace skia { + +/** + * Simple timer that times out after a given delay and executes a void + * callback function. + */ +class CaptureTimer { + bool clear = false; + +public: + using TimeoutCallback = std::function<void()>; + // Start the timeout. + void setTimeout(TimeoutCallback function, std::chrono::milliseconds delay); + // Stop and clean up. + void stop(); +}; + +} // namespace skia +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/skia/debug/CommonPool.cpp b/libs/renderengine/skia/debug/CommonPool.cpp new file mode 100644 index 0000000000..bf15300227 --- /dev/null +++ b/libs/renderengine/skia/debug/CommonPool.cpp @@ -0,0 +1,98 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CommonPool.h" + +#undef LOG_TAG +#define LOG_TAG "RenderEngine" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <sys/resource.h> +#include <utils/Trace.h> + +#include <system/thread_defs.h> +#include <array> + +namespace android { +namespace renderengine { +namespace skia { + +CommonPool::CommonPool() { + ATRACE_CALL(); + + CommonPool* pool = this; + // Create 2 workers + for (int i = 0; i < THREAD_COUNT; i++) { + std::thread worker([pool, i] { + { + std::array<char, 20> name{"reTask"}; + snprintf(name.data(), name.size(), "reTask%d", i); + auto self = pthread_self(); + pthread_setname_np(self, name.data()); + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_FOREGROUND); + } + pool->workerLoop(); + }); + worker.detach(); + } +} + +CommonPool& CommonPool::instance() { + static CommonPool pool; + return pool; +} + +void CommonPool::post(Task&& task) { + instance().enqueue(std::move(task)); +} + +void CommonPool::enqueue(Task&& task) { + std::unique_lock lock(mLock); + while (mWorkQueue.size() > QUEUE_SIZE) { + lock.unlock(); + ALOGW("Queue is full: %d, waiting before adding more tasks.", QUEUE_SIZE); + usleep(100); + lock.lock(); + } + mWorkQueue.push(std::move(task)); + if (mWaitingThreads == THREAD_COUNT || (mWaitingThreads > 0 && mWorkQueue.size() > 1)) { + mCondition.notify_one(); + } +} + +void CommonPool::workerLoop() { + std::unique_lock lock(mLock); + while (true) { + if (mWorkQueue.size() == 0) { + mWaitingThreads++; + mCondition.wait(lock); + mWaitingThreads--; + } + // Need to double-check that work is still available now that we have the lock + // It may have already been grabbed by a different thread + while (mWorkQueue.size() > 0) { + auto work = mWorkQueue.front(); + mWorkQueue.pop(); + lock.unlock(); + work(); + lock.lock(); + } + } +} + +} // namespace skia +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/skia/debug/CommonPool.h b/libs/renderengine/skia/debug/CommonPool.h new file mode 100644 index 0000000000..7fc3d231c6 --- /dev/null +++ b/libs/renderengine/skia/debug/CommonPool.h @@ -0,0 +1,70 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <log/log.h> + +#include <condition_variable> +#include <functional> +#include <future> +#include <mutex> +#include <queue> + +namespace android { +namespace renderengine { +namespace skia { + +namespace { +#define PREVENT_COPY_AND_ASSIGN(Type) \ +private: \ + Type(const Type&) = delete; \ + void operator=(const Type&) = delete +} // namespace + +/** + * Shamelessly copied from HWUI to execute Skia Capturing on the back thread in + * a safe manner. + */ +class CommonPool { + PREVENT_COPY_AND_ASSIGN(CommonPool); + +public: + using Task = std::function<void()>; + static constexpr auto THREAD_COUNT = 2; + static constexpr auto QUEUE_SIZE = 128; + + static void post(Task&& func); + +private: + static CommonPool& instance(); + + CommonPool(); + ~CommonPool() {} + + void enqueue(Task&&); + + void workerLoop(); + + std::mutex mLock; + std::condition_variable mCondition; + int mWaitingThreads = 0; + std::queue<Task> mWorkQueue; +}; + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/debug/README.md b/libs/renderengine/skia/debug/README.md new file mode 100644 index 0000000000..4719e34a26 --- /dev/null +++ b/libs/renderengine/skia/debug/README.md @@ -0,0 +1,17 @@ +This library turns on recording of skia commands in SkiaGL version of the RE. +The debug property defines number of milliseconds for the recording to take place. +A non zero value turns on the recording. The recording will stop after MS specified. +To reset the recording, set the capture_skia_ms flag to a new time. When recording +is finished, the capture_skia_ms flag will be set to 0 to avoid circular recording. + +In order to allow the process to write files onto the device run: +adb shell setenforce 0 + +To start recording run: +adb shell setprop debug.renderengine.capture_skia_ms 1000 + +File will be stored in the /data/user/ directory on the device: +adb shell ls -al /data/user/ + +To retrieve the data from the device: +adb pull /data/user/re_skiacapture_<timestamp>.mskp diff --git a/libs/renderengine/skia/debug/SkiaCapture.cpp b/libs/renderengine/skia/debug/SkiaCapture.cpp new file mode 100644 index 0000000000..40f5cf299d --- /dev/null +++ b/libs/renderengine/skia/debug/SkiaCapture.cpp @@ -0,0 +1,204 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkiaCapture.h" + +#undef LOG_TAG +#define LOG_TAG "RenderEngine" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <android-base/properties.h> +#include <android-base/stringprintf.h> +#include <log/log.h> +#include <renderengine/RenderEngine.h> +#include <utils/Trace.h> + +#include "CommonPool.h" +#include "src/utils/SkMultiPictureDocument.h" + +namespace android { +namespace renderengine { +namespace skia { + +// The root of the filename to write a recorded SKP to. In order for this file to +// be written to /data/user/, user must run 'adb shell setenforce 0' in the device. +static const std::string CAPTURED_FILENAME_BASE = "/data/user/re_skiacapture"; + +SkiaCapture::~SkiaCapture() { + mTimer.stop(); +} + +SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) NO_THREAD_SAFETY_ANALYSIS { + ATRACE_CALL(); + + // If we are not running yet, set up. + if (CC_LIKELY(!mCaptureRunning)) { + mTimerInterval = std::chrono::milliseconds( + base::GetIntProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_SKIA_MS, 0)); + // Set up the multi-frame capture. If we fail to set it up, then just return canvas. + // If interval is 0, return surface. + if (CC_LIKELY(mTimerInterval == 0ms || !setupMultiFrameCapture())) { + return surface->getCanvas(); + } + // Start the new timer. When timer expires, write to file. + mTimer.setTimeout( + [this] { + const std::scoped_lock lock(mMutex); + LOG_ALWAYS_FATAL_IF(mCurrentPageCanvas != nullptr); + writeToFile(); + // To avoid going in circles, set the flag to 0. This way the capture can be + // restarted just by setting the flag and without restarting the process. + base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_SKIA_MS, "0"); + }, + mTimerInterval); + } + + mMutex.lock(); + + // Create a canvas pointer, fill it. + mCurrentPageCanvas = mMultiPic->beginPage(surface->width(), surface->height()); + + // Setting up an nway canvas is common to any kind of capture. + mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height()); + mNwayCanvas->addCanvas(surface->getCanvas()); + mNwayCanvas->addCanvas(mCurrentPageCanvas); + + return mNwayCanvas.get(); +} + +void SkiaCapture::endCapture() NO_THREAD_SAFETY_ANALYSIS { + ATRACE_CALL(); + // Don't end anything if we are not running. + if (CC_LIKELY(!mCaptureRunning)) { + return; + } + // Reset the canvas pointer. + mCurrentPageCanvas = nullptr; + mNwayCanvas.reset(); + // End page. + if (mMultiPic) { + mMultiPic->endPage(); + } + mMutex.unlock(); +} + +SkCanvas* SkiaCapture::tryOffscreenCapture(SkSurface* surface, OffscreenState* state) { + ATRACE_CALL(); + // Don't start anything if we are not running. + if (CC_LIKELY(!mCaptureRunning)) { + return surface->getCanvas(); + } + + // Create a canvas pointer, fill it. + state->offscreenRecorder = std::make_unique<SkPictureRecorder>(); + SkCanvas* pictureCanvas = + state->offscreenRecorder->beginRecording(surface->width(), surface->height()); + + // Setting up an nway canvas is common to any kind of capture. + state->offscreenCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height()); + state->offscreenCanvas->addCanvas(surface->getCanvas()); + state->offscreenCanvas->addCanvas(pictureCanvas); + + return state->offscreenCanvas.get(); +} + +uint64_t SkiaCapture::endOffscreenCapture(OffscreenState* state) { + ATRACE_CALL(); + // Don't end anything if we are not running. + if (CC_LIKELY(!mCaptureRunning)) { + return 0; + } + + // compute the uniqueID for this capture + static std::atomic<uint64_t> nextID{1}; + const uint64_t uniqueID = nextID.fetch_add(1, std::memory_order_relaxed); + + // Reset the canvas pointer as we are no longer drawing into it + state->offscreenCanvas.reset(); + + // Record the offscreen as a picture in the currently active page. + SkRect bounds = + SkRect::Make(state->offscreenRecorder->getRecordingCanvas()->imageInfo().dimensions()); + mCurrentPageCanvas + ->drawAnnotation(bounds, + String8::format("OffscreenLayerDraw|%" PRId64, uniqueID).c_str(), + nullptr); + mCurrentPageCanvas->drawPicture(state->offscreenRecorder->finishRecordingAsPicture()); + + // Reset the offscreen picture recorder + state->offscreenRecorder.reset(); + + return uniqueID; +} + +void SkiaCapture::writeToFile() { + ATRACE_CALL(); + // Pass mMultiPic and mOpenMultiPicStream to a background thread, which will + // handle the heavyweight serialization work and destroy them. + // mOpenMultiPicStream is released to a bare pointer because keeping it in + // a smart pointer makes the lambda non-copyable. The lambda is only called + // once, so this is safe. + SkFILEWStream* stream = mOpenMultiPicStream.release(); + CommonPool::post([doc = std::move(mMultiPic), stream] { + ALOGD("Finalizing multi frame SKP"); + doc->close(); + delete stream; + ALOGD("Multi frame SKP complete."); + }); + mCaptureRunning = false; +} + +bool SkiaCapture::setupMultiFrameCapture() { + ATRACE_CALL(); + ALOGD("Set up multi-frame capture, ms = %llu", mTimerInterval.count()); + + std::string captureFile; + // Attach a timestamp to the file. + base::StringAppendF(&captureFile, "%s_%lld.mskp", CAPTURED_FILENAME_BASE.c_str(), + std::chrono::steady_clock::now().time_since_epoch().count()); + auto stream = std::make_unique<SkFILEWStream>(captureFile.c_str()); + // We own this stream and need to hold it until close() finishes. + if (stream->isValid()) { + mOpenMultiPicStream = std::move(stream); + mSerialContext.reset(new SkSharingSerialContext()); + SkSerialProcs procs; + procs.fImageProc = SkSharingSerialContext::serializeImage; + procs.fImageCtx = mSerialContext.get(); + procs.fTypefaceProc = [](SkTypeface* tf, void* ctx) { + return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData); + }; + // SkDocuments don't take ownership of the streams they write. + // we need to keep it until after mMultiPic.close() + // procs is passed as a pointer, but just as a method of having an optional default. + // procs doesn't need to outlive this Make call + // The last argument is a callback for the endPage behavior. + // See SkSharingProc.h for more explanation of this callback. + mMultiPic = SkMakeMultiPictureDocument( + mOpenMultiPicStream.get(), &procs, + [sharingCtx = mSerialContext.get()](const SkPicture* pic) { + SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx); + }); + mCaptureRunning = true; + return true; + } else { + ALOGE("Could not open \"%s\" for writing.", captureFile.c_str()); + return false; + } +} + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/debug/SkiaCapture.h b/libs/renderengine/skia/debug/SkiaCapture.h new file mode 100644 index 0000000000..5e18e60f93 --- /dev/null +++ b/libs/renderengine/skia/debug/SkiaCapture.h @@ -0,0 +1,92 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <SkDocument.h> +#include <SkNWayCanvas.h> +#include <SkPictureRecorder.h> +#include <SkSurface.h> + +#include <chrono> +#include <mutex> + +#include "CaptureTimer.h" +#include "tools/SkSharingProc.h" + +namespace android { +namespace renderengine { +namespace skia { + +using namespace std::chrono_literals; + +/** + * Class that captures frames that are sent to Skia in Render Engine. It sets up + * a multi frame capture and writes it into a file on the device. The capture is + * done based on a timer. + */ +class SkiaCapture { + using Interval = std::chrono::milliseconds; + +public: + SkiaCapture() {} + virtual ~SkiaCapture(); + // Called every frame. Normally returns early with screen canvas. + // But when capture is enabled, returns an nwaycanvas where commands are also recorded. + SkCanvas* tryCapture(SkSurface* surface); + // Called at the end of every frame. + void endCapture(); + // Returns whether the capture is running. + bool isCaptureRunning() { return mCaptureRunning; } + + // Offscreen state member variables are private to SkiaCapture, but the allocation + // and lifetime is managed by the caller. This enables nested offscreen + // captures to occur. + struct OffscreenState { + std::unique_ptr<SkPictureRecorder> offscreenRecorder; + std::unique_ptr<SkNWayCanvas> offscreenCanvas; + }; + SkCanvas* tryOffscreenCapture(SkSurface* surface, OffscreenState* state); + uint64_t endOffscreenCapture(OffscreenState* state); + +private: + // Performs the first-frame work of a multi frame SKP capture. Returns true if successful. + bool setupMultiFrameCapture(); + + // Closes the recording and serializes sequence to a file. + void writeToFile(); + + // Multi frame serialization stream and writer used when serializing more than one frame. + std::unique_ptr<SkFILEWStream> mOpenMultiPicStream; + sk_sp<SkDocument> mMultiPic; + std::unique_ptr<SkSharingSerialContext> mSerialContext; + std::unique_ptr<SkNWayCanvas> mNwayCanvas; + + SkCanvas* mCurrentPageCanvas = nullptr; + + // Capturing and interval control. + bool mCaptureRunning = false; + CaptureTimer mTimer; + Interval mTimerInterval = 0ms; + + // Mutex to ensure that a frame in progress when the timer fires is allowed to run to + // completion before we write the file to disk. + std::mutex mMutex; +}; + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/debug/record.sh b/libs/renderengine/skia/debug/record.sh new file mode 100755 index 0000000000..bc406d97a8 --- /dev/null +++ b/libs/renderengine/skia/debug/record.sh @@ -0,0 +1,106 @@ +# This script captures MSKP files from RenderEngine in a connected device. +# this only functions when RenderEngine uses the Skia backend. +# it triggers code in SkiaCapture.cpp. + +# for a newly flashed device, perform first time steps with +# record.sh rootandsetup + +# record all frames that RenderEngine handles over the span of 2 seconds. +# record.sh 2000 + +if [ -z "$1" ]; then + printf 'Usage:\n record.sh rootandsetup\n' + printf ' record.sh MILLISECONDS\n\n' + exit 1 +elif [ "$1" == "rootandsetup" ]; then + # first time use requires these changes + adb root + adb shell setenforce 0 + adb shell setprop debug.renderengine.backend "skiagl" + adb shell stop + adb shell start + exit 1; +fi + +# name of the newest file in /data/user/ before starting +oldname=$(adb shell ls -cr /data/user/ | head -n 1) + +# record frames for some number of milliseconds. +adb shell setprop debug.renderengine.capture_skia_ms $1 + +# give the device time to both record, and starting writing the file. +# Total time needed to write the file depends on how much data was recorded. +# the loop at the end waits for this. +sleep $(($1 / 1000 + 2)); + +# There is no guarantee that at least one frame passed through renderengine during that time +# but as far as I know it always at least writes a 0-byte file with a new name, unless it crashes +# the process it is recording. +# /data/user/re_skiacapture_56204430551705.mskp + +# list the files here from newest to oldest, keep only the name of the newest. +name=$(adb shell ls -cr /data/user/ | head -n 1) +remote_path=/data/user/$name + +if [[ $oldname = $name ]]; then + echo "No new file written, probably no RenderEngine activity during recording period." + exit 1 +fi + +# return the size of a file in bytes +adb_filesize() { + adb shell "wc -c \"$1\"" 2> /dev/null | awk '{print $1}' +} + +mskp_size=$(adb_filesize "/data/user/$name") +if [[ $mskp_size = "0" ]]; then + echo "Empty file, probably no RenderEngine activity during recording period." + exit 1 +fi + +spin() { + case "$spin" in + 1) printf '\b|';; + 2) printf '\b\\';; + 3) printf '\b-';; + *) printf '\b/';; + esac + spin=$(( ( ${spin:-0} + 1 ) % 4 )) + sleep $1 +} + +printf "MSKP captured, Waiting for file serialization to finish.\n" + +local_path=~/Downloads/$name + +# wait for the file size to stop changing + +timeout=$(( $(date +%s) + 300)) +last_size='0' # output of last size check command +unstable=true # false once the file size stops changing +counter=0 # used to perform size check only 1/sec though we update spinner 20/sec +# loop until the file size is unchanged for 1 second. +while [ $unstable != 0 ] ; do + spin 0.05 + counter=$(( $counter+1 )) + if ! (( $counter % 20)) ; then + new_size=$(adb_filesize "$remote_path") + unstable=$(($new_size != $last_size)) + last_size=$new_size + fi + if [ $(date +%s) -gt $timeout ] ; then + printf '\bTimed out.\n' + exit 3 + fi +done +printf '\b' + +printf "MSKP file serialized: %s\n" $(echo $last_size | numfmt --to=iec) + +adb pull "$remote_path" "$local_path" +if ! [ -f "$local_path" ] ; then + printf "something went wrong with `adb pull`." + exit 4 +fi +adb shell rm "$remote_path" +printf 'SKP saved to %s\n\n' "$local_path"
\ No newline at end of file diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp new file mode 100644 index 0000000000..9d20e327a8 --- /dev/null +++ b/libs/renderengine/skia/filters/BlurFilter.cpp @@ -0,0 +1,190 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "BlurFilter.h" +#include <SkCanvas.h> +#include <SkData.h> +#include <SkPaint.h> +#include <SkRuntimeEffect.h> +#include <SkSize.h> +#include <SkString.h> +#include <SkSurface.h> +#include <log/log.h> +#include <utils/Trace.h> + +namespace android { +namespace renderengine { +namespace skia { + +BlurFilter::BlurFilter() { + SkString blurString(R"( + in shader input; + uniform float2 in_blurOffset; + + half4 main(float2 xy) { + half4 c = sample(input, xy); + c += sample(input, xy + float2( in_blurOffset.x, in_blurOffset.y)); + c += sample(input, xy + float2( in_blurOffset.x, -in_blurOffset.y)); + c += sample(input, xy + float2(-in_blurOffset.x, in_blurOffset.y)); + c += sample(input, xy + float2(-in_blurOffset.x, -in_blurOffset.y)); + + return half4(c.rgb * 0.2, 1.0); + } + )"); + + auto [blurEffect, error] = SkRuntimeEffect::Make(blurString); + if (!blurEffect) { + LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str()); + } + mBlurEffect = std::move(blurEffect); + + SkString mixString(R"( + in shader blurredInput; + in shader originalInput; + uniform float mixFactor; + + half4 main(float2 xy) { + return half4(mix(sample(originalInput), sample(blurredInput), mixFactor)); + } + )"); + + auto [mixEffect, mixError] = SkRuntimeEffect::Make(mixString); + if (!mixEffect) { + LOG_ALWAYS_FATAL("RuntimeShader error: %s", mixError.c_str()); + } + mMixEffect = std::move(mixEffect); +} + +sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius, + const sk_sp<SkImage> input, const SkRect& blurRect) const { + // Kawase is an approximation of Gaussian, but it behaves differently from it. + // A radius transformation is required for approximating them, and also to introduce + // non-integer steps, necessary to smoothly interpolate large radii. + float tmpRadius = (float)blurRadius / 6.0f; + float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius)); + float radiusByPasses = tmpRadius / (float)numberOfPasses; + + // create blur surface with the bit depth and colorspace of the original surface + SkImageInfo scaledInfo = input->imageInfo().makeWH(blurRect.width() * kInputScale, + blurRect.height() * kInputScale); + + const float stepX = radiusByPasses; + const float stepY = radiusByPasses; + + // For sampling Skia's API expects the inverse of what logically seems appropriate. In this + // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale) + // but instead we must do the inverse. + SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop); + blurMatrix.postScale(kInputScale, kInputScale); + + // start by downscaling and doing the first blur pass + SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone); + SkRuntimeShaderBuilder blurBuilder(mBlurEffect); + blurBuilder.child("input") = + input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix); + blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInputScale, stepY * kInputScale}; + + sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false)); + + // And now we'll build our chain of scaled blur stages + for (auto i = 1; i < numberOfPasses; i++) { + const float stepScale = (float)i * kInputScale; + blurBuilder.child("input") = + tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear); + blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale}; + tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false); + } + + return tmpBlur; +} + +static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRect, float scale) { + // 1. Apply the blur shader matrix, which scales up the blured surface to its real size + auto matrix = SkMatrix::Scale(scale, scale); + // 2. Since the blurred surface has the size of the layer, we align it with the + // top left corner of the layer position. + matrix.postConcat(SkMatrix::Translate(blurRect.fLeft, blurRect.fTop)); + // 3. Finally, apply the inverse canvas matrix. The snapshot made in the BlurFilter is in the + // original surface orientation. The inverse matrix has to be applied to align the blur + // surface with the current orientation/position of the canvas. + SkMatrix drawInverse; + if (canvas != nullptr && canvas->getTotalMatrix().invert(&drawInverse)) { + matrix.postConcat(drawInverse); + } + return matrix; +} + +void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion, + const SkRect& blurRect, sk_sp<SkImage> blurredImage, + sk_sp<SkImage> input) { + ATRACE_CALL(); + + SkPaint paint; + paint.setAlphaf(effectRegion.alpha); + if (effectRegion.alpha == 1.0f) { + paint.setBlendMode(SkBlendMode::kSrc); + } + + const auto blurMatrix = getShaderTransform(canvas, blurRect, kInverseInputScale); + SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone); + const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, + linearSampling, &blurMatrix); + + if (effectRegion.blurRadius < kMaxCrossFadeRadius) { + // For sampling Skia's API expects the inverse of what logically seems appropriate. In this + // case you might expect the matrix to simply be the canvas matrix. + SkMatrix inputMatrix; + if (!canvas->getTotalMatrix().invert(&inputMatrix)) { + ALOGE("matrix was unable to be inverted"); + } + + SkRuntimeShaderBuilder blurBuilder(mMixEffect); + blurBuilder.child("blurredInput") = blurShader; + blurBuilder.child("originalInput") = + input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling, + inputMatrix); + blurBuilder.uniform("mixFactor") = effectRegion.blurRadius / kMaxCrossFadeRadius; + + paint.setShader(blurBuilder.makeShader(nullptr, true)); + } else { + paint.setShader(blurShader); + } + + // TODO we should AA at least the drawRoundRect which would mean no SRC blending + // TODO this round rect calculation doesn't match the one used to draw in RenderEngine + auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right, + effectRegion.bottom); + + if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 || + effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) { + const SkVector radii[4] = + {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL), + SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR), + SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL), + SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)}; + SkRRect roundedRect; + roundedRect.setRectRadii(rect, radii); + canvas->drawRRect(roundedRect, paint); + } else { + canvas->drawRect(rect, paint); + } +} + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h new file mode 100644 index 0000000000..731ba11483 --- /dev/null +++ b/libs/renderengine/skia/filters/BlurFilter.h @@ -0,0 +1,65 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <SkCanvas.h> +#include <SkImage.h> +#include <SkRuntimeEffect.h> +#include <SkSurface.h> +#include <ui/BlurRegion.h> + +using namespace std; + +namespace android { +namespace renderengine { +namespace skia { + +/** + * This is an implementation of a Kawase blur, as described in here: + * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/ + * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf + */ +class BlurFilter { +public: + // Downsample FBO to improve performance + static constexpr float kInputScale = 0.25f; + // Downsample scale factor used to improve performance + static constexpr float kInverseInputScale = 1.0f / kInputScale; + // Maximum number of render passes + static constexpr uint32_t kMaxPasses = 4; + // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited + // image, up to this radius. + static constexpr float kMaxCrossFadeRadius = 30.0f; + + explicit BlurFilter(); + virtual ~BlurFilter(){}; + + // Execute blur, saving it to a texture + sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius, + const sk_sp<SkImage> blurInput, const SkRect& blurRect) const; + + void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& blurRect, + sk_sp<SkImage> blurredImage, sk_sp<SkImage> input); + +private: + sk_sp<SkRuntimeEffect> mBlurEffect; + sk_sp<SkRuntimeEffect> mMixEffect; +}; + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp new file mode 100644 index 0000000000..84af016458 --- /dev/null +++ b/libs/renderengine/skia/filters/LinearEffect.cpp @@ -0,0 +1,477 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "LinearEffect.h" + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <SkString.h> +#include <utils/Trace.h> + +#include <optional> + +#include "log/log.h" +#include "math/mat4.h" +#include "system/graphics-base-v1.0.h" +#include "ui/ColorSpace.h" + +namespace android { +namespace renderengine { +namespace skia { + +static void generateEOTF(ui::Dataspace dataspace, SkString& shader) { + switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_ST2084: + shader.append(R"( + + float3 EOTF(float3 color) { + float m1 = (2610.0 / 4096.0) / 4.0; + float m2 = (2523.0 / 4096.0) * 128.0; + float c1 = (3424.0 / 4096.0); + float c2 = (2413.0 / 4096.0) * 32.0; + float c3 = (2392.0 / 4096.0) * 32.0; + + float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2)); + tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp); + return pow(tmp, 1.0 / float3(m1)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_HLG: + shader.append(R"( + float EOTF_channel(float channel) { + const float a = 0.17883277; + const float b = 0.28466892; + const float c = 0.55991073; + return channel <= 0.5 ? channel * channel / 3.0 : + (exp((channel - c) / a) + b) / 12.0; + } + + float3 EOTF(float3 color) { + return float3(EOTF_channel(color.r), EOTF_channel(color.g), + EOTF_channel(color.b)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_LINEAR: + shader.append(R"( + float3 EOTF(float3 color) { + return color; + } + )"); + break; + case HAL_DATASPACE_TRANSFER_SRGB: + default: + shader.append(R"( + + float EOTF_sRGB(float srgb) { + return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4); + } + + float3 EOTF_sRGB(float3 srgb) { + return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b)); + } + + float3 EOTF(float3 srgb) { + return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb)); + } + )"); + break; + } +} + +static void generateXYZTransforms(SkString& shader) { + shader.append(R"( + uniform float4x4 in_rgbToXyz; + uniform float4x4 in_xyzToRgb; + float3 ToXYZ(float3 rgb) { + return clamp((in_rgbToXyz * float4(rgb, 1.0)).rgb, 0.0, 1.0); + } + + float3 ToRGB(float3 xyz) { + return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0); + } + )"); +} + +// Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits]) +static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, SkString& shader) { + switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_ST2084: + shader.append(R"( + float3 ScaleLuminance(float3 xyz) { + return xyz * 10000.0; + } + )"); + break; + case HAL_DATASPACE_TRANSFER_HLG: + shader.append(R"( + float3 ScaleLuminance(float3 xyz) { + return xyz * 1000.0 * pow(xyz.y, 0.2); + } + )"); + break; + default: + shader.append(R"( + float3 ScaleLuminance(float3 xyz) { + return xyz * in_displayMaxLuminance; + } + )"); + break; + } +} + +static void generateToneMapInterpolation(ui::Dataspace inputDataspace, + ui::Dataspace outputDataspace, SkString& shader) { + switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_ST2084: + case HAL_DATASPACE_TRANSFER_HLG: + switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_ST2084: + shader.append(R"( + float3 ToneMap(float3 xyz) { + return xyz; + } + )"); + break; + case HAL_DATASPACE_TRANSFER_HLG: + // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so + // we'll clamp the luminance range in case we're mapping from PQ input to HLG + // output. + shader.append(R"( + float3 ToneMap(float3 xyz) { + return clamp(xyz, 0.0, 1000.0); + } + )"); + break; + default: + // Here we're mapping from HDR to SDR content, so interpolate using a Hermitian + // polynomial onto the smaller luminance range. + shader.append(R"( + float3 ToneMap(float3 xyz) { + float maxInLumi = in_inputMaxLuminance; + float maxOutLumi = in_displayMaxLuminance; + + float nits = xyz.y; + + // clamp to max input luminance + nits = clamp(nits, 0.0, maxInLumi); + + // scale [0.0, maxInLumi] to [0.0, maxOutLumi] + if (maxInLumi <= maxOutLumi) { + return xyz * (maxOutLumi / maxInLumi); + } else { + // three control points + const float x0 = 10.0; + const float y0 = 17.0; + float x1 = maxOutLumi * 0.75; + float y1 = x1; + float x2 = x1 + (maxInLumi - x1) / 2.0; + float y2 = y1 + (maxOutLumi - y1) * 0.75; + + // horizontal distances between the last three control points + float h12 = x2 - x1; + float h23 = maxInLumi - x2; + // tangents at the last three control points + float m1 = (y2 - y1) / h12; + float m3 = (maxOutLumi - y2) / h23; + float m2 = (m1 + m3) / 2.0; + + if (nits < x0) { + // scale [0.0, x0] to [0.0, y0] linearly + float slope = y0 / x0; + return xyz * slope; + } else if (nits < x1) { + // scale [x0, x1] to [y0, y1] linearly + float slope = (y1 - y0) / (x1 - x0); + nits = y0 + (nits - x0) * slope; + } else if (nits < x2) { + // scale [x1, x2] to [y1, y2] using Hermite interp + float t = (nits - x1) / h12; + nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) + + (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t; + } else { + // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp + float t = (nits - x2) / h23; + nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) + + (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t; + } + } + + // color.y is greater than x0 and is thus non-zero + return xyz * (nits / xyz.y); + } + )"); + break; + } + break; + default: + switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_ST2084: + case HAL_DATASPACE_TRANSFER_HLG: + // Map from SDR onto an HDR output buffer + // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto + // [0, maxOutLumi] which is hard-coded to be 3000 nits. + shader.append(R"( + float3 ToneMap(float3 xyz) { + const float maxOutLumi = 3000.0; + + const float x0 = 5.0; + const float y0 = 2.5; + float x1 = in_displayMaxLuminance * 0.7; + float y1 = maxOutLumi * 0.15; + float x2 = in_displayMaxLuminance * 0.9; + float y2 = maxOutLumi * 0.45; + float x3 = in_displayMaxLuminance; + float y3 = maxOutLumi; + + float c1 = y1 / 3.0; + float c2 = y2 / 2.0; + float c3 = y3 / 1.5; + + float nits = xyz.y; + + if (nits <= x0) { + // scale [0.0, x0] to [0.0, y0] linearly + float slope = y0 / x0; + return xyz * slope; + } else if (nits <= x1) { + // scale [x0, x1] to [y0, y1] using a curve + float t = (nits - x0) / (x1 - x0); + nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1; + } else if (nits <= x2) { + // scale [x1, x2] to [y1, y2] using a curve + float t = (nits - x1) / (x2 - x1); + nits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + t * t * y2; + } else { + // scale [x2, x3] to [y2, y3] using a curve + float t = (nits - x2) / (x3 - x2); + nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3; + } + + // xyz.y is greater than x0 and is thus non-zero + return xyz * (nits / xyz.y); + } + )"); + break; + default: + // For completeness, this is tone-mapping from SDR to SDR, where this is just a + // no-op. + shader.append(R"( + float3 ToneMap(float3 xyz) { + return xyz; + } + )"); + break; + } + break; + } +} + +// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1]) +static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace, SkString& shader) { + switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_ST2084: + shader.append(R"( + float3 NormalizeLuminance(float3 xyz) { + return xyz / 10000.0; + } + )"); + break; + case HAL_DATASPACE_TRANSFER_HLG: + shader.append(R"( + float3 NormalizeLuminance(float3 xyz) { + return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2); + } + )"); + break; + default: + shader.append(R"( + float3 NormalizeLuminance(float3 xyz) { + return xyz / in_displayMaxLuminance; + } + )"); + break; + } +} + +static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace, + SkString& shader) { + // Input uniforms + shader.append(R"( + uniform float in_displayMaxLuminance; + uniform float in_inputMaxLuminance; + )"); + + generateLuminanceScalesForOOTF(inputDataspace, shader); + generateToneMapInterpolation(inputDataspace, outputDataspace, shader); + generateLuminanceNormalizationForOOTF(outputDataspace, shader); + + shader.append(R"( + float3 OOTF(float3 xyz) { + return NormalizeLuminance(ToneMap(ScaleLuminance(xyz))); + } + )"); +} + +static void generateOETF(ui::Dataspace dataspace, SkString& shader) { + switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_ST2084: + shader.append(R"( + + float3 OETF(float3 xyz) { + float m1 = (2610.0 / 4096.0) / 4.0; + float m2 = (2523.0 / 4096.0) * 128.0; + float c1 = (3424.0 / 4096.0); + float c2 = (2413.0 / 4096.0) * 32.0; + float c3 = (2392.0 / 4096.0) * 32.0; + + float3 tmp = pow(xyz, float3(m1)); + tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp); + return pow(tmp, float3(m2)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_HLG: + shader.append(R"( + float OETF_channel(float channel) { + const float a = 0.17883277; + const float b = 0.28466892; + const float c = 0.55991073; + return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) : + a * log(12.0 * channel - b) + c; + } + + float3 OETF(float3 linear) { + return float3(OETF_channel(linear.r), OETF_channel(linear.g), + OETF_channel(linear.b)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_LINEAR: + shader.append(R"( + float3 OETF(float3 linear) { + return linear; + } + )"); + break; + case HAL_DATASPACE_TRANSFER_SRGB: + default: + shader.append(R"( + float OETF_sRGB(float linear) { + return linear <= 0.0031308 ? + linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055; + } + + float3 OETF_sRGB(float3 linear) { + return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b)); + } + + float3 OETF(float3 linear) { + return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)); + } + )"); + break; + } +} + +static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader) { + shader.append(R"( + in shader input; + half4 main(float2 xy) { + float4 c = float4(sample(input, xy)); + )"); + if (undoPremultipliedAlpha) { + shader.append(R"( + c.rgb = c.rgb / (c.a + 0.0019); + )"); + } + shader.append(R"( + c.rgb = OETF(ToRGB(OOTF(ToXYZ(EOTF(c.rgb))))); + )"); + if (undoPremultipliedAlpha) { + shader.append(R"( + c.rgb = c.rgb * (c.a + 0.0019); + )"); + } + shader.append(R"( + return c; + } + )"); +} +static ColorSpace toColorSpace(ui::Dataspace dataspace) { + switch (dataspace & HAL_DATASPACE_STANDARD_MASK) { + case HAL_DATASPACE_STANDARD_BT709: + return ColorSpace::sRGB(); + break; + case HAL_DATASPACE_STANDARD_DCI_P3: + return ColorSpace::DisplayP3(); + break; + case HAL_DATASPACE_STANDARD_BT2020: + return ColorSpace::BT2020(); + break; + default: + return ColorSpace::sRGB(); + break; + } +} + +sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) { + ATRACE_CALL(); + SkString shaderString; + generateEOTF(linearEffect.inputDataspace, shaderString); + generateXYZTransforms(shaderString); + generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString); + generateOETF(linearEffect.outputDataspace, shaderString); + generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString); + + auto [shader, error] = SkRuntimeEffect::Make(shaderString); + if (!shader) { + LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str()); + } + return shader; +} + +sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEffect& linearEffect, + sk_sp<SkRuntimeEffect> runtimeEffect, + const mat4& colorTransform, float maxDisplayLuminance, + float maxMasteringLuminance, float maxContentLuminance) { + ATRACE_CALL(); + SkRuntimeShaderBuilder effectBuilder(runtimeEffect); + + effectBuilder.child("input") = shader; + + if (linearEffect.inputDataspace == linearEffect.outputDataspace) { + effectBuilder.uniform("in_rgbToXyz") = mat4(); + effectBuilder.uniform("in_xyzToRgb") = colorTransform; + } else { + ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace); + ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace); + + effectBuilder.uniform("in_rgbToXyz") = mat4(inputColorSpace.getRGBtoXYZ()); + effectBuilder.uniform("in_xyzToRgb") = + colorTransform * mat4(outputColorSpace.getXYZtoRGB()); + } + + effectBuilder.uniform("in_displayMaxLuminance") = maxDisplayLuminance; + effectBuilder.uniform("in_inputMaxLuminance") = + std::min(maxMasteringLuminance, maxContentLuminance); + return effectBuilder.makeShader(nullptr, false); +} + +} // namespace skia +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h new file mode 100644 index 0000000000..20b8338931 --- /dev/null +++ b/libs/renderengine/skia/filters/LinearEffect.h @@ -0,0 +1,104 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/mat4.h> + +#include <optional> + +#include "SkRuntimeEffect.h" +#include "SkShader.h" +#include "ui/GraphicTypes.h" + +namespace android { +namespace renderengine { +namespace skia { + +/** + * Arguments for creating an effect that applies color transformations in linear XYZ space. + * A linear effect is decomposed into the following steps when operating on an image: + * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended + * relative display brightness of the scene in nits for each RGB channel + * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display + * luminance. + * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone + * mapping to display SDR content alongside HDR content, or any number of subjective transformations + * 4. Transformation matrix from linear XYZ back to linear RGB brightness. + * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to + * output RGB colors. + * + * For further reading, consult the recommendation in ITU-R BT.2390-4: + * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf + * + * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is + * intended to be the output surface. However, Skia does not support complex tone mapping such as + * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied + * to the source colors. so that the tone mapping process is only applied once by this effect. Tone + * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions) + * alongside other content, whereby maximum input luminance is mapped to maximum output luminance + * and intermediate values are interpolated. + */ +struct LinearEffect { + // Input dataspace of the source colors. + const ui::Dataspace inputDataspace = ui::Dataspace::SRGB; + + // Working dataspace for the output surface, for conversion from linear space. + const ui::Dataspace outputDataspace = ui::Dataspace::SRGB; + + // Sets whether alpha premultiplication must be undone. + // This is required if the source colors use premultiplied alpha and is not opaque. + const bool undoPremultipliedAlpha = false; +}; + +static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) { + return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace && + lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha; +} + +struct LinearEffectHasher { + // Inspired by art/runtime/class_linker.cc + // Also this is what boost:hash_combine does + static size_t HashCombine(size_t seed, size_t val) { + return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2)); + } + size_t operator()(const LinearEffect& le) const { + size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace); + result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace)); + return HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha)); + } +}; + +sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect); + +// Generates a shader resulting from applying the a linear effect created from +// LinearEffectArgs::buildEffect to an inputShader. +// Optionally, a color transform may also be provided, which combines with the +// matrix transforming from linear XYZ to linear RGB immediately before OETF. +// We also provide additional HDR metadata upon creating the shader: +// * The max display luminance is the max luminance of the physical display in nits +// * The max mastering luminance is provided as the max luminance from the SMPTE 2086 +// standard. +// * The max content luminance is provided as the max light level from the CTA 861.3 +// standard. +sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader, + const LinearEffect& linearEffect, + sk_sp<SkRuntimeEffect> runtimeEffect, + const mat4& colorTransform, float maxDisplayLuminance, + float maxMasteringLuminance, float maxContentLuminance); +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp index fdb3a6f8d0..51c702884b 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -12,26 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_test { name: "librenderengine_test", - defaults: ["surfaceflinger_defaults"], + defaults: ["skia_deps", "surfaceflinger_defaults"], test_suites: ["device-tests"], srcs: [ "RenderEngineTest.cpp", + "RenderEngineThreadedTest.cpp", + ], + include_dirs: [ + "external/skia/src/gpu", ], static_libs: [ "libgmock", "librenderengine", + "librenderengine_mocks", ], + shared_libs: [ "libbase", "libcutils", diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 1ec9412019..886c9da70a 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -14,20 +14,27 @@ * limitations under the License. */ +#undef LOG_TAG +#define LOG_TAG "RenderEngineTest" + // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" +#pragma clang diagnostic ignored "-Wextra" -#include <chrono> -#include <condition_variable> -#include <fstream> - -#include <gtest/gtest.h> #include <cutils/properties.h> +#include <gtest/gtest.h> #include <renderengine/RenderEngine.h> #include <sync/sync.h> #include <ui/PixelFormat.h> + +#include <chrono> +#include <condition_variable> +#include <fstream> + #include "../gl/GLESRenderEngine.h" +#include "../skia/SkiaGLRenderEngine.h" +#include "../threaded/RenderEngineThreaded.h" constexpr int DEFAULT_DISPLAY_WIDTH = 128; constexpr int DEFAULT_DISPLAY_HEIGHT = 256; @@ -36,37 +43,128 @@ constexpr bool WRITE_BUFFER_TO_FILE_ON_FAILURE = false; namespace android { -struct RenderEngineTest : public ::testing::Test { - static void SetUpTestSuite() { +class RenderEngineFactory { +public: + virtual ~RenderEngineFactory() = default; + + virtual std::string name() = 0; + virtual renderengine::RenderEngine::RenderEngineType type() = 0; + virtual std::unique_ptr<renderengine::RenderEngine> createRenderEngine() = 0; + virtual std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() { + return nullptr; + } +}; + +class GLESRenderEngineFactory : public RenderEngineFactory { +public: + std::string name() override { return "GLESRenderEngineFactory"; } + + renderengine::RenderEngine::RenderEngineType type() { + return renderengine::RenderEngine::RenderEngineType::GLES; + } + + std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { + return createGLESRenderEngine(); + } + + std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() { renderengine::RenderEngineCreationArgs reCreationArgs = - renderengine::RenderEngineCreationArgs::Builder() - .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) - .setImageCacheSize(1) - .setUseColorManagerment(false) - .setEnableProtectedContext(false) - .setPrecacheToneMapperShaderOnly(false) - .setSupportsBackgroundBlur(true) - .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) - .build(); - sRE = renderengine::gl::GLESRenderEngine::create(reCreationArgs); - - reCreationArgs.useColorManagement = true; - sRECM = renderengine::gl::GLESRenderEngine::create(reCreationArgs); + renderengine::RenderEngineCreationArgs::Builder() + .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) + .setImageCacheSize(1) + .setUseColorManagerment(false) + .setEnableProtectedContext(false) + .setPrecacheToneMapperShaderOnly(false) + .setSupportsBackgroundBlur(true) + .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) + .setRenderEngineType(type()) + .build(); + return renderengine::gl::GLESRenderEngine::create(reCreationArgs); + } +}; + +class GLESCMRenderEngineFactory : public RenderEngineFactory { +public: + std::string name() override { return "GLESCMRenderEngineFactory"; } + + renderengine::RenderEngine::RenderEngineType type() { + return renderengine::RenderEngine::RenderEngineType::GLES; } - static void TearDownTestSuite() { - // The ordering here is important - sCurrentBuffer must live longer - // than RenderEngine to avoid a null reference on tear-down. - sRE = nullptr; - sRECM = nullptr; - sCurrentBuffer = nullptr; + std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { + return createGLESRenderEngine(); } + std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() override { + renderengine::RenderEngineCreationArgs reCreationArgs = + renderengine::RenderEngineCreationArgs::Builder() + .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) + .setImageCacheSize(1) + .setEnableProtectedContext(false) + .setPrecacheToneMapperShaderOnly(false) + .setSupportsBackgroundBlur(true) + .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) + .setRenderEngineType(type()) + .setUseColorManagerment(true) + .build(); + return renderengine::gl::GLESRenderEngine::create(reCreationArgs); + } +}; + +class SkiaGLESRenderEngineFactory : public RenderEngineFactory { +public: + std::string name() override { return "SkiaGLRenderEngineFactory"; } + + renderengine::RenderEngine::RenderEngineType type() { + return renderengine::RenderEngine::RenderEngineType::SKIA_GL; + } + + std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { + renderengine::RenderEngineCreationArgs reCreationArgs = + renderengine::RenderEngineCreationArgs::Builder() + .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) + .setImageCacheSize(1) + .setEnableProtectedContext(false) + .setPrecacheToneMapperShaderOnly(false) + .setSupportsBackgroundBlur(true) + .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) + .setRenderEngineType(type()) + .build(); + return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs); + } +}; + +class SkiaGLESCMRenderEngineFactory : public RenderEngineFactory { +public: + std::string name() override { return "SkiaGLCMRenderEngineFactory"; } + + renderengine::RenderEngine::RenderEngineType type() { + return renderengine::RenderEngine::RenderEngineType::SKIA_GL; + } + + std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { + renderengine::RenderEngineCreationArgs reCreationArgs = + renderengine::RenderEngineCreationArgs::Builder() + .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) + .setImageCacheSize(1) + .setEnableProtectedContext(false) + .setPrecacheToneMapperShaderOnly(false) + .setSupportsBackgroundBlur(true) + .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) + .setRenderEngineType(type()) + .setUseColorManagerment(true) + .build(); + return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs); + } +}; + +class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> { +public: static sp<GraphicBuffer> allocateDefaultBuffer() { return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | - GRALLOC_USAGE_HW_RENDER, + GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, "output"); } @@ -78,19 +176,26 @@ struct RenderEngineTest : public ::testing::Test { "input"); } - RenderEngineTest() { mBuffer = allocateDefaultBuffer(); } + RenderEngineTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + mBuffer = allocateDefaultBuffer(); + } ~RenderEngineTest() { if (WRITE_BUFFER_TO_FILE_ON_FAILURE && ::testing::Test::HasFailure()) { writeBufferToFile("/data/texture_out_"); } for (uint32_t texName : mTexNames) { - sRE->deleteTextures(1, &texName); - EXPECT_FALSE(sRE->isTextureNameKnownForTesting(texName)); - } - for (uint32_t texName : mTexNamesCM) { - sRECM->deleteTextures(1, &texName); + mRE->deleteTextures(1, &texName); + if (mGLESRE != nullptr) { + EXPECT_FALSE(mGLESRE->isTextureNameKnownForTesting(texName)); + } } + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } void writeBufferToFile(const char* basename) { @@ -233,6 +338,26 @@ struct RenderEngineTest : public ::testing::Test { backgroundColor.a); } + void expectShadowColorWithoutCaster(const FloatRect& casterBounds, + const renderengine::ShadowSettings& shadow, + const ubyte4& backgroundColor) { + const float shadowInset = shadow.length * -1.0f; + const Rect casterRect(casterBounds); + const Rect shadowRect = + Rect(casterRect).inset(shadowInset, shadowInset, shadowInset, shadowInset); + + const Region backgroundRegion = + Region(fullscreenRect()).subtractSelf(casterRect).subtractSelf(shadowRect); + + expectAlpha(shadowRect, 255); + // (0, 0, 0) fill on the bounds of the layer should be ignored. + expectBufferColor(casterRect, 255, 255, 255, 255, 254); + + // verify background + expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b, + backgroundColor.a); + } + static renderengine::ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength, bool casterIsTranslucent) { renderengine::ShadowSettings shadow; @@ -258,16 +383,11 @@ struct RenderEngineTest : public ::testing::Test { } void invokeDraw(renderengine::DisplaySettings settings, - std::vector<const renderengine::LayerSettings*> layers, - sp<GraphicBuffer> buffer, - bool useColorManagement = false) { + std::vector<const renderengine::LayerSettings*> layers) { base::unique_fd fence; - status_t status = useColorManagement ? - sRECM ->drawLayers(settings, layers, buffer->getNativeBuffer(), true, - base::unique_fd(), &fence) : - sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true, - base::unique_fd(), &fence); - sCurrentBuffer = buffer; + status_t status = + mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence); + mCurrentBuffer = mBuffer; int fd = fence.release(); if (fd >= 0) { @@ -276,21 +396,15 @@ struct RenderEngineTest : public ::testing::Test { } ASSERT_EQ(NO_ERROR, status); - if (layers.size() > 0) { - if (useColorManagement) { - ASSERT_TRUE(sRECM->isFramebufferImageCachedForTesting(buffer->getId())); - } else { - ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId())); - } + if (layers.size() > 0 && mGLESRE != nullptr) { + ASSERT_TRUE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getId())); } } void drawEmptyLayers() { renderengine::DisplaySettings settings; std::vector<const renderengine::LayerSettings*> layers; - // Meaningless buffer since we don't do any drawing - sp<GraphicBuffer> buffer = new GraphicBuffer(); - invokeDraw(settings, layers, buffer); + invokeDraw(settings, layers); } template <typename SourceVariant> @@ -336,15 +450,12 @@ struct RenderEngineTest : public ::testing::Test { void fillBufferLayerTransform(); template <typename SourceVariant> - void fillBufferWithColorTransform(bool useColorManagement = false); + void fillBufferWithColorTransform(); template <typename SourceVariant> void fillBufferColorTransform(); template <typename SourceVariant> - void fillBufferColorTransformCM(); - - template <typename SourceVariant> void fillBufferWithColorTransformZeroLayerAlpha(); template <typename SourceVariant> @@ -385,38 +496,54 @@ struct RenderEngineTest : public ::testing::Test { const renderengine::ShadowSettings& shadow, const ubyte4& casterColor, const ubyte4& backgroundColor); - // Keep around the same renderengine object to save on initialization time. - // For now, exercise the GL backend directly so that some caching specifics - // can be tested without changing the interface. - static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRE; - // renderengine object with Color Management enabled - static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRECM; + void drawShadowWithoutCaster(const FloatRect& castingBounds, + const renderengine::ShadowSettings& shadow, + const ubyte4& backgroundColor); + + void initializeRenderEngine(); + + std::unique_ptr<renderengine::RenderEngine> mRE; + // GLESRenderEngine for testing GLES-specific behavior. + // Owened by mRE, but this is downcasted. + renderengine::gl::GLESRenderEngine* mGLESRE = nullptr; + // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to // be freed *after* RenderEngine is destroyed, so that the EGL image is // destroyed first. - static sp<GraphicBuffer> sCurrentBuffer; + sp<GraphicBuffer> mCurrentBuffer; sp<GraphicBuffer> mBuffer; std::vector<uint32_t> mTexNames; - std::vector<uint32_t> mTexNamesCM; }; -std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr; -std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRECM = nullptr; - -sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr; +void RenderEngineTest::initializeRenderEngine() { + const auto& renderEngineFactory = GetParam(); + if (renderEngineFactory->type() == renderengine::RenderEngine::RenderEngineType::GLES) { + // Only GLESRenderEngine exposes test-only methods. Provide a pointer to the + // GLESRenderEngine if we're using it so that we don't need to dynamic_cast + // every time. + std::unique_ptr<renderengine::gl::GLESRenderEngine> renderEngine = + renderEngineFactory->createGLESRenderEngine(); + mGLESRE = renderEngine.get(); + mRE = std::move(renderEngine); + } else { + mRE = renderEngineFactory->createRenderEngine(); + } +} struct ColorSourceVariant { static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b, - RenderEngineTest* /*fixture*/, bool /*useColorManagement*/ = false) { + RenderEngineTest* /*fixture*/) { layer.source.solidColor = half3(r, g, b); + layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; } }; struct RelaxOpaqueBufferVariant { static void setOpaqueBit(renderengine::LayerSettings& layer) { layer.source.buffer.isOpaque = false; + layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; } static uint8_t getAlphaChannel() { return 255; } @@ -425,29 +552,24 @@ struct RelaxOpaqueBufferVariant { struct ForceOpaqueBufferVariant { static void setOpaqueBit(renderengine::LayerSettings& layer) { layer.source.buffer.isOpaque = true; + layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; } static uint8_t getAlphaChannel() { // The isOpaque bit will override the alpha channel, so this should be // arbitrary. - return 10; + return 50; } }; template <typename OpaquenessVariant> struct BufferSourceVariant { static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b, - RenderEngineTest* fixture, - bool useColorManagement = false) { + RenderEngineTest* fixture) { sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1); uint32_t texName; - if (useColorManagement) { - fixture->sRECM->genTextures(1, &texName); - fixture->mTexNamesCM.push_back(texName); - } else { - fixture->sRE->genTextures(1, &texName); - fixture->mTexNames.push_back(texName); - } + fixture->mRE->genTextures(1, &texName); + fixture->mTexNames.push_back(texName); uint8_t* pixels; buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, @@ -468,6 +590,7 @@ struct BufferSourceVariant { layer.source.buffer.buffer = buf; layer.source.buffer.textureName = texName; + layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; OpaquenessVariant::setOpaqueBit(layer); } }; @@ -477,17 +600,19 @@ void RenderEngineTest::fillBuffer(half r, half g, half b, half a) { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; std::vector<const renderengine::LayerSettings*> layers; renderengine::LayerSettings layer; + layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; layer.geometry.boundaries = fullscreenRect().toFloatRect(); SourceVariant::fillColor(layer, r, g, b, this); layer.alpha = a; layers.push_back(&layer); - invokeDraw(settings, layers, mBuffer); + invokeDraw(settings, layers); } template <typename SourceVariant> @@ -517,18 +642,20 @@ void RenderEngineTest::fillRedTransparentBuffer() { template <typename SourceVariant> void RenderEngineTest::fillRedOffsetBuffer() { renderengine::DisplaySettings settings; + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; settings.physicalDisplay = offsetRect(); settings.clip = offsetRectAtZero(); std::vector<const renderengine::LayerSettings*> layers; renderengine::LayerSettings layer; + layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; layer.geometry.boundaries = offsetRectAtZero().toFloatRect(); SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this); layer.alpha = 1.0f; layers.push_back(&layer); - invokeDraw(settings, layers, mBuffer); + invokeDraw(settings, layers); } template <typename SourceVariant> @@ -548,6 +675,7 @@ void RenderEngineTest::fillBufferPhysicalOffset() { template <typename SourceVariant> void RenderEngineTest::fillBufferCheckers(uint32_t orientationFlag) { renderengine::DisplaySettings settings; + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; settings.physicalDisplay = fullscreenRect(); // Here logical space is 2x2 settings.clip = Rect(2, 2); @@ -556,18 +684,21 @@ void RenderEngineTest::fillBufferCheckers(uint32_t orientationFlag) { std::vector<const renderengine::LayerSettings*> layers; renderengine::LayerSettings layerOne; + layerOne.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; Rect rectOne(0, 0, 1, 1); layerOne.geometry.boundaries = rectOne.toFloatRect(); SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this); layerOne.alpha = 1.0f; renderengine::LayerSettings layerTwo; + layerTwo.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; Rect rectTwo(0, 1, 1, 2); layerTwo.geometry.boundaries = rectTwo.toFloatRect(); SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this); layerTwo.alpha = 1.0f; renderengine::LayerSettings layerThree; + layerThree.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; Rect rectThree(1, 0, 2, 1); layerThree.geometry.boundaries = rectThree.toFloatRect(); SourceVariant::fillColor(layerThree, 0.0f, 0.0f, 1.0f, this); @@ -577,7 +708,7 @@ void RenderEngineTest::fillBufferCheckers(uint32_t orientationFlag) { layers.push_back(&layerTwo); layers.push_back(&layerThree); - invokeDraw(settings, layers, mBuffer); + invokeDraw(settings, layers); } template <typename SourceVariant> @@ -650,10 +781,12 @@ void RenderEngineTest::fillBufferWithLayerTransform() { settings.physicalDisplay = fullscreenRect(); // Here logical space is 2x2 settings.clip = Rect(2, 2); + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; std::vector<const renderengine::LayerSettings*> layers; renderengine::LayerSettings layer; + layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; layer.geometry.boundaries = Rect(1, 1).toFloatRect(); // Translate one pixel diagonally layer.geometry.positionTransform = mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1); @@ -663,7 +796,7 @@ void RenderEngineTest::fillBufferWithLayerTransform() { layers.push_back(&layer); - invokeDraw(settings, layers, mBuffer); + invokeDraw(settings, layers); } template <typename SourceVariant> @@ -677,16 +810,18 @@ void RenderEngineTest::fillBufferLayerTransform() { } template <typename SourceVariant> -void RenderEngineTest::fillBufferWithColorTransform(bool useColorManagement) { +void RenderEngineTest::fillBufferWithColorTransform() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = Rect(1, 1); + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; std::vector<const renderengine::LayerSettings*> layers; renderengine::LayerSettings layer; + layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; layer.geometry.boundaries = Rect(1, 1).toFloatRect(); - SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this, useColorManagement); + SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this); layer.alpha = 1.0f; // construct a fake color matrix @@ -700,7 +835,7 @@ void RenderEngineTest::fillBufferWithColorTransform(bool useColorManagement) { layers.push_back(&layer); - invokeDraw(settings, layers, mBuffer, useColorManagement); + invokeDraw(settings, layers); } template <typename SourceVariant> @@ -710,12 +845,6 @@ void RenderEngineTest::fillBufferColorTransform() { } template <typename SourceVariant> -void RenderEngineTest::fillBufferColorTransformCM() { - fillBufferWithColorTransform<SourceVariant>(true); - expectBufferColor(fullscreenRect(), 126, 0, 0, 255, 1); -} - -template <typename SourceVariant> void RenderEngineTest::fillBufferWithColorTransformZeroLayerAlpha() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); @@ -730,16 +859,13 @@ void RenderEngineTest::fillBufferWithColorTransformZeroLayerAlpha() { // construct a fake color matrix // simple inverse color - settings.colorTransform = mat4(-1, 0, 0, 0, - 0, -1, 0, 0, - 0, 0, -1, 0, - 1, 1, 1, 1); + settings.colorTransform = mat4(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 1, 1, 1, 1); layer.geometry.boundaries = Rect(1, 1).toFloatRect(); layers.push_back(&layer); - invokeDraw(settings, layers, mBuffer); + invokeDraw(settings, layers); } template <typename SourceVariant> @@ -753,10 +879,12 @@ void RenderEngineTest::fillRedBufferWithRoundedCorners() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; std::vector<const renderengine::LayerSettings*> layers; renderengine::LayerSettings layer; + layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; layer.geometry.boundaries = fullscreenRect().toFloatRect(); layer.geometry.roundedCornersRadius = 5.0f; layer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect(); @@ -765,7 +893,7 @@ void RenderEngineTest::fillRedBufferWithRoundedCorners() { layers.push_back(&layer); - invokeDraw(settings, layers, mBuffer); + invokeDraw(settings, layers); } template <typename SourceVariant> @@ -797,18 +925,21 @@ void RenderEngineTest::fillBufferAndBlurBackground() { auto center = DEFAULT_DISPLAY_WIDTH / 2; renderengine::DisplaySettings settings; + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); std::vector<const renderengine::LayerSettings*> layers; renderengine::LayerSettings backgroundLayer; + backgroundLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect(); SourceVariant::fillColor(backgroundLayer, 0.0f, 1.0f, 0.0f, this); backgroundLayer.alpha = 1.0f; layers.push_back(&backgroundLayer); renderengine::LayerSettings leftLayer; + leftLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; leftLayer.geometry.boundaries = Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT).toFloatRect(); SourceVariant::fillColor(leftLayer, 1.0f, 0.0f, 0.0f, this); @@ -816,17 +947,21 @@ void RenderEngineTest::fillBufferAndBlurBackground() { layers.push_back(&leftLayer); renderengine::LayerSettings blurLayer; + blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; blurLayer.geometry.boundaries = fullscreenRect().toFloatRect(); blurLayer.backgroundBlurRadius = blurRadius; + SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this); blurLayer.alpha = 0; layers.push_back(&blurLayer); - invokeDraw(settings, layers, mBuffer); + invokeDraw(settings, layers); + + // solid color + expectBufferColor(Rect(0, 0, 1, 1), 255, 0, 0, 255, 0 /* tolerance */); - expectBufferColor(Rect(center - 1, center - 5, center, center + 5), 150, 150, 0, 255, - 50 /* tolerance */); - expectBufferColor(Rect(center, center - 5, center + 1, center + 5), 150, 150, 0, 255, - 50 /* tolerance */); + // blurred color (downsampling should result in the center color being close to 128) + expectBufferColor(Rect(center - 1, center - 5, center + 1, center + 5), 128, 128, 0, 255, + 10 /* tolerance */); } template <typename SourceVariant> @@ -834,17 +969,19 @@ void RenderEngineTest::overlayCorners() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; std::vector<const renderengine::LayerSettings*> layersFirst; renderengine::LayerSettings layerOne; + layerOne.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; layerOne.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0); SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this); layerOne.alpha = 0.2; layersFirst.push_back(&layerOne); - invokeDraw(settings, layersFirst, mBuffer); + invokeDraw(settings, layersFirst); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 51, 0, 0, 51); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), @@ -852,6 +989,7 @@ void RenderEngineTest::overlayCorners() { std::vector<const renderengine::LayerSettings*> layersSecond; renderengine::LayerSettings layerTwo; + layerTwo.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; layerTwo.geometry.boundaries = FloatRect(DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); @@ -859,7 +997,7 @@ void RenderEngineTest::overlayCorners() { layerTwo.alpha = 1.0f; layersSecond.push_back(&layerTwo); - invokeDraw(settings, layersSecond, mBuffer); + invokeDraw(settings, layersSecond); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 0, 0, 0, 0); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1, @@ -871,15 +1009,17 @@ void RenderEngineTest::fillRedBufferTextureTransform() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = Rect(1, 1); + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; std::vector<const renderengine::LayerSettings*> layers; renderengine::LayerSettings layer; + layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; // Here will allocate a checker board texture, but transform texture // coordinates so that only the upper left is applied. sp<GraphicBuffer> buf = allocateSourceBuffer(2, 2); uint32_t texName; - RenderEngineTest::sRE->genTextures(1, &texName); + RenderEngineTest::mRE->genTextures(1, &texName); this->mTexNames.push_back(texName); uint8_t* pixels; @@ -909,7 +1049,7 @@ void RenderEngineTest::fillRedBufferTextureTransform() { layers.push_back(&layer); - invokeDraw(settings, layers, mBuffer); + invokeDraw(settings, layers); } void RenderEngineTest::fillBufferTextureTransform() { @@ -928,7 +1068,7 @@ void RenderEngineTest::fillRedBufferWithPremultiplyAlpha() { renderengine::LayerSettings layer; sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1); uint32_t texName; - RenderEngineTest::sRE->genTextures(1, &texName); + RenderEngineTest::mRE->genTextures(1, &texName); this->mTexNames.push_back(texName); uint8_t* pixels; @@ -948,7 +1088,7 @@ void RenderEngineTest::fillRedBufferWithPremultiplyAlpha() { layers.push_back(&layer); - invokeDraw(settings, layers, mBuffer); + invokeDraw(settings, layers); } void RenderEngineTest::fillBufferWithPremultiplyAlpha() { @@ -967,7 +1107,7 @@ void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() { renderengine::LayerSettings layer; sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1); uint32_t texName; - RenderEngineTest::sRE->genTextures(1, &texName); + RenderEngineTest::mRE->genTextures(1, &texName); this->mTexNames.push_back(texName); uint8_t* pixels; @@ -987,7 +1127,7 @@ void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() { layers.push_back(&layer); - invokeDraw(settings, layers, mBuffer); + invokeDraw(settings, layers); } void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() { @@ -1005,7 +1145,7 @@ void RenderEngineTest::clearLeftRegion() { // fake layer, without bounds should not render anything renderengine::LayerSettings layer; layers.push_back(&layer); - invokeDraw(settings, layers, mBuffer); + invokeDraw(settings, layers); } void RenderEngineTest::clearRegion() { @@ -1022,6 +1162,7 @@ void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLaye const renderengine::ShadowSettings& shadow, const ubyte4& casterColor, const ubyte4& backgroundColor) { renderengine::DisplaySettings settings; + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); @@ -1029,6 +1170,7 @@ void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLaye // add background layer renderengine::LayerSettings bgLayer; + bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; bgLayer.geometry.boundaries = fullscreenRect().toFloatRect(); ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f, backgroundColor.b / 255.0f, this); @@ -1037,6 +1179,7 @@ void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLaye // add shadow layer renderengine::LayerSettings shadowLayer; + shadowLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; shadowLayer.geometry.boundaries = castingLayer.geometry.boundaries; shadowLayer.alpha = castingLayer.alpha; shadowLayer.shadow = shadow; @@ -1044,32 +1187,108 @@ void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLaye // add layer casting the shadow renderengine::LayerSettings layer = castingLayer; + layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; SourceVariant::fillColor(layer, casterColor.r / 255.0f, casterColor.g / 255.0f, casterColor.b / 255.0f, this); layers.push_back(&layer); - invokeDraw(settings, layers, mBuffer); + invokeDraw(settings, layers); } -TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) { +void RenderEngineTest::drawShadowWithoutCaster(const FloatRect& castingBounds, + const renderengine::ShadowSettings& shadow, + const ubyte4& backgroundColor) { + renderengine::DisplaySettings settings; + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + std::vector<const renderengine::LayerSettings*> layers; + + // add background layer + renderengine::LayerSettings bgLayer; + bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; + bgLayer.geometry.boundaries = fullscreenRect().toFloatRect(); + ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f, + backgroundColor.b / 255.0f, this); + bgLayer.alpha = backgroundColor.a / 255.0f; + layers.push_back(&bgLayer); + + // add shadow layer + renderengine::LayerSettings shadowLayer; + shadowLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; + shadowLayer.geometry.boundaries = castingBounds; + shadowLayer.alpha = 1.0f; + ColorSourceVariant::fillColor(shadowLayer, 0, 0, 0, this); + shadowLayer.shadow = shadow; + layers.push_back(&shadowLayer); + + invokeDraw(settings, layers); +} + +INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest, + testing::Values(std::make_shared<GLESRenderEngineFactory>(), + std::make_shared<GLESCMRenderEngineFactory>(), + std::make_shared<SkiaGLESRenderEngineFactory>(), + std::make_shared<SkiaGLESCMRenderEngineFactory>())); + +TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) { + initializeRenderEngine(); drawEmptyLayers(); } -TEST_F(RenderEngineTest, drawLayers_nullOutputBuffer) { +TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) { + const auto& renderEngineFactory = GetParam(); + mRE = renderEngineFactory->createRenderEngine(); + renderengine::DisplaySettings settings; + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + // 255, 255, 255, 255 is full opaque white. + const ubyte4 backgroundColor(255.f, 255.f, 255.f, 255.f); + // Create layer with given color. + renderengine::LayerSettings bgLayer; + bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; + bgLayer.geometry.boundaries = fullscreenRect().toFloatRect(); + bgLayer.source.solidColor = half3(backgroundColor.r / 255.0f, backgroundColor.g / 255.0f, + backgroundColor.b / 255.0f); + bgLayer.alpha = backgroundColor.a / 255.0f; + // Transform the red color. + bgLayer.colorTransform = mat4(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + + std::vector<const renderengine::LayerSettings*> layers; + layers.push_back(&bgLayer); + + invokeDraw(settings, layers); + + // Expect to see full opaque pixel (with inverted red from the transform). + expectBufferColor(Rect(0, 0, 10, 10), 0.f, backgroundColor.g, backgroundColor.b, + backgroundColor.a); +} + +TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) { + initializeRenderEngine(); + + renderengine::DisplaySettings settings; + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; std::vector<const renderengine::LayerSettings*> layers; renderengine::LayerSettings layer; layer.geometry.boundaries = fullscreenRect().toFloatRect(); BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this); layers.push_back(&layer); base::unique_fd fence; - status_t status = sRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence); + status_t status = mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence); ASSERT_EQ(BAD_VALUE, status); } -TEST_F(RenderEngineTest, drawLayers_nullOutputFence) { +TEST_P(RenderEngineTest, drawLayers_nullOutputFence) { + initializeRenderEngine(); + renderengine::DisplaySettings settings; + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); @@ -1080,15 +1299,24 @@ TEST_F(RenderEngineTest, drawLayers_nullOutputFence) { layer.alpha = 1.0; layers.push_back(&layer); - status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, - base::unique_fd(), nullptr); - sCurrentBuffer = mBuffer; + status_t status = mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr); + mCurrentBuffer = mBuffer; ASSERT_EQ(NO_ERROR, status); expectBufferColor(fullscreenRect(), 255, 0, 0, 255); } -TEST_F(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) { +TEST_P(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) { + const auto& renderEngineFactory = GetParam(); + + if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { + // GLES-specific test + return; + } + + initializeRenderEngine(); + renderengine::DisplaySettings settings; + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); @@ -1099,224 +1327,270 @@ TEST_F(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) { layer.alpha = 1.0; layers.push_back(&layer); - status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), false, - base::unique_fd(), nullptr); - sCurrentBuffer = mBuffer; + status_t status = mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr); + mCurrentBuffer = mBuffer; ASSERT_EQ(NO_ERROR, status); - ASSERT_FALSE(sRE->isFramebufferImageCachedForTesting(mBuffer->getId())); + ASSERT_FALSE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getId())); expectBufferColor(fullscreenRect(), 255, 0, 0, 255); } -TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) { +TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) { + initializeRenderEngine(); fillRedBuffer<ColorSourceVariant>(); } -TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) { +TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) { + initializeRenderEngine(); fillGreenBuffer<ColorSourceVariant>(); } -TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) { +TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) { + initializeRenderEngine(); fillBlueBuffer<ColorSourceVariant>(); } -TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) { +TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) { + initializeRenderEngine(); fillRedTransparentBuffer<ColorSourceVariant>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) { + initializeRenderEngine(); fillBufferPhysicalOffset<ColorSourceVariant>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) { + initializeRenderEngine(); fillBufferCheckersRotate0<ColorSourceVariant>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) { + initializeRenderEngine(); fillBufferCheckersRotate90<ColorSourceVariant>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) { + initializeRenderEngine(); fillBufferCheckersRotate180<ColorSourceVariant>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) { + initializeRenderEngine(); fillBufferCheckersRotate270<ColorSourceVariant>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) { + initializeRenderEngine(); fillBufferLayerTransform<ColorSourceVariant>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { + initializeRenderEngine(); fillBufferColorTransform<ColorSourceVariant>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_colorSource) { - fillBufferColorTransformCM<ColorSourceVariant>(); +TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) { + initializeRenderEngine(); + fillBufferWithRoundedCorners<ColorSourceVariant>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) { + initializeRenderEngine(); fillBufferColorTransformZeroLayerAlpha<ColorSourceVariant>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) { - fillBufferWithRoundedCorners<ColorSourceVariant>(); -} - -TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) { + initializeRenderEngine(); fillBufferAndBlurBackground<ColorSourceVariant>(); } -TEST_F(RenderEngineTest, drawLayers_overlayCorners_colorSource) { +TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) { + initializeRenderEngine(); overlayCorners<ColorSourceVariant>(); } -TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) { + initializeRenderEngine(); fillRedBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) { + initializeRenderEngine(); fillGreenBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) { + initializeRenderEngine(); fillBlueBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) { + initializeRenderEngine(); fillRedTransparentBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) { + initializeRenderEngine(); fillBufferPhysicalOffset<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) { + initializeRenderEngine(); fillBufferCheckersRotate0<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) { + initializeRenderEngine(); fillBufferCheckersRotate90<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) { + initializeRenderEngine(); fillBufferCheckersRotate180<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) { + initializeRenderEngine(); fillBufferCheckersRotate270<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) { + initializeRenderEngine(); fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) { + initializeRenderEngine(); fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_opaqueBufferSource) { - fillBufferColorTransformCM<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) { + initializeRenderEngine(); + fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) { + initializeRenderEngine(); fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) { - fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>(); -} - -TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) { + initializeRenderEngine(); fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) { +TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) { + initializeRenderEngine(); overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) { + initializeRenderEngine(); fillRedBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) { + initializeRenderEngine(); fillGreenBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) { + initializeRenderEngine(); fillBlueBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) { + initializeRenderEngine(); fillRedTransparentBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) { + initializeRenderEngine(); fillBufferPhysicalOffset<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) { + initializeRenderEngine(); fillBufferCheckersRotate0<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) { + initializeRenderEngine(); fillBufferCheckersRotate90<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) { + initializeRenderEngine(); fillBufferCheckersRotate180<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) { + initializeRenderEngine(); fillBufferCheckersRotate270<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) { + initializeRenderEngine(); fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { + initializeRenderEngine(); fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_bufferSource) { - fillBufferColorTransformCM<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) { + initializeRenderEngine(); + fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) { + initializeRenderEngine(); fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) { - fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); -} - -TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) { + initializeRenderEngine(); fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_overlayCorners_bufferSource) { +TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) { + initializeRenderEngine(); overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -TEST_F(RenderEngineTest, drawLayers_fillBufferTextureTransform) { +TEST_P(RenderEngineTest, drawLayers_fillBufferTextureTransform) { + initializeRenderEngine(); fillBufferTextureTransform(); } -TEST_F(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) { +TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) { + initializeRenderEngine(); fillBufferWithPremultiplyAlpha(); } -TEST_F(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { +TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { + initializeRenderEngine(); fillBufferWithoutPremultiplyAlpha(); } -TEST_F(RenderEngineTest, drawLayers_clearRegion) { +TEST_P(RenderEngineTest, drawLayers_clearRegion) { + initializeRenderEngine(); clearRegion(); } -TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) { +TEST_P(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) { + const auto& renderEngineFactory = GetParam(); + + if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { + // GLES-specific test + return; + } + + initializeRenderEngine(); + renderengine::DisplaySettings settings; + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); @@ -1327,48 +1601,32 @@ TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) { BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this); layers.push_back(&layer); - invokeDraw(settings, layers, mBuffer); + invokeDraw(settings, layers); uint64_t bufferId = layer.source.buffer.buffer->getId(); - EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); + EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId)); std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier = - sRE->unbindExternalTextureBufferForTesting(bufferId); + mGLESRE->unbindExternalTextureBufferForTesting(bufferId); std::lock_guard<std::mutex> lock(barrier->mutex); ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), [&]() REQUIRES(barrier->mutex) { return barrier->isOpen; })); - EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); + EXPECT_FALSE(mGLESRE->isImageCachedForTesting(bufferId)); EXPECT_EQ(NO_ERROR, barrier->result); } -TEST_F(RenderEngineTest, bindExternalBuffer_withNullBuffer) { - status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr); - ASSERT_EQ(BAD_VALUE, result); -} +TEST_P(RenderEngineTest, cacheExternalBuffer_withNullBuffer) { + const auto& renderEngineFactory = GetParam(); -TEST_F(RenderEngineTest, bindExternalBuffer_cachesImages) { - sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1); - uint32_t texName; - sRE->genTextures(1, &texName); - mTexNames.push_back(texName); + if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { + // GLES-specific test + return; + } - sRE->bindExternalTextureBuffer(texName, buf, nullptr); - uint64_t bufferId = buf->getId(); - EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); - std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier = - sRE->unbindExternalTextureBufferForTesting(bufferId); - std::lock_guard<std::mutex> lock(barrier->mutex); - ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), - [&]() REQUIRES(barrier->mutex) { - return barrier->isOpen; - })); - EXPECT_EQ(NO_ERROR, barrier->result); - EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); -} + initializeRenderEngine(); -TEST_F(RenderEngineTest, cacheExternalBuffer_withNullBuffer) { std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier = - sRE->cacheExternalTextureBufferForTesting(nullptr); + mGLESRE->cacheExternalTextureBufferForTesting(nullptr); std::lock_guard<std::mutex> lock(barrier->mutex); ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), [&]() REQUIRES(barrier->mutex) { @@ -1378,11 +1636,20 @@ TEST_F(RenderEngineTest, cacheExternalBuffer_withNullBuffer) { EXPECT_EQ(BAD_VALUE, barrier->result); } -TEST_F(RenderEngineTest, cacheExternalBuffer_cachesImages) { +TEST_P(RenderEngineTest, cacheExternalBuffer_cachesImages) { + const auto& renderEngineFactory = GetParam(); + + if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { + // GLES-specific test + return; + } + + initializeRenderEngine(); + sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1); uint64_t bufferId = buf->getId(); std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier = - sRE->cacheExternalTextureBufferForTesting(buf); + mGLESRE->cacheExternalTextureBufferForTesting(buf); { std::lock_guard<std::mutex> lock(barrier->mutex); ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), @@ -1391,8 +1658,8 @@ TEST_F(RenderEngineTest, cacheExternalBuffer_cachesImages) { })); EXPECT_EQ(NO_ERROR, barrier->result); } - EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); - barrier = sRE->unbindExternalTextureBufferForTesting(bufferId); + EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId)); + barrier = mGLESRE->unbindExternalTextureBufferForTesting(bufferId); { std::lock_guard<std::mutex> lock(barrier->mutex); ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), @@ -1401,10 +1668,27 @@ TEST_F(RenderEngineTest, cacheExternalBuffer_cachesImages) { })); EXPECT_EQ(NO_ERROR, barrier->result); } - EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); + EXPECT_FALSE(mGLESRE->isImageCachedForTesting(bufferId)); +} + +TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { + initializeRenderEngine(); + + const ubyte4 backgroundColor(255, 255, 255, 255); + const float shadowLength = 5.0f; + Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); + casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); + renderengine::ShadowSettings settings = + getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength, + false /* casterIsTranslucent */); + + drawShadowWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor); + expectShadowColorWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor); } -TEST_F(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { +TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { + initializeRenderEngine(); + const ubyte4 casterColor(255, 0, 0, 255); const ubyte4 backgroundColor(255, 255, 255, 255); const float shadowLength = 5.0f; @@ -1421,13 +1705,16 @@ TEST_F(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { expectShadowColor(castingLayer, settings, casterColor, backgroundColor); } -TEST_F(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { +TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { + initializeRenderEngine(); + const ubyte4 casterColor(255, 0, 0, 255); const ubyte4 backgroundColor(255, 255, 255, 255); const float shadowLength = 5.0f; Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); renderengine::LayerSettings castingLayer; + castingLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; castingLayer.geometry.boundaries = casterBounds.toFloatRect(); castingLayer.alpha = 1.0f; renderengine::ShadowSettings settings = @@ -1438,13 +1725,16 @@ TEST_F(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { expectShadowColor(castingLayer, settings, casterColor, backgroundColor); } -TEST_F(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { +TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { + initializeRenderEngine(); + const ubyte4 casterColor(255, 0, 0, 255); const ubyte4 backgroundColor(255, 255, 255, 255); const float shadowLength = 5.0f; Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); renderengine::LayerSettings castingLayer; + castingLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; castingLayer.geometry.boundaries = casterBounds.toFloatRect(); castingLayer.alpha = 1.0f; renderengine::ShadowSettings settings = @@ -1456,7 +1746,9 @@ TEST_F(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { expectShadowColor(castingLayer, settings, casterColor, backgroundColor); } -TEST_F(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { +TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { + initializeRenderEngine(); + const ubyte4 casterColor(255, 0, 0, 255); const ubyte4 backgroundColor(255, 255, 255, 255); const float shadowLength = 5.0f; @@ -1476,7 +1768,9 @@ TEST_F(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { expectShadowColor(castingLayer, settings, casterColor, backgroundColor); } -TEST_F(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) { +TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) { + initializeRenderEngine(); + const ubyte4 casterColor(255, 0, 0, 255); const ubyte4 backgroundColor(255, 255, 255, 255); const float shadowLength = 5.0f; @@ -1501,10 +1795,20 @@ TEST_F(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) { backgroundColor.a); } -TEST_F(RenderEngineTest, cleanupPostRender_cleansUpOnce) { +TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { + const auto& renderEngineFactory = GetParam(); + + if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { + // GLES-specific test + return; + } + + initializeRenderEngine(); + renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; std::vector<const renderengine::LayerSettings*> layers; renderengine::LayerSettings layer; @@ -1514,25 +1818,33 @@ TEST_F(RenderEngineTest, cleanupPostRender_cleansUpOnce) { layers.push_back(&layer); base::unique_fd fenceOne; - sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, base::unique_fd(), - &fenceOne); + mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne); base::unique_fd fenceTwo; - sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, std::move(fenceOne), - &fenceTwo); + mRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo); const int fd = fenceTwo.get(); if (fd >= 0) { sync_wait(fd, -1); } // Only cleanup the first time. - EXPECT_TRUE(sRE->cleanupPostRender( + EXPECT_TRUE(mRE->cleanupPostRender( renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES)); - EXPECT_FALSE(sRE->cleanupPostRender( + EXPECT_FALSE(mRE->cleanupPostRender( renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES)); } -TEST_F(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) { +TEST_P(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) { + const auto& renderEngineFactory = GetParam(); + + if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { + // GLES-specific test + return; + } + + initializeRenderEngine(); + renderengine::DisplaySettings settings; + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); @@ -1544,7 +1856,7 @@ TEST_F(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory layers.push_back(&layer); base::unique_fd fence; - sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, base::unique_fd(), &fence); + mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence); const int fd = fence.get(); if (fd >= 0) { @@ -1553,18 +1865,67 @@ TEST_F(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory uint64_t bufferId = layer.source.buffer.buffer->getId(); uint32_t texName = layer.source.buffer.textureName; - EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); - EXPECT_EQ(bufferId, sRE->getBufferIdForTextureNameForTesting(texName)); + EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId)); + EXPECT_EQ(bufferId, mGLESRE->getBufferIdForTextureNameForTesting(texName)); - EXPECT_TRUE(sRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL)); + EXPECT_TRUE(mRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL)); // Now check that our view of memory is good. - EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); - EXPECT_EQ(std::nullopt, sRE->getBufferIdForTextureNameForTesting(bufferId)); - EXPECT_TRUE(sRE->isTextureNameKnownForTesting(texName)); + EXPECT_FALSE(mGLESRE->isImageCachedForTesting(bufferId)); + EXPECT_EQ(std::nullopt, mGLESRE->getBufferIdForTextureNameForTesting(bufferId)); + EXPECT_TRUE(mGLESRE->isTextureNameKnownForTesting(texName)); +} + +TEST_P(RenderEngineTest, testRoundedCornersCrop) { + initializeRenderEngine(); + + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; + + std::vector<const renderengine::LayerSettings*> layers; + + renderengine::LayerSettings redLayer; + redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; + redLayer.geometry.boundaries = fullscreenRect().toFloatRect(); + redLayer.geometry.roundedCornersRadius = 5.0f; + redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect(); + // Red background. + redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f); + redLayer.alpha = 1.0f; + + layers.push_back(&redLayer); + + // Green layer with 1/3 size. + renderengine::LayerSettings greenLayer; + greenLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; + greenLayer.geometry.boundaries = fullscreenRect().toFloatRect(); + greenLayer.geometry.roundedCornersRadius = 5.0f; + // Bottom right corner is not going to be rounded. + greenLayer.geometry.roundedCornersCrop = + Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3, DEFAULT_DISPLAY_HEIGHT, + DEFAULT_DISPLAY_HEIGHT) + .toFloatRect(); + greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f); + greenLayer.alpha = 1.0f; + + layers.push_back(&greenLayer); + + invokeDraw(settings, layers); + + // Corners should be ignored... + // Screen size: width is 128, height is 256. + expectBufferColor(Rect(0, 0, 1, 1), 0, 0, 0, 0); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH - 1, 0, DEFAULT_DISPLAY_WIDTH, 1), 0, 0, 0, 0); + expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT - 1, 1, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0); + // Bottom right corner is kept out of the clipping, and it's green. + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1, + DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + 0, 255, 0, 255); } } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" +#pragma clang diagnostic pop // ignored "-Wconversion -Wextra" diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp new file mode 100644 index 0000000000..02ff06f393 --- /dev/null +++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp @@ -0,0 +1,165 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cutils/properties.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <renderengine/mock/RenderEngine.h> +#include "../threaded/RenderEngineThreaded.h" + +namespace android { + +using testing::_; +using testing::Eq; +using testing::Mock; +using testing::Return; + +struct RenderEngineThreadedTest : public ::testing::Test { + ~RenderEngineThreadedTest() {} + + void SetUp() override { + mThreadedRE = renderengine::threaded::RenderEngineThreaded::create( + [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); }); + } + + std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE; + renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); +}; + +TEST_F(RenderEngineThreadedTest, dump) { + std::string testString = "XYZ"; + EXPECT_CALL(*mRenderEngine, dump(_)); + mThreadedRE->dump(testString); +} + +TEST_F(RenderEngineThreadedTest, primeCache) { + EXPECT_CALL(*mRenderEngine, primeCache()); + mThreadedRE->primeCache(); +} + +TEST_F(RenderEngineThreadedTest, genTextures) { + uint32_t texName; + EXPECT_CALL(*mRenderEngine, genTextures(1, &texName)); + mThreadedRE->genTextures(1, &texName); +} + +TEST_F(RenderEngineThreadedTest, deleteTextures) { + uint32_t texName; + EXPECT_CALL(*mRenderEngine, deleteTextures(1, &texName)); + mThreadedRE->deleteTextures(1, &texName); +} + +TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns20) { + size_t size = 20; + EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size)); + size_t result = mThreadedRE->getMaxTextureSize(); + ASSERT_EQ(size, result); +} + +TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns0) { + size_t size = 0; + EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size)); + size_t result = mThreadedRE->getMaxTextureSize(); + ASSERT_EQ(size, result); +} + +TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns20) { + size_t dims = 20; + EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims)); + size_t result = mThreadedRE->getMaxViewportDims(); + ASSERT_EQ(dims, result); +} + +TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns0) { + size_t dims = 0; + EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims)); + size_t result = mThreadedRE->getMaxViewportDims(); + ASSERT_EQ(dims, result); +} + +TEST_F(RenderEngineThreadedTest, isProtected_returnsFalse) { + EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false)); + status_t result = mThreadedRE->isProtected(); + ASSERT_EQ(false, result); +} + +TEST_F(RenderEngineThreadedTest, isProtected_returnsTrue) { + EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(true)); + size_t result = mThreadedRE->isProtected(); + ASSERT_EQ(true, result); +} + +TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsFalse) { + EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(false)); + status_t result = mThreadedRE->supportsProtectedContent(); + ASSERT_EQ(false, result); +} + +TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsTrue) { + EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(true)); + status_t result = mThreadedRE->supportsProtectedContent(); + ASSERT_EQ(true, result); +} + +TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsFalse) { + EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(false)); + status_t result = mThreadedRE->useProtectedContext(false); + ASSERT_EQ(false, result); +} + +TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsTrue) { + EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(true)); + status_t result = mThreadedRE->useProtectedContext(false); + ASSERT_EQ(true, result); +} + +TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) { + EXPECT_CALL(*mRenderEngine, + cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL)) + .WillOnce(Return(false)); + status_t result = + mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL); + ASSERT_EQ(false, result); +} + +TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) { + EXPECT_CALL(*mRenderEngine, + cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL)) + .WillOnce(Return(true)); + status_t result = + mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL); + ASSERT_EQ(true, result); +} + +TEST_F(RenderEngineThreadedTest, drawLayers) { + renderengine::DisplaySettings settings; + std::vector<const renderengine::LayerSettings*> layers; + sp<GraphicBuffer> buffer = new GraphicBuffer(); + base::unique_fd bufferFence; + base::unique_fd drawFence; + + EXPECT_CALL(*mRenderEngine, drawLayers) + .WillOnce([](const renderengine::DisplaySettings&, + const std::vector<const renderengine::LayerSettings*>&, + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, + base::unique_fd*) -> status_t { return NO_ERROR; }); + + status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false, + std::move(bufferFence), &drawFence); + ASSERT_EQ(NO_ERROR, result); +} + +} // namespace android diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp new file mode 100644 index 0000000000..3b97f5659e --- /dev/null +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -0,0 +1,320 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "RenderEngineThreaded.h" + +#include <sched.h> +#include <chrono> +#include <future> + +#include <android-base/stringprintf.h> +#include <private/gui/SyncFeatures.h> +#include <utils/Trace.h> + +#include "gl/GLESRenderEngine.h" + +using namespace std::chrono_literals; + +namespace android { +namespace renderengine { +namespace threaded { + +std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) { + return std::make_unique<RenderEngineThreaded>(std::move(factory)); +} + +RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory) { + ATRACE_CALL(); + + std::lock_guard lockThread(mThreadMutex); + mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory); +} + +RenderEngineThreaded::~RenderEngineThreaded() { + { + std::lock_guard lock(mThreadMutex); + mRunning = false; + mCondition.notify_one(); + } + + if (mThread.joinable()) { + mThread.join(); + } +} + +// NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations. +void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS { + ATRACE_CALL(); + + struct sched_param param = {0}; + param.sched_priority = 2; + if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO"); + } + + mRenderEngine = factory(); + + std::unique_lock<std::mutex> lock(mThreadMutex); + pthread_setname_np(pthread_self(), mThreadName); + + while (mRunning) { + if (!mFunctionCalls.empty()) { + auto task = mFunctionCalls.front(); + mFunctionCalls.pop(); + task(*mRenderEngine); + } + mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) { + return !mRunning || !mFunctionCalls.empty(); + }); + } +} + +void RenderEngineThreaded::primeCache() const { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::primeCache"); + instance.primeCache(); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +void RenderEngineThreaded::dump(std::string& result) { + std::promise<std::string> resultPromise; + std::future<std::string> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::dump"); + std::string localResult = result; + instance.dump(localResult); + resultPromise.set_value(std::move(localResult)); + }); + } + mCondition.notify_one(); + // Note: This is an rvalue. + result.assign(resultFuture.get()); +} + +void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::genTextures"); + instance.genTextures(count, names); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::deleteTextures"); + instance.deleteTextures(count, names); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +void RenderEngineThreaded::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { + // This function is designed so it can run asynchronously, so we do not need to wait + // for the futures. + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([=](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::cacheExternalTextureBuffer"); + instance.cacheExternalTextureBuffer(buffer); + }); + } + mCondition.notify_one(); +} + +void RenderEngineThreaded::unbindExternalTextureBuffer(uint64_t bufferId) { + // This function is designed so it can run asynchronously, so we do not need to wait + // for the futures. + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([=](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::unbindExternalTextureBuffer"); + instance.unbindExternalTextureBuffer(bufferId); + }); + } + mCondition.notify_one(); +} + +size_t RenderEngineThreaded::getMaxTextureSize() const { + std::promise<size_t> resultPromise; + std::future<size_t> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::getMaxTextureSize"); + size_t size = instance.getMaxTextureSize(); + resultPromise.set_value(size); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +size_t RenderEngineThreaded::getMaxViewportDims() const { + std::promise<size_t> resultPromise; + std::future<size_t> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::getMaxViewportDims"); + size_t size = instance.getMaxViewportDims(); + resultPromise.set_value(size); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +bool RenderEngineThreaded::isProtected() const { + std::promise<bool> resultPromise; + std::future<bool> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::isProtected"); + bool returnValue = instance.isProtected(); + resultPromise.set_value(returnValue); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +bool RenderEngineThreaded::supportsProtectedContent() const { + std::promise<bool> resultPromise; + std::future<bool> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::supportsProtectedContent"); + bool returnValue = instance.supportsProtectedContent(); + resultPromise.set_value(returnValue); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +bool RenderEngineThreaded::useProtectedContext(bool useProtectedContext) { + std::promise<bool> resultPromise; + std::future<bool> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push( + [&resultPromise, useProtectedContext](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::useProtectedContext"); + bool returnValue = instance.useProtectedContext(useProtectedContext); + resultPromise.set_value(returnValue); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +bool RenderEngineThreaded::cleanupPostRender(CleanupMode mode) { + std::promise<bool> resultPromise; + std::future<bool> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, mode](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::cleanupPostRender"); + bool returnValue = instance.cleanupPostRender(mode); + resultPromise.set_value(returnValue); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display, + const std::vector<const LayerSettings*>& layers, + const sp<GraphicBuffer>& buffer, + const bool useFramebufferCache, + base::unique_fd&& bufferFence, + base::unique_fd* drawFence) { + std::promise<status_t> resultPromise; + std::future<status_t> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache, + &bufferFence, &drawFence](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::drawLayers"); + status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache, + std::move(bufferFence), drawFence); + resultPromise.set_value(status); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +void RenderEngineThreaded::cleanFramebufferCache() { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::cleanFramebufferCache"); + instance.cleanFramebufferCache(); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +int RenderEngineThreaded::getContextPriority() { + std::promise<int> resultPromise; + std::future<int> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::getContextPriority"); + int priority = instance.getContextPriority(); + resultPromise.set_value(priority); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +} // namespace threaded +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h new file mode 100644 index 0000000000..8b1e2dec5a --- /dev/null +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -0,0 +1,90 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/thread_annotations.h> +#include <condition_variable> +#include <mutex> +#include <queue> +#include <thread> + +#include "renderengine/RenderEngine.h" + +namespace android { +namespace renderengine { +namespace threaded { + +using CreateInstanceFactory = std::function<std::unique_ptr<renderengine::RenderEngine>()>; + +/** + * This class extends a basic RenderEngine class. It contains a thread. Each time a function of + * this class is called, we create a lambda function that is put on a queue. The main thread then + * executes the functions in order. + */ +class RenderEngineThreaded : public RenderEngine { +public: + static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory); + + RenderEngineThreaded(CreateInstanceFactory factory); + ~RenderEngineThreaded() override; + void primeCache() const override; + + void dump(std::string& result) override; + + void genTextures(size_t count, uint32_t* names) override; + void deleteTextures(size_t count, uint32_t const* names) override; + void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override; + void unbindExternalTextureBuffer(uint64_t bufferId) override; + size_t getMaxTextureSize() const override; + size_t getMaxViewportDims() const override; + + bool isProtected() const override; + bool supportsProtectedContent() const override; + bool useProtectedContext(bool useProtectedContext) override; + bool cleanupPostRender(CleanupMode mode) override; + + status_t drawLayers(const DisplaySettings& display, + const std::vector<const LayerSettings*>& layers, + const sp<GraphicBuffer>& buffer, const bool useFramebufferCache, + base::unique_fd&& bufferFence, base::unique_fd* drawFence) override; + + void cleanFramebufferCache() override; + int getContextPriority() override; + +private: + void threadMain(CreateInstanceFactory factory); + + /* ------------------------------------------------------------------------ + * Threading + */ + const char* const mThreadName = "RenderEngineThread"; + // Protects the creation and destruction of mThread. + mutable std::mutex mThreadMutex; + std::thread mThread GUARDED_BY(mThreadMutex); + bool mRunning GUARDED_BY(mThreadMutex) = true; + mutable std::queue<std::function<void(renderengine::RenderEngine& instance)>> mFunctionCalls + GUARDED_BY(mThreadMutex); + mutable std::condition_variable mCondition; + + /* ------------------------------------------------------------------------ + * Render Engine + */ + std::unique_ptr<renderengine::RenderEngine> mRenderEngine; +}; +} // namespace threaded +} // namespace renderengine +} // namespace android diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp index 497c33c386..e8154a6931 100644 --- a/libs/sensor/Android.bp +++ b/libs/sensor/Android.bp @@ -12,15 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library_shared { name: "libsensor", diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp index 8ed09f8ff0..a6b0aaf0b5 100644 --- a/libs/sensor/ISensorServer.cpp +++ b/libs/sensor/ISensorServer.cpp @@ -216,14 +216,25 @@ status_t BnSensorServer::onTransact( int32_t type; Vector<float> floats; Vector<int32_t> ints; + uint32_t count; handle = data.readInt32(); type = data.readInt32(); - floats.resize(data.readUint32()); + + count = data.readUint32(); + if (count > (data.dataAvail() / sizeof(float))) { + return BAD_VALUE; + } + floats.resize(count); for (auto &i : floats) { i = data.readFloat(); } - ints.resize(data.readUint32()); + + count = data.readUint32(); + if (count > (data.dataAvail() / sizeof(int32_t))) { + return BAD_VALUE; + } + ints.resize(count); for (auto &i : ints) { i = data.readInt32(); } diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp index 8fdb003a5d..c9a7668563 100644 --- a/libs/sensor/tests/Android.bp +++ b/libs/sensor/tests/Android.bp @@ -12,15 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_test { name: "libsensor_test", diff --git a/libs/sensorprivacy/Android.bp b/libs/sensorprivacy/Android.bp index 00514c4417..4a606ffec2 100644 --- a/libs/sensorprivacy/Android.bp +++ b/libs/sensorprivacy/Android.bp @@ -12,15 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library_shared { name: "libsensorprivacy", diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp index f973cbad17..47144696f2 100644 --- a/libs/sensorprivacy/SensorPrivacyManager.cpp +++ b/libs/sensorprivacy/SensorPrivacyManager.cpp @@ -64,6 +64,17 @@ void SensorPrivacyManager::addSensorPrivacyListener( } } +status_t SensorPrivacyManager::addIndividualSensorPrivacyListener(int userId, int sensor, + const sp<hardware::ISensorPrivacyListener>& listener) +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + return service->addIndividualSensorPrivacyListener(userId, sensor, listener) + .transactionError(); + } + return UNEXPECTED_NULL; +} + void SensorPrivacyManager::removeSensorPrivacyListener( const sp<hardware::ISensorPrivacyListener>& listener) { @@ -85,6 +96,31 @@ bool SensorPrivacyManager::isSensorPrivacyEnabled() return false; } +bool SensorPrivacyManager::isIndividualSensorPrivacyEnabled(int userId, int sensor) +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + bool result; + service->isIndividualSensorPrivacyEnabled(userId, sensor, &result); + return result; + } + // if the SensorPrivacyManager is not available then assume sensor privacy is disabled + return false; +} + +status_t SensorPrivacyManager::isIndividualSensorPrivacyEnabled(int userId, int sensor, + bool &returnVal) +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + binder::Status res = service->isIndividualSensorPrivacyEnabled(userId, sensor, &returnVal); + return res.transactionError(); + } + // if the SensorPrivacyManager is not available then assume sensor privacy is disabled + returnVal = false; + return UNKNOWN_ERROR; +} + status_t SensorPrivacyManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) { sp<hardware::ISensorPrivacyManager> service = getService(); diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl index 4c2d5dbb8f..629b8c2093 100644 --- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl +++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl @@ -22,9 +22,17 @@ import android.hardware.ISensorPrivacyListener; interface ISensorPrivacyManager { void addSensorPrivacyListener(in ISensorPrivacyListener listener); + void addIndividualSensorPrivacyListener(int userId, int sensor, in ISensorPrivacyListener listener); + void removeSensorPrivacyListener(in ISensorPrivacyListener listener); boolean isSensorPrivacyEnabled(); + boolean isIndividualSensorPrivacyEnabled(int userId, int sensor); + void setSensorPrivacy(boolean enable); + + void setIndividualSensorPrivacy(int userId, int sensor, boolean enable); + + void setIndividualSensorPrivacyForProfileGroup(int userId, int sensor, boolean enable); } diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h index 2546a681b9..12778e16b6 100644 --- a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h +++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h @@ -28,11 +28,20 @@ namespace android { class SensorPrivacyManager { public: + enum { + INDIVIDUAL_SENSOR_MICROPHONE = 1, + INDIVIDUAL_SENSOR_CAMERA = 2 + }; + SensorPrivacyManager(); void addSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener); + status_t addIndividualSensorPrivacyListener(int userId, int sensor, + const sp<hardware::ISensorPrivacyListener>& listener); void removeSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener); bool isSensorPrivacyEnabled(); + bool isIndividualSensorPrivacyEnabled(int userId, int sensor); + status_t isIndividualSensorPrivacyEnabled(int userId, int sensor, bool &result); status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient); status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient); diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 07760ab765..714ee3e909 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -12,21 +12,72 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_applicable_licenses: ["frameworks_native_libs_ui_license"], +cc_defaults { + name: "libui-defaults", + clang: true, + cflags: [ + "-Wall", + "-Werror", + ], + cppflags: [ + "-Wextra", + ], + + sanitize: { + integer_overflow: true, + misc_undefined: ["bounds"], + }, + } -// Added automatically by a large-scale-change -// See: http://go/android-license-faq -license { - name: "frameworks_native_libs_ui_license", - visibility: [":__subpackages__"], - license_kinds: [ - "SPDX-license-identifier-Apache-2.0", +cc_library_static { + name: "libui-types", + vendor_available: true, + host_supported: true, + target: { + windows: { + enabled: true, + } + }, + + defaults: [ + "libui-defaults", ], - license_text: [ - "NOTICE", + + apex_available: [ + "//apex_available:anyapex", + "//apex_available:platform", + ], + min_sdk_version: "apex_inherit", + + shared_libs: [ + "libbase", + "libutils", ], + + static_libs: [ + "libarect", + "libmath", + ], + + srcs: [ + "ColorSpace.cpp", + "Rect.cpp", + "Region.cpp", + "Transform.cpp", + ], + + export_include_dirs: [ + "include", + "include_private", + "include_types", + ], + + export_static_lib_headers: [ + "libarect", + "libmath", + ], + } cc_library_shared { @@ -52,8 +103,9 @@ cc_library_shared { }, srcs: [ - "ColorSpace.cpp", "DebugUtils.cpp", + "DeviceProductInfo.cpp", + "DisplayInfo.cpp", "Fence.cpp", "FenceTime.cpp", "FrameStats.cpp", @@ -67,11 +119,7 @@ cc_library_shared { "HdrCapabilities.cpp", "PixelFormat.cpp", "PublicFormat.cpp", - "Rect.cpp", - "Region.cpp", "Size.cpp", - "Transform.cpp", - "UiConfig.cpp", ], include_dirs: [ @@ -82,8 +130,11 @@ cc_library_shared { "include_private", ], - // Uncomment the following line to enable VALIDATE_REGIONS traces - //defaults: ["libui-validate-regions-defaults"], + defaults: [ + "libui-defaults", + // Uncomment the following line to enable VALIDATE_REGIONS traces + //defaults: ["libui-validate-regions-defaults"], + ], shared_libs: [ "android.hardware.graphics.allocator@2.0", @@ -117,6 +168,10 @@ cc_library_shared { "libmath", ], + whole_static_libs: [ + "libui-types", + ], + // bufferhub is not used when building libgui for vendors target: { vendor: { @@ -188,6 +243,8 @@ filegroup { name: "libui_host_common", srcs: [ "Rect.cpp", - "PixelFormat.cpp" + "Region.cpp", + "PixelFormat.cpp", + "Transform.cpp" ], } diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp index f394635aa2..1f006ceb69 100644 --- a/libs/ui/DebugUtils.cpp +++ b/libs/ui/DebugUtils.cpp @@ -321,10 +321,6 @@ std::string decodeRenderIntent(RenderIntent renderIntent) { return std::string("Unknown RenderIntent"); } -std::string to_string(const android::Rect& rect) { - return StringPrintf("(%4d,%4d,%4d,%4d)", rect.left, rect.top, rect.right, rect.bottom); -} - std::string toString(const android::DeviceProductInfo::ManufactureOrModelDate& date) { using ModelYear = android::DeviceProductInfo::ModelYear; using ManufactureYear = android::DeviceProductInfo::ManufactureYear; diff --git a/libs/ui/DeviceProductInfo.cpp b/libs/ui/DeviceProductInfo.cpp new file mode 100644 index 0000000000..4d6ce4306a --- /dev/null +++ b/libs/ui/DeviceProductInfo.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ui/DeviceProductInfo.h> + +#include <android-base/stringprintf.h> +#include <ui/FlattenableHelpers.h> +#include <utils/Log.h> + +#define RETURN_IF_ERROR(op) \ + if (const status_t status = (op); status != OK) return status; + +namespace android { + +using base::StringAppendF; + +size_t DeviceProductInfo::getFlattenedSize() const { + return FlattenableHelpers::getFlattenedSize(name) + + FlattenableHelpers::getFlattenedSize(manufacturerPnpId) + + FlattenableHelpers::getFlattenedSize(productId) + + FlattenableHelpers::getFlattenedSize(manufactureOrModelDate) + + FlattenableHelpers::getFlattenedSize(relativeAddress); +} + +status_t DeviceProductInfo::flatten(void* buffer, size_t size) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, name)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufacturerPnpId)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, productId)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufactureOrModelDate)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, relativeAddress)); + return OK; +} + +status_t DeviceProductInfo::unflatten(void const* buffer, size_t size) { + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &name)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufacturerPnpId)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &productId)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufactureOrModelDate)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &relativeAddress)); + return OK; +} + +void DeviceProductInfo::dump(std::string& result) const { + StringAppendF(&result, "{name=%s, ", name.c_str()); + StringAppendF(&result, "manufacturerPnpId=%s, ", manufacturerPnpId.data()); + StringAppendF(&result, "productId=%s, ", productId.c_str()); + + if (const auto* model = std::get_if<ModelYear>(&manufactureOrModelDate)) { + StringAppendF(&result, "modelYear=%u, ", model->year); + } else if (const auto* manufactureWeekAndYear = + std::get_if<ManufactureWeekAndYear>(&manufactureOrModelDate)) { + StringAppendF(&result, "manufactureWeek=%u, ", manufactureWeekAndYear->week); + StringAppendF(&result, "manufactureYear=%d, ", manufactureWeekAndYear->year); + } else if (const auto* manufactureYear = + std::get_if<ManufactureYear>(&manufactureOrModelDate)) { + StringAppendF(&result, "manufactureYear=%d, ", manufactureYear->year); + } else { + ALOGE("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate"); + } + + result.append("relativeAddress=["); + for (size_t i = 0; i < relativeAddress.size(); i++) { + if (i != 0) { + result.append(", "); + } + StringAppendF(&result, "%u", relativeAddress[i]); + } + result.append("]}"); +} + +} // namespace android diff --git a/libs/ui/DisplayInfo.cpp b/libs/ui/DisplayInfo.cpp new file mode 100644 index 0000000000..73a78af186 --- /dev/null +++ b/libs/ui/DisplayInfo.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ui/DisplayInfo.h> + +#include <cstdint> + +#include <ui/FlattenableHelpers.h> + +#define RETURN_IF_ERROR(op) \ + if (const status_t status = (op); status != OK) return status; + +namespace android { + +size_t DisplayInfo::getFlattenedSize() const { + return FlattenableHelpers::getFlattenedSize(connectionType) + + FlattenableHelpers::getFlattenedSize(density) + + FlattenableHelpers::getFlattenedSize(secure) + + FlattenableHelpers::getFlattenedSize(deviceProductInfo); +} + +status_t DisplayInfo::flatten(void* buffer, size_t size) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, connectionType)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, density)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, secure)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, deviceProductInfo)); + return OK; +} + +status_t DisplayInfo::unflatten(void const* buffer, size_t size) { + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &connectionType)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &density)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &secure)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &deviceProductInfo)); + return OK; +} + +} // namespace android diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp index f799ce4cb0..636fbde8d7 100644 --- a/libs/ui/Gralloc4.cpp +++ b/libs/ui/Gralloc4.cpp @@ -1052,7 +1052,7 @@ std::string Gralloc4Mapper::dumpBuffers(bool less) const { Gralloc4Allocator::Gralloc4Allocator(const Gralloc4Mapper& mapper) : mMapper(mapper) { mAllocator = IAllocator::getService(); if (mAllocator == nullptr) { - ALOGW("allocator 3.x is not supported"); + ALOGW("allocator 4.x is not supported"); return; } } diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index 943d13ec68..91d2d58df7 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -83,20 +83,17 @@ void GraphicBufferAllocator::dump(std::string& result, bool less) const { KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); uint64_t total = 0; result.append("GraphicBufferAllocator buffers:\n"); - const size_t c = list.size(); - for (size_t i=0 ; i<c ; i++) { + const size_t count = list.size(); + StringAppendF(&result, "%10s | %11s | %18s | %s | %8s | %10s | %s\n", "Handle", "Size", + "W (Stride) x H", "Layers", "Format", "Usage", "Requestor"); + for (size_t i = 0; i < count; i++) { const alloc_rec_t& rec(list.valueAt(i)); - if (rec.size) { - StringAppendF(&result, - "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n", - list.keyAt(i), static_cast<double>(rec.size) / 1024.0, rec.width, rec.stride, rec.height, - rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str()); - } else { - StringAppendF(&result, - "%10p: unknown | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n", - list.keyAt(i), rec.width, rec.stride, rec.height, rec.layerCount, - rec.format, rec.usage, rec.requestorName.c_str()); - } + std::string sizeStr = (rec.size) + ? base::StringPrintf("%7.2f KiB", static_cast<double>(rec.size) / 1024.0) + : "unknown"; + StringAppendF(&result, "%10p | %11s | %4u (%4u) x %4u | %6u | %8X | 0x%8" PRIx64 " | %s\n", + list.keyAt(i), sizeStr.c_str(), rec.width, rec.stride, rec.height, + rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str()); total += rec.size; } StringAppendF(&result, "Total allocated by GraphicBufferAllocator (estimate): %.2f KB\n", diff --git a/libs/ui/PublicFormat.cpp b/libs/ui/PublicFormat.cpp index 70e3ce768c..78e82dab39 100644 --- a/libs/ui/PublicFormat.cpp +++ b/libs/ui/PublicFormat.cpp @@ -35,6 +35,8 @@ int mapPublicFormatToHalFormat(PublicFormat f) { case PublicFormat::RAW_SENSOR: case PublicFormat::RAW_DEPTH: return HAL_PIXEL_FORMAT_RAW16; + case PublicFormat::RAW_DEPTH10: + return HAL_PIXEL_FORMAT_RAW10; default: // Most formats map 1:1 return static_cast<int>(f); @@ -50,6 +52,7 @@ android_dataspace mapPublicFormatToHalDataspace(PublicFormat f) { case PublicFormat::DEPTH_POINT_CLOUD: case PublicFormat::DEPTH16: case PublicFormat::RAW_DEPTH: + case PublicFormat::RAW_DEPTH10: dataspace = Dataspace::DEPTH; break; case PublicFormat::RAW_SENSOR: @@ -80,6 +83,13 @@ android_dataspace mapPublicFormatToHalDataspace(PublicFormat f) { PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace) { Dataspace ds = static_cast<Dataspace>(dataSpace); switch (format) { + case HAL_PIXEL_FORMAT_RAW10: + switch (ds) { + case Dataspace::DEPTH: + return PublicFormat::RAW_DEPTH10; + default: + return PublicFormat::RAW10; + } case HAL_PIXEL_FORMAT_RGBA_8888: case HAL_PIXEL_FORMAT_RGBX_8888: case HAL_PIXEL_FORMAT_RGBA_FP16: @@ -87,10 +97,10 @@ PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace d case HAL_PIXEL_FORMAT_RGB_888: case HAL_PIXEL_FORMAT_RGB_565: case HAL_PIXEL_FORMAT_Y8: - case HAL_PIXEL_FORMAT_RAW10: case HAL_PIXEL_FORMAT_RAW12: case HAL_PIXEL_FORMAT_YCbCr_420_888: case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_YCBCR_P010: // Enums overlap in both name and value return static_cast<PublicFormat>(format); case HAL_PIXEL_FORMAT_RAW16: diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp index 13fed3a239..a8d6285169 100644 --- a/libs/ui/Rect.cpp +++ b/libs/ui/Rect.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <android-base/stringprintf.h> #include <system/graphics.h> #include <ui/Rect.h> @@ -149,4 +150,13 @@ Rect Rect::reduce(const Rect& exclude) const { return result; } +std::string to_string(const android::Rect& rect) { + return android::base::StringPrintf("Rect(%d, %d, %d, %d)", rect.left, rect.top, rect.right, + rect.bottom); +} + +void PrintTo(const Rect& rect, ::std::ostream* os) { + *os << to_string(rect); +} + }; // namespace android diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp index 06b6bfe797..cd68c1c0ec 100644 --- a/libs/ui/Transform.cpp +++ b/libs/ui/Transform.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#undef LOG_TAG +#define LOG_TAG "Transform" + #include <math.h> #include <android-base/stringprintf.h> @@ -22,8 +25,7 @@ #include <ui/Transform.h> #include <utils/String8.h> -namespace android { -namespace ui { +namespace android::ui { Transform::Transform() { reset(); @@ -55,11 +57,9 @@ bool Transform::operator==(const Transform& other) const { mMatrix[1][1] == other.mMatrix[1][1] && mMatrix[1][2] == other.mMatrix[1][2] && mMatrix[2][0] == other.mMatrix[2][0] && mMatrix[2][1] == other.mMatrix[2][1] && mMatrix[2][2] == other.mMatrix[2][2]; - ; } -Transform Transform::operator * (const Transform& rhs) const -{ +Transform Transform::operator*(const Transform& rhs) const { if (CC_LIKELY(mType == IDENTITY)) return rhs; @@ -87,6 +87,19 @@ Transform Transform::operator * (const Transform& rhs) const return r; } +Transform Transform::operator * (float value) const { + Transform r(*this); + const mat33& M(mMatrix); + mat33& R(r.mMatrix); + for (size_t i = 0; i < 3; i++) { + for (size_t j = 0; j < 2; j++) { + R[i][j] = M[i][j] * value; + } + } + r.type(); + return r; +} + Transform& Transform::operator=(const Transform& other) { mMatrix = other.mMatrix; mType = other.mType; @@ -105,14 +118,30 @@ float Transform::ty() const { return mMatrix[2][1]; } -float Transform::sx() const { +float Transform::dsdx() const { return mMatrix[0][0]; } -float Transform::sy() const { +float Transform::dtdx() const { + return mMatrix[1][0]; +} + +float Transform::dtdy() const { + return mMatrix[0][1]; +} + +float Transform::dsdy() const { return mMatrix[1][1]; } +float Transform::getScaleX() const { + return sqrt((dsdx() * dsdx()) + (dtdx() * dtdx())); +} + +float Transform::getScaleY() const { + return sqrt((dtdy() * dtdy()) + (dsdy() * dsdy())); +} + void Transform::reset() { mType = IDENTITY; for(size_t i = 0; i < 3; i++) { @@ -122,8 +151,7 @@ void Transform::reset() { } } -void Transform::set(float tx, float ty) -{ +void Transform::set(float tx, float ty) { mMatrix[2][0] = tx; mMatrix[2][1] = ty; mMatrix[2][2] = 1.0f; @@ -135,8 +163,7 @@ void Transform::set(float tx, float ty) } } -void Transform::set(float a, float b, float c, float d) -{ +void Transform::set(float a, float b, float c, float d) { mat33& M(mMatrix); M[0][0] = a; M[1][0] = b; M[0][1] = c; M[1][1] = d; @@ -144,8 +171,7 @@ void Transform::set(float a, float b, float c, float d) mType = UNKNOWN_TYPE; } -status_t Transform::set(uint32_t flags, float w, float h) -{ +status_t Transform::set(uint32_t flags, float w, float h) { if (flags & ROT_INVALID) { // that's not allowed! reset(); @@ -187,6 +213,15 @@ status_t Transform::set(uint32_t flags, float w, float h) return NO_ERROR; } +void Transform::set(const std::array<float, 9>& matrix) { + mat33& M(mMatrix); + M[0][0] = matrix[0]; M[1][0] = matrix[1]; M[2][0] = matrix[2]; + M[0][1] = matrix[3]; M[1][1] = matrix[4]; M[2][1] = matrix[5]; + M[0][2] = matrix[6]; M[1][2] = matrix[7]; M[2][2] = matrix[8]; + mType = UNKNOWN_TYPE; + type(); +} + vec2 Transform::transform(const vec2& v) const { vec2 r; const mat33& M(mMatrix); @@ -204,18 +239,15 @@ vec3 Transform::transform(const vec3& v) const { return r; } -vec2 Transform::transform(int x, int y) const -{ - return transform(vec2(x,y)); +vec2 Transform::transform(float x, float y) const { + return transform(vec2(x, y)); } -Rect Transform::makeBounds(int w, int h) const -{ +Rect Transform::makeBounds(int w, int h) const { return transform( Rect(w, h) ); } -Rect Transform::transform(const Rect& bounds, bool roundOutwards) const -{ +Rect Transform::transform(const Rect& bounds, bool roundOutwards) const { Rect r; vec2 lt( bounds.left, bounds.top ); vec2 rt( bounds.right, bounds.top ); @@ -242,8 +274,7 @@ Rect Transform::transform(const Rect& bounds, bool roundOutwards) const return r; } -FloatRect Transform::transform(const FloatRect& bounds) const -{ +FloatRect Transform::transform(const FloatRect& bounds) const { vec2 lt(bounds.left, bounds.top); vec2 rt(bounds.right, bounds.top); vec2 lb(bounds.left, bounds.bottom); @@ -263,8 +294,7 @@ FloatRect Transform::transform(const FloatRect& bounds) const return r; } -Region Transform::transform(const Region& reg) const -{ +Region Transform::transform(const Region& reg) const { Region out; if (CC_UNLIKELY(type() > TRANSLATE)) { if (CC_LIKELY(preserveRects())) { @@ -284,8 +314,7 @@ Region Transform::transform(const Region& reg) const return out; } -uint32_t Transform::type() const -{ +uint32_t Transform::type() const { if (mType & UNKNOWN_TYPE) { // recompute what this transform is @@ -380,16 +409,18 @@ uint32_t Transform::getType() const { return type() & 0xFF; } -uint32_t Transform::getOrientation() const -{ +uint32_t Transform::getOrientation() const { return (type() >> 8) & 0xFF; } -bool Transform::preserveRects() const -{ +bool Transform::preserveRects() const { return (getOrientation() & ROT_INVALID) ? false : true; } +bool Transform::needsBilinearFiltering() const { + return (!preserveRects() || getType() >= ui::Transform::SCALE); +} + mat4 Transform::asMatrix4() const { // Internally Transform uses a 3x3 matrix since the transform is meant for // two-dimensional values. An equivalent 4x4 matrix means inserting an extra @@ -421,7 +452,43 @@ mat4 Transform::asMatrix4() const { return m; } -void Transform::dump(std::string& out, const char* name) const { +static std::string rotationToString(const uint32_t rotationFlags) { + switch (rotationFlags) { + case Transform::ROT_0: + return "ROT_0"; + case Transform::FLIP_H: + return "FLIP_H"; + case Transform::FLIP_V: + return "FLIP_V"; + case Transform::ROT_90: + return "ROT_90"; + case Transform::ROT_180: + return "ROT_180"; + case Transform::ROT_270: + return "ROT_270"; + case Transform::ROT_INVALID: + default: + return "ROT_INVALID"; + } +} + +static std::string transformToString(const uint32_t transform) { + if (transform == Transform::IDENTITY) { + return "IDENTITY"; + } + + if (transform == Transform::UNKNOWN) { + return "UNKNOWN"; + } + + std::string out; + if (transform & Transform::SCALE) out.append("SCALE "); + if (transform & Transform::ROTATE) out.append("ROTATE "); + if (transform & Transform::TRANSLATE) out.append("TRANSLATE"); + return out; +} + +void Transform::dump(std::string& out, const char* name, const char* prefix) const { using android::base::StringAppendF; type(); // Ensure the information in mType is up to date @@ -429,40 +496,34 @@ void Transform::dump(std::string& out, const char* name) const { const uint32_t type = mType; const uint32_t orient = type >> 8; - StringAppendF(&out, "%s 0x%08x (", name, orient); + out += prefix; + out += name; + out += " "; if (orient & ROT_INVALID) { - out.append("ROT_INVALID "); - } else { - if (orient & ROT_90) { - out.append("ROT_90 "); - } else { - out.append("ROT_0 "); - } - if (orient & FLIP_V) out.append("FLIP_V "); - if (orient & FLIP_H) out.append("FLIP_H "); + StringAppendF(&out, "0x%08x ", orient); } + out += "(" + rotationToString(orient) + ") "; - StringAppendF(&out, ") 0x%02x (", type); - - if (!(type & (SCALE | ROTATE | TRANSLATE))) out.append("IDENTITY "); - if (type & SCALE) out.append("SCALE "); - if (type & ROTATE) out.append("ROTATE "); - if (type & TRANSLATE) out.append("TRANSLATE "); + if (type & UNKNOWN) { + StringAppendF(&out, "0x%02x ", type); + } + out += "(" + transformToString(type) + ")\n"; - out.append(")\n"); + if (type == IDENTITY) { + return; + } for (size_t i = 0; i < 3; i++) { - StringAppendF(&out, " %.4f %.4f %.4f\n", static_cast<double>(mMatrix[0][i]), + StringAppendF(&out, "%s %.4f %.4f %.4f\n", prefix, static_cast<double>(mMatrix[0][i]), static_cast<double>(mMatrix[1][i]), static_cast<double>(mMatrix[2][i])); } } -void Transform::dump(const char* name) const { +void Transform::dump(const char* name, const char* prefix) const { std::string out; - dump(out, name); + dump(out, name, prefix); ALOGD("%s", out.c_str()); } -} // namespace ui -} // namespace android +} // namespace android::ui diff --git a/libs/ui/include/ui/BlurRegion.h b/libs/ui/include/ui/BlurRegion.h new file mode 100644 index 0000000000..69a586ecbb --- /dev/null +++ b/libs/ui/include/ui/BlurRegion.h @@ -0,0 +1,53 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <inttypes.h> +#include <iosfwd> +#include <iostream> + +namespace android { + +struct BlurRegion { + uint32_t blurRadius; + float cornerRadiusTL; + float cornerRadiusTR; + float cornerRadiusBL; + float cornerRadiusBR; + float alpha; + int left; + int top; + int right; + int bottom; +}; + +static inline void PrintTo(const BlurRegion& blurRegion, ::std::ostream* os) { + *os << "BlurRegion {"; + *os << "\n .blurRadius = " << blurRegion.blurRadius; + *os << "\n .cornerRadiusTL = " << blurRegion.cornerRadiusTL; + *os << "\n .cornerRadiusTR = " << blurRegion.cornerRadiusTR; + *os << "\n .cornerRadiusBL = " << blurRegion.cornerRadiusBL; + *os << "\n .cornerRadiusBR = " << blurRegion.cornerRadiusBR; + *os << "\n .alpha = " << blurRegion.alpha; + *os << "\n .left = " << blurRegion.left; + *os << "\n .top = " << blurRegion.top; + *os << "\n .right = " << blurRegion.right; + *os << "\n .bottom = " << blurRegion.bottom; + *os << "\n}"; +} + +} // namespace android
\ No newline at end of file diff --git a/libs/ui/include/ui/DebugUtils.h b/libs/ui/include/ui/DebugUtils.h index 4685575441..18cd487b8f 100644 --- a/libs/ui/include/ui/DebugUtils.h +++ b/libs/ui/include/ui/DebugUtils.h @@ -34,5 +34,4 @@ std::string decodeColorMode(android::ui::ColorMode colormode); std::string decodeColorTransform(android_color_transform colorTransform); std::string decodePixelFormat(android::PixelFormat format); std::string decodeRenderIntent(android::ui::RenderIntent renderIntent); -std::string to_string(const android::Rect& rect); std::string toString(const android::DeviceProductInfo&); diff --git a/libs/ui/include/ui/DeviceProductInfo.h b/libs/ui/include/ui/DeviceProductInfo.h index af00342c0c..807a5d96a3 100644 --- a/libs/ui/include/ui/DeviceProductInfo.h +++ b/libs/ui/include/ui/DeviceProductInfo.h @@ -19,7 +19,12 @@ #include <array> #include <cstdint> #include <optional> +#include <string> +#include <type_traits> #include <variant> +#include <vector> + +#include <utils/Flattenable.h> namespace android { @@ -29,13 +34,7 @@ using PnpId = std::array<char, 4>; // Product-specific information about the display or the directly connected device on the // display chain. For example, if the display is transitively connected, this field may contain // product information about the intermediate device. -struct DeviceProductInfo { - static constexpr size_t TEXT_BUFFER_SIZE = 20; - static constexpr size_t RELATIVE_ADDRESS_SIZE = 4; - - using RelativeAddress = std::array<uint8_t, RELATIVE_ADDRESS_SIZE>; - static constexpr RelativeAddress NO_RELATIVE_ADDRESS = {0xff, 0xff, 0xff, 0xff}; - +struct DeviceProductInfo : LightFlattenable<DeviceProductInfo> { struct ModelYear { uint32_t year; }; @@ -48,21 +47,29 @@ struct DeviceProductInfo { }; // Display name. - std::array<char, TEXT_BUFFER_SIZE> name; + std::string name; // Manufacturer Plug and Play ID. PnpId manufacturerPnpId; // Manufacturer product ID. - std::array<char, TEXT_BUFFER_SIZE> productId; + std::string productId; using ManufactureOrModelDate = std::variant<ModelYear, ManufactureYear, ManufactureWeekAndYear>; + static_assert(std::is_trivially_copyable_v<ManufactureOrModelDate>); ManufactureOrModelDate manufactureOrModelDate; - // Relative address in the display network. Unavailable address is indicated - // by all elements equal to 255. + // Relative address in the display network. Empty vector indicates that the + // address is unavailable. // For example, for HDMI connected device this will be the physical address. - RelativeAddress relativeAddress; + std::vector<uint8_t> relativeAddress; + + bool isFixedSize() const { return false; } + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; + status_t unflatten(void const* buffer, size_t size); + + void dump(std::string& result) const; }; } // namespace android diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h new file mode 100644 index 0000000000..f196ab901a --- /dev/null +++ b/libs/ui/include/ui/DisplayId.h @@ -0,0 +1,194 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> +#include <optional> +#include <string> + +namespace android { + +// ID of a physical or a virtual display. This class acts as a type safe wrapper around uint64_t. +// The encoding of the ID is type-specific for bits 0 to 61. +struct DisplayId { + // Flag indicating that the display is virtual. + static constexpr uint64_t FLAG_VIRTUAL = 1ULL << 63; + + // Flag indicating that the ID is stable across reboots. + static constexpr uint64_t FLAG_STABLE = 1ULL << 62; + + // TODO(b/162612135) Remove default constructor + DisplayId() = default; + constexpr DisplayId(const DisplayId&) = default; + DisplayId& operator=(const DisplayId&) = default; + + uint64_t value; + +protected: + explicit constexpr DisplayId(uint64_t id) : value(id) {} +}; + +static_assert(sizeof(DisplayId) == sizeof(uint64_t)); + +inline bool operator==(DisplayId lhs, DisplayId rhs) { + return lhs.value == rhs.value; +} + +inline bool operator!=(DisplayId lhs, DisplayId rhs) { + return !(lhs == rhs); +} + +inline std::string to_string(DisplayId displayId) { + return std::to_string(displayId.value); +} + +// DisplayId of a physical display, such as the internal display or externally connected display. +struct PhysicalDisplayId : DisplayId { + static constexpr std::optional<PhysicalDisplayId> tryCast(DisplayId id) { + if (id.value & FLAG_VIRTUAL) { + return std::nullopt; + } + return {PhysicalDisplayId(id)}; + } + + // Returns a stable ID based on EDID information. + static constexpr PhysicalDisplayId fromEdid(uint8_t port, uint16_t manufacturerId, + uint32_t modelHash) { + return PhysicalDisplayId(FLAG_STABLE, port, manufacturerId, modelHash); + } + + // Returns an unstable ID. If EDID is available using "fromEdid" is preferred. + static constexpr PhysicalDisplayId fromPort(uint8_t port) { + constexpr uint16_t kManufacturerId = 0; + constexpr uint32_t kModelHash = 0; + return PhysicalDisplayId(0, port, kManufacturerId, kModelHash); + } + + // TODO(b/162612135) Remove default constructor + PhysicalDisplayId() = default; + // TODO(b/162612135) Remove constructor + explicit constexpr PhysicalDisplayId(uint64_t id) : DisplayId(id) {} + + constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); } + + constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); } + +private: + constexpr PhysicalDisplayId(uint64_t flags, uint8_t port, uint16_t manufacturerId, + uint32_t modelHash) + : DisplayId(flags | (static_cast<uint64_t>(manufacturerId) << 40) | + (static_cast<uint64_t>(modelHash) << 8) | port) {} + + explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other) {} +}; + +static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t)); + +struct VirtualDisplayId : DisplayId { + using BaseId = uint32_t; + // Flag indicating that this virtual display is backed by the GPU. + static constexpr uint64_t FLAG_GPU = 1ULL << 61; + + static constexpr std::optional<VirtualDisplayId> tryCast(DisplayId id) { + if (id.value & FLAG_VIRTUAL) { + return {VirtualDisplayId(id)}; + } + return std::nullopt; + } + +protected: + constexpr VirtualDisplayId(uint64_t flags, BaseId baseId) + : DisplayId(DisplayId::FLAG_VIRTUAL | flags | baseId) {} + + explicit constexpr VirtualDisplayId(DisplayId other) : DisplayId(other) {} +}; + +struct HalVirtualDisplayId : VirtualDisplayId { + explicit constexpr HalVirtualDisplayId(BaseId baseId) : VirtualDisplayId(0, baseId) {} + + static constexpr std::optional<HalVirtualDisplayId> tryCast(DisplayId id) { + if ((id.value & FLAG_VIRTUAL) && !(id.value & VirtualDisplayId::FLAG_GPU)) { + return {HalVirtualDisplayId(id)}; + } + return std::nullopt; + } + +private: + explicit constexpr HalVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {} +}; + +struct GpuVirtualDisplayId : VirtualDisplayId { + explicit constexpr GpuVirtualDisplayId(BaseId baseId) + : VirtualDisplayId(VirtualDisplayId::FLAG_GPU, baseId) {} + + static constexpr std::optional<GpuVirtualDisplayId> tryCast(DisplayId id) { + if ((id.value & FLAG_VIRTUAL) && (id.value & VirtualDisplayId::FLAG_GPU)) { + return {GpuVirtualDisplayId(id)}; + } + return std::nullopt; + } + +private: + explicit constexpr GpuVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {} +}; + +// HalDisplayId is the ID of a display which is managed by HWC. +// PhysicalDisplayId and HalVirtualDisplayId are implicitly convertible to HalDisplayId. +struct HalDisplayId : DisplayId { + constexpr HalDisplayId(HalVirtualDisplayId other) : DisplayId(other) {} + constexpr HalDisplayId(PhysicalDisplayId other) : DisplayId(other) {} + + static constexpr std::optional<HalDisplayId> tryCast(DisplayId id) { + if (GpuVirtualDisplayId::tryCast(id)) { + return std::nullopt; + } + return {HalDisplayId(id)}; + } + +private: + explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {} +}; + +static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t)); +static_assert(sizeof(HalVirtualDisplayId) == sizeof(uint64_t)); +static_assert(sizeof(GpuVirtualDisplayId) == sizeof(uint64_t)); +static_assert(sizeof(HalDisplayId) == sizeof(uint64_t)); + +} // namespace android + +namespace std { + +template <> +struct hash<android::DisplayId> { + size_t operator()(android::DisplayId displayId) const { + return hash<uint64_t>()(displayId.value); + } +}; + +template <> +struct hash<android::PhysicalDisplayId> : hash<android::DisplayId> {}; + +template <> +struct hash<android::HalVirtualDisplayId> : hash<android::DisplayId> {}; + +template <> +struct hash<android::GpuVirtualDisplayId> : hash<android::DisplayId> {}; + +template <> +struct hash<android::HalDisplayId> : hash<android::DisplayId> {}; + +} // namespace std diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h index 897060c2ed..03e0a3886e 100644 --- a/libs/ui/include/ui/DisplayInfo.h +++ b/libs/ui/include/ui/DisplayInfo.h @@ -20,19 +20,23 @@ #include <type_traits> #include <ui/DeviceProductInfo.h> +#include <utils/Flattenable.h> namespace android { enum class DisplayConnectionType { Internal, External }; // Immutable information about physical display. -struct DisplayInfo { +struct DisplayInfo : LightFlattenable<DisplayInfo> { DisplayConnectionType connectionType = DisplayConnectionType::Internal; float density = 0.f; bool secure = false; std::optional<DeviceProductInfo> deviceProductInfo; -}; -static_assert(std::is_trivially_copyable_v<DisplayInfo>); + bool isFixedSize() const { return false; } + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; + status_t unflatten(void const* buffer, size_t size); +}; } // namespace android diff --git a/libs/ui/include/ui/DisplayConfig.h b/libs/ui/include/ui/DisplayMode.h index d6fbaab387..145d7efd19 100644 --- a/libs/ui/include/ui/DisplayConfig.h +++ b/libs/ui/include/ui/DisplayMode.h @@ -21,10 +21,10 @@ #include <ui/Size.h> #include <utils/Timers.h> -namespace android { +namespace android::ui { -// Configuration supported by physical display. -struct DisplayConfig { +// Mode supported by physical display. +struct DisplayMode { ui::Size resolution; float xDpi = 0; float yDpi = 0; @@ -33,9 +33,9 @@ struct DisplayConfig { nsecs_t appVsyncOffset = 0; nsecs_t sfVsyncOffset = 0; nsecs_t presentationDeadline = 0; - int configGroup = -1; + int group = -1; }; -static_assert(std::is_trivially_copyable_v<DisplayConfig>); +static_assert(std::is_trivially_copyable_v<DisplayMode>); -} // namespace android +} // namespace android::ui diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h index 64efc84f1b..70a0d50611 100644 --- a/libs/ui/include/ui/DisplayState.h +++ b/libs/ui/include/ui/DisplayState.h @@ -32,7 +32,7 @@ constexpr LayerStack NO_LAYER_STACK = static_cast<LayerStack>(-1); struct DisplayState { LayerStack layerStack = NO_LAYER_STACK; Rotation orientation = ROTATION_0; - Size viewport; + Size layerStackSpaceRect; }; static_assert(std::is_trivially_copyable_v<DisplayState>); diff --git a/libs/ui/include/ui/FatVector.h b/libs/ui/include/ui/FatVector.h index 25fe3a0a2a..cb61e6a320 100644 --- a/libs/ui/include/ui/FatVector.h +++ b/libs/ui/include/ui/FatVector.h @@ -82,6 +82,12 @@ public: this->reserve(SIZE); } + FatVector(std::initializer_list<T> init) + : std::vector<T, InlineStdAllocator<T, SIZE>>(init, + InlineStdAllocator<T, SIZE>(mAllocation)) { + this->reserve(SIZE); + } + explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); } private: diff --git a/libs/ui/include/ui/PublicFormat.h b/libs/ui/include/ui/PublicFormat.h index 1152cc5526..aa58805718 100644 --- a/libs/ui/include/ui/PublicFormat.h +++ b/libs/ui/include/ui/PublicFormat.h @@ -50,9 +50,11 @@ enum class PublicFormat { JPEG = 0x100, DEPTH_POINT_CLOUD = 0x101, RAW_DEPTH = 0x1002, // @hide + RAW_DEPTH10 = 0x1003, // @hide YV12 = 0x32315659, Y8 = 0x20203859, Y16 = 0x20363159, // @hide + YCBCR_P010 = 0x36, DEPTH16 = 0x44363159, DEPTH_JPEG = 0x69656963, HEIC = 0x48454946, diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h index 2f2229e67c..58323e553e 100644 --- a/libs/ui/include/ui/Rect.h +++ b/libs/ui/include/ui/Rect.h @@ -19,10 +19,10 @@ #include <ostream> +#include <log/log.h> #include <utils/Flattenable.h> #include <utils/Log.h> #include <utils/TypeHelpers.h> -#include <log/log.h> #include <ui/FloatRect.h> #include <ui/Point.h> @@ -202,6 +202,15 @@ public: // the input. Rect transform(uint32_t xform, int32_t width, int32_t height) const; + Rect scale(float scaleX, float scaleY) const { + return Rect(FloatRect(left * scaleX, top * scaleY, right * scaleX, bottom * scaleY)); + } + + Rect& scaleSelf(float scaleX, float scaleY) { + set(scale(scaleX, scaleY)); + return *this; + } + // this calculates (Region(*this) - exclude).bounds() efficiently Rect reduce(const Rect& exclude) const; @@ -216,11 +225,10 @@ public: } }; +std::string to_string(const android::Rect& rect); + // Defining PrintTo helps with Google Tests. -static inline void PrintTo(const Rect& rect, ::std::ostream* os) { - *os << "Rect(" << rect.left << ", " << rect.top << ", " << rect.right << ", " << rect.bottom - << ")"; -} +void PrintTo(const Rect& rect, ::std::ostream* os); ANDROID_BASIC_TYPES_TRAITS(Rect) diff --git a/libs/ui/include/ui/Rotation.h b/libs/ui/include/ui/Rotation.h index 89008f6694..83d431dea3 100644 --- a/libs/ui/include/ui/Rotation.h +++ b/libs/ui/include/ui/Rotation.h @@ -41,6 +41,15 @@ constexpr Rotation operator+(Rotation lhs, Rotation rhs) { return toRotation((toRotationInt(lhs) + toRotationInt(rhs)) % N); } +constexpr Rotation operator-(Rotation lhs, Rotation rhs) { + constexpr auto N = toRotationInt(ROTATION_270) + 1; + return toRotation((N + toRotationInt(lhs) - toRotationInt(rhs)) % N); +} + +constexpr Rotation operator-(Rotation rotation) { + return ROTATION_0 - rotation; +} + constexpr const char* toCString(Rotation rotation) { switch (rotation) { case ROTATION_0: diff --git a/libs/ui/include/ui/StretchEffect.h b/libs/ui/include/ui/StretchEffect.h new file mode 100644 index 0000000000..1d2460ccfc --- /dev/null +++ b/libs/ui/include/ui/StretchEffect.h @@ -0,0 +1,57 @@ +/* + * Copyright 2021 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/Flattenable.h> +#include "FloatRect.h" + +#include <type_traits> + +namespace android { + +struct StretchEffect : public LightFlattenablePod<StretchEffect> { + FloatRect area = {0, 0, 0, 0}; + float vectorX = 0; + float vectorY = 0; + float maxAmount = 0; + + bool operator==(const StretchEffect& other) const { + return area == other.area && vectorX == other.vectorX && vectorY == other.vectorY && + maxAmount == other.maxAmount; + } + + static bool isZero(float value) { + constexpr float NON_ZERO_EPSILON = 0.001f; + return fabsf(value) <= NON_ZERO_EPSILON; + } + + bool isNoOp() const { return isZero(vectorX) && isZero(vectorY); } + + bool hasEffect() const { return !isNoOp(); } + + void sanitize() { + // If the area is empty, or the max amount is zero, then reset back to defaults + if (area.bottom >= area.top || area.left >= area.right || isZero(maxAmount)) { + *this = StretchEffect{}; + } + } +}; + +static_assert(std::is_trivially_copyable<StretchEffect>::value, + "StretchEffect must be trivially copyable to be flattenable"); + +} // namespace android
\ No newline at end of file diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h index c6bb598d7f..a197b3b20e 100644 --- a/libs/ui/include/ui/Transform.h +++ b/libs/ui/include/ui/Transform.h @@ -18,10 +18,10 @@ #include <stdint.h> #include <sys/types.h> +#include <array> #include <ostream> #include <string> -#include <hardware/hardware.h> #include <math/mat4.h> #include <math/vec2.h> #include <math/vec3.h> @@ -44,9 +44,9 @@ public: enum RotationFlags : uint32_t { ROT_0 = 0, - FLIP_H = HAL_TRANSFORM_FLIP_H, - FLIP_V = HAL_TRANSFORM_FLIP_V, - ROT_90 = HAL_TRANSFORM_ROT_90, + FLIP_H = 1, // HAL_TRANSFORM_FLIP_H + FLIP_V = 2, // HAL_TRANSFORM_FLIP_V + ROT_90 = 4, // HAL_TRANSFORM_ROT_90 ROT_180 = FLIP_H | FLIP_V, ROT_270 = ROT_180 | ROT_90, ROT_INVALID = 0x80 @@ -61,32 +61,43 @@ public: }; // query the transform - bool preserveRects() const; - uint32_t getType() const; - uint32_t getOrientation() const; + bool preserveRects() const; + + // Returns if bilinear filtering is needed after applying this transform to avoid aliasing. + bool needsBilinearFiltering() const; + + uint32_t getType() const; + uint32_t getOrientation() const; bool operator==(const Transform& other) const; const vec3& operator [] (size_t i) const; // returns column i float tx() const; float ty() const; - float sx() const; - float sy() const; + float dsdx() const; + float dtdx() const; + float dtdy() const; + float dsdy() const; + + float getScaleX() const; + float getScaleY() const; // modify the transform void reset(); void set(float tx, float ty); void set(float a, float b, float c, float d); status_t set(uint32_t flags, float w, float h); + void set(const std::array<float, 9>& matrix); // transform data Rect makeBounds(int w, int h) const; - vec2 transform(int x, int y) const; + vec2 transform(float x, float y) const; Region transform(const Region& reg) const; Rect transform(const Rect& bounds, bool roundOutwards = false) const; FloatRect transform(const FloatRect& bounds) const; Transform& operator = (const Transform& other); Transform operator * (const Transform& rhs) const; + Transform operator * (float value) const; // assumes the last row is < 0 , 0 , 1 > vec2 transform(const vec2& v) const; vec3 transform(const vec3& v) const; @@ -97,10 +108,10 @@ public: Transform inverse() const; // for debugging - void dump(std::string& result, const char* name) const; - void dump(const char* name) const; + void dump(std::string& result, const char* name, const char* prefix = "") const; + void dump(const char* name, const char* prefix = "") const; - static RotationFlags toRotationFlags(Rotation); + static constexpr RotationFlags toRotationFlags(Rotation); private: struct mat33 { @@ -125,7 +136,7 @@ inline void PrintTo(const Transform& t, ::std::ostream* os) { *os << out; } -inline Transform::RotationFlags Transform::toRotationFlags(Rotation rotation) { +inline constexpr Transform::RotationFlags Transform::toRotationFlags(Rotation rotation) { switch (rotation) { case ROTATION_0: return ROT_0; diff --git a/libs/ui/include_private/ui/FlattenableHelpers.h b/libs/ui/include_private/ui/FlattenableHelpers.h new file mode 100644 index 0000000000..8e316d8ae0 --- /dev/null +++ b/libs/ui/include_private/ui/FlattenableHelpers.h @@ -0,0 +1,160 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <numeric> +#include <optional> +#include <type_traits> +#include <vector> + +#include <utils/Flattenable.h> + +#define RETURN_IF_ERROR(op) \ + if (const status_t status = (op); status != OK) return status; + +namespace android { + +struct FlattenableHelpers { + // Helpers for reading and writing POD structures + template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> + static constexpr size_t getFlattenedSize(const T&) { + return sizeof(T); + } + + template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> + static status_t flatten(void** buffer, size_t* size, const T& value) { + if (*size < sizeof(T)) return NO_MEMORY; + FlattenableUtils::write(*buffer, *size, value); + return OK; + } + + template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> + static status_t unflatten(const void** buffer, size_t* size, T* value) { + if (*size < sizeof(T)) return NO_MEMORY; + FlattenableUtils::read(*buffer, *size, *value); + return OK; + } + + // Helpers for reading and writing std::string + static size_t getFlattenedSize(const std::string& str) { + return sizeof(uint64_t) + str.length(); + } + + static status_t flatten(void** buffer, size_t* size, const std::string& str) { + if (*size < getFlattenedSize(str)) return NO_MEMORY; + flatten(buffer, size, (uint64_t)str.length()); + memcpy(reinterpret_cast<char*>(*buffer), str.c_str(), str.length()); + FlattenableUtils::advance(*buffer, *size, str.length()); + return OK; + } + + static status_t unflatten(const void** buffer, size_t* size, std::string* str) { + uint64_t length; + RETURN_IF_ERROR(unflatten(buffer, size, &length)); + if (*size < length) return NO_MEMORY; + str->assign(reinterpret_cast<const char*>(*buffer), length); + FlattenableUtils::advance(*buffer, *size, length); + return OK; + } + + // Helpers for reading and writing LightFlattenable + template <class T> + static size_t getFlattenedSize(const LightFlattenable<T>& value) { + return value.getFlattenedSize(); + } + + template <class T> + static status_t flatten(void** buffer, size_t* size, const LightFlattenable<T>& value) { + RETURN_IF_ERROR(value.flatten(*buffer, *size)); + FlattenableUtils::advance(*buffer, *size, value.getFlattenedSize()); + return OK; + } + + template <class T> + static status_t unflatten(const void** buffer, size_t* size, LightFlattenable<T>* value) { + RETURN_IF_ERROR(value->unflatten(*buffer, *size)); + FlattenableUtils::advance(*buffer, *size, value->getFlattenedSize()); + return OK; + } + + // Helpers for reading and writing std::optional + template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>> + static size_t getFlattenedSize(const std::optional<T>& value) { + return sizeof(bool) + (value ? getFlattenedSize(*value) : 0); + } + + template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>> + static status_t flatten(void** buffer, size_t* size, const std::optional<T>& value) { + if (value) { + RETURN_IF_ERROR(flatten(buffer, size, true)); + RETURN_IF_ERROR(flatten(buffer, size, *value)); + } else { + RETURN_IF_ERROR(flatten(buffer, size, false)); + } + return OK; + } + + template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>> + static status_t unflatten(const void** buffer, size_t* size, std::optional<T>* value) { + bool isPresent; + RETURN_IF_ERROR(unflatten(buffer, size, &isPresent)); + if (isPresent) { + *value = T(); + RETURN_IF_ERROR(unflatten(buffer, size, &(**value))); + } else { + value->reset(); + } + return OK; + } + + // Helpers for reading and writing std::vector + template <class T> + static size_t getFlattenedSize(const std::vector<T>& value) { + return std::accumulate(value.begin(), value.end(), sizeof(uint64_t), + [](size_t sum, const T& element) { + return sum + getFlattenedSize(element); + }); + } + + template <class T> + static status_t flatten(void** buffer, size_t* size, const std::vector<T>& value) { + RETURN_IF_ERROR(flatten(buffer, size, (uint64_t)value.size())); + for (const auto& element : value) { + RETURN_IF_ERROR(flatten(buffer, size, element)); + } + return OK; + } + + template <class T> + static status_t unflatten(const void** buffer, size_t* size, std::vector<T>* value) { + uint64_t numElements; + RETURN_IF_ERROR(unflatten(buffer, size, &numElements)); + // We don't need an extra size check since each iteration of the loop does that + std::vector<T> elements; + for (size_t i = 0; i < numElements; i++) { + T element; + RETURN_IF_ERROR(unflatten(buffer, size, &element)); + elements.push_back(element); + } + *value = std::move(elements); + return OK; + } +}; + +} // namespace android + +#undef RETURN_IF_ERROR
\ No newline at end of file diff --git a/libs/ui/include/ui/ColorSpace.h b/libs/ui/include_types/ui/ColorSpace.h index 241ec106c0..241ec106c0 100644 --- a/libs/ui/include/ui/ColorSpace.h +++ b/libs/ui/include_types/ui/ColorSpace.h diff --git a/libs/ui/include_vndk/ui/ColorSpace.h b/libs/ui/include_vndk/ui/ColorSpace.h index ddf70d5bdf..7d2a6d307e 120000 --- a/libs/ui/include_vndk/ui/ColorSpace.h +++ b/libs/ui/include_vndk/ui/ColorSpace.h @@ -1 +1 @@ -../../include/ui/ColorSpace.h
\ No newline at end of file +../../include_types/ui/ColorSpace.h
\ No newline at end of file diff --git a/libs/ui/include_vndk/ui/DisplayConfig.h b/libs/ui/include_vndk/ui/DisplayConfig.h deleted file mode 120000 index 1450319502..0000000000 --- a/libs/ui/include_vndk/ui/DisplayConfig.h +++ /dev/null @@ -1 +0,0 @@ -../../include/ui/DisplayConfig.h
\ No newline at end of file diff --git a/libs/ui/include_vndk/ui/DisplayId.h b/libs/ui/include_vndk/ui/DisplayId.h new file mode 120000 index 0000000000..73c9fe8d68 --- /dev/null +++ b/libs/ui/include_vndk/ui/DisplayId.h @@ -0,0 +1 @@ +../../include/ui/DisplayId.h
\ No newline at end of file diff --git a/libs/ui/include_vndk/ui/DisplayMode.h b/libs/ui/include_vndk/ui/DisplayMode.h new file mode 120000 index 0000000000..c87754a682 --- /dev/null +++ b/libs/ui/include_vndk/ui/DisplayMode.h @@ -0,0 +1 @@ +../../include/ui/DisplayMode.h
\ No newline at end of file diff --git a/libs/ui/include_vndk/ui/PhysicalDisplayId.h b/libs/ui/include_vndk/ui/PhysicalDisplayId.h deleted file mode 120000 index 6e3fb1e62e..0000000000 --- a/libs/ui/include_vndk/ui/PhysicalDisplayId.h +++ /dev/null @@ -1 +0,0 @@ -../../include/ui/PhysicalDisplayId.h
\ No newline at end of file diff --git a/libs/ui/include_vndk/ui/UiConfig.h b/libs/ui/include_vndk/ui/UiConfig.h deleted file mode 120000 index f580ce1095..0000000000 --- a/libs/ui/include_vndk/ui/UiConfig.h +++ /dev/null @@ -1 +0,0 @@ -../../include/ui/UiConfig.h
\ No newline at end of file diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index bc53346274..d005ce8e23 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -14,15 +14,6 @@ // limitations under the License. // -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_libs_ui_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_libs_ui_license"], -} - cc_test { name: "Region_test", shared_libs: ["libui"], @@ -38,6 +29,20 @@ cc_test { } cc_test { + name: "DisplayId_test", + shared_libs: ["libui"], + srcs: ["DisplayId_test.cpp"], + cflags: ["-Wall", "-Werror"], +} + +cc_test { + name: "FlattenableHelpers_test", + shared_libs: ["libui"], + srcs: ["FlattenableHelpers_test.cpp"], + cflags: ["-Wall", "-Werror"], +} + +cc_test { name: "GraphicBufferAllocator_test", header_libs: [ "libnativewindow_headers", @@ -87,6 +92,14 @@ cc_test { } cc_test { + name: "Rect_test", + test_suites: ["device-tests"], + shared_libs: ["libui"], + srcs: ["Rect_test.cpp"], + cflags: ["-Wall", "-Werror"], +} + +cc_test { name: "Size_test", test_suites: ["device-tests"], shared_libs: ["libui"], diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp new file mode 100644 index 0000000000..1d908b8ef1 --- /dev/null +++ b/libs/ui/tests/DisplayId_test.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ui/DisplayId.h> + +#include <gtest/gtest.h> + +namespace android::ui { + +TEST(DisplayIdTest, createPhysicalIdFromEdid) { + constexpr uint8_t port = 1; + constexpr uint16_t manufacturerId = 13; + constexpr uint32_t modelHash = 42; + PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash); + EXPECT_EQ(port, id.getPort()); + EXPECT_EQ(manufacturerId, id.getManufacturerId()); + EXPECT_FALSE(VirtualDisplayId::tryCast(id)); + EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); + EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); + EXPECT_TRUE(PhysicalDisplayId::tryCast(id)); + EXPECT_TRUE(HalDisplayId::tryCast(id)); +} + +TEST(DisplayIdTest, createPhysicalIdFromPort) { + constexpr uint8_t port = 3; + PhysicalDisplayId id = PhysicalDisplayId::fromPort(port); + EXPECT_EQ(port, id.getPort()); + EXPECT_FALSE(VirtualDisplayId::tryCast(id)); + EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); + EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); + EXPECT_TRUE(PhysicalDisplayId::tryCast(id)); + EXPECT_TRUE(HalDisplayId::tryCast(id)); +} + +TEST(DisplayIdTest, createGpuVirtualId) { + GpuVirtualDisplayId id(42); + EXPECT_TRUE(VirtualDisplayId::tryCast(id)); + EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id)); + EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); + EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); + EXPECT_FALSE(HalDisplayId::tryCast(id)); +} + +TEST(DisplayIdTest, createHalVirtualId) { + HalVirtualDisplayId id(42); + EXPECT_TRUE(VirtualDisplayId::tryCast(id)); + EXPECT_TRUE(HalVirtualDisplayId::tryCast(id)); + EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); + EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); + EXPECT_TRUE(HalDisplayId::tryCast(id)); +} + +} // namespace android::ui diff --git a/libs/ui/tests/FlattenableHelpers_test.cpp b/libs/ui/tests/FlattenableHelpers_test.cpp new file mode 100644 index 0000000000..db32bc7596 --- /dev/null +++ b/libs/ui/tests/FlattenableHelpers_test.cpp @@ -0,0 +1,136 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "FlattenableHelpersTest" + +#include <ui/FlattenableHelpers.h> + +#include <gtest/gtest.h> +#include <utils/Flattenable.h> +#include <cstdint> +#include <memory> +#include <optional> +#include <string> +#include <vector> + +namespace android { + +namespace { + +struct TestLightFlattenable : LightFlattenable<TestLightFlattenable> { + std::unique_ptr<int32_t> ptr; + + bool isFixedSize() const { return true; } + size_t getFlattenedSize() const { return sizeof(int32_t); } + + status_t flatten(void* buffer, size_t size) const { + FlattenableUtils::write(buffer, size, *ptr); + return OK; + } + + status_t unflatten(void const* buffer, size_t size) { + int value; + FlattenableUtils::read(buffer, size, value); + ptr = std::make_unique<int32_t>(value); + return OK; + } +}; + +class FlattenableHelpersTest : public testing::Test { +public: + template <class T> + void testWriteThenRead(const T& value, size_t bufferSize) { + std::vector<int8_t> buffer(bufferSize); + auto rawBuffer = reinterpret_cast<void*>(buffer.data()); + size_t size = buffer.size(); + ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value)); + + auto rawReadBuffer = reinterpret_cast<const void*>(buffer.data()); + size = buffer.size(); + T valueRead; + ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead)); + EXPECT_EQ(value, valueRead); + } + + template <class T> + void testTriviallyCopyable(const T& value) { + testWriteThenRead(value, sizeof(T)); + } + + template <class T> + void testWriteThenRead(const T& value) { + testWriteThenRead(value, FlattenableHelpers::getFlattenedSize(value)); + } +}; + +TEST_F(FlattenableHelpersTest, TriviallyCopyable) { + testTriviallyCopyable(42); + testTriviallyCopyable(1LL << 63); + testTriviallyCopyable(false); + testTriviallyCopyable(true); + testTriviallyCopyable(std::optional<int>()); + testTriviallyCopyable(std::optional<int>(4)); +} + +TEST_F(FlattenableHelpersTest, String) { + testWriteThenRead(std::string("Android")); + testWriteThenRead(std::string()); +} + +TEST_F(FlattenableHelpersTest, Vector) { + testWriteThenRead(std::vector<int>({1, 2, 3})); + testWriteThenRead(std::vector<int>()); +} + +TEST_F(FlattenableHelpersTest, OptionalOfLightFlattenable) { + std::vector<size_t> buffer; + constexpr int kInternalValue = 16; + { + std::optional<TestLightFlattenable> value = + TestLightFlattenable{.ptr = std::make_unique<int32_t>(kInternalValue)}; + buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0); + void* rawBuffer = reinterpret_cast<void*>(buffer.data()); + size_t size = buffer.size(); + ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value)); + } + + const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data()); + size_t size = buffer.size(); + std::optional<TestLightFlattenable> valueRead; + ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead)); + ASSERT_TRUE(valueRead.has_value()); + EXPECT_EQ(kInternalValue, *valueRead->ptr); +} + +TEST_F(FlattenableHelpersTest, NullOptionalOfLightFlattenable) { + std::vector<size_t> buffer; + { + std::optional<TestLightFlattenable> value; + buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0); + void* rawBuffer = reinterpret_cast<void*>(buffer.data()); + size_t size = buffer.size(); + ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value)); + } + + const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data()); + size_t size = buffer.size(); + std::optional<TestLightFlattenable> valueRead; + ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead)); + ASSERT_FALSE(valueRead.has_value()); +} + +} // namespace +} // namespace android diff --git a/libs/ui/tests/Rect_test.cpp b/libs/ui/tests/Rect_test.cpp new file mode 100644 index 0000000000..5499a5b507 --- /dev/null +++ b/libs/ui/tests/Rect_test.cpp @@ -0,0 +1,262 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <system/graphics.h> +#include <ui/FloatRect.h> +#include <ui/Point.h> +#include <ui/Rect.h> +#include <ui/Size.h> + +#include <gtest/gtest.h> + +namespace android::ui { + +TEST(RectTest, constructDefault) { + const Rect rect; + EXPECT_FALSE(rect.isValid()); + EXPECT_TRUE(rect.isEmpty()); +} + +TEST(RectTest, constructFromWidthAndHeight) { + const Rect rect(100, 200); + EXPECT_TRUE(rect.isValid()); + EXPECT_FALSE(rect.isEmpty()); + EXPECT_EQ(0, rect.top); + EXPECT_EQ(0, rect.left); + EXPECT_EQ(100, rect.right); + EXPECT_EQ(200, rect.bottom); + EXPECT_EQ(100, rect.getWidth()); + EXPECT_EQ(200, rect.getHeight()); +} + +TEST(RectTest, constructFromSize) { + const Rect rect(Size(100, 200)); + EXPECT_TRUE(rect.isValid()); + EXPECT_FALSE(rect.isEmpty()); + EXPECT_EQ(0, rect.top); + EXPECT_EQ(0, rect.left); + EXPECT_EQ(100, rect.right); + EXPECT_EQ(200, rect.bottom); + EXPECT_EQ(100, rect.getWidth()); + EXPECT_EQ(200, rect.getHeight()); +} + +TEST(RectTest, constructFromLTRB) { + const Rect rect(11, 12, 14, 14); + EXPECT_TRUE(rect.isValid()); + EXPECT_FALSE(rect.isEmpty()); + EXPECT_EQ(11, rect.left); + EXPECT_EQ(12, rect.top); + EXPECT_EQ(14, rect.right); + EXPECT_EQ(14, rect.bottom); + EXPECT_EQ(3, rect.getWidth()); + EXPECT_EQ(2, rect.getHeight()); +} + +TEST(RectTest, constructFromPoints) { + const Rect rect(Point(11, 12), Point(14, 14)); + EXPECT_TRUE(rect.isValid()); + EXPECT_FALSE(rect.isEmpty()); + EXPECT_EQ(11, rect.left); + EXPECT_EQ(12, rect.top); + EXPECT_EQ(14, rect.right); + EXPECT_EQ(14, rect.bottom); + EXPECT_EQ(3, rect.getWidth()); + EXPECT_EQ(2, rect.getHeight()); +} + +TEST(RectTest, constructFromFloatRect) { + { + const Rect rect(FloatRect(10, 20, 30, 40)); + EXPECT_TRUE(rect.isValid()); + EXPECT_FALSE(rect.isEmpty()); + EXPECT_EQ(10, rect.left); + EXPECT_EQ(20, rect.top); + EXPECT_EQ(30, rect.right); + EXPECT_EQ(40, rect.bottom); + } + // Construct with floating point error + { + constexpr float kError = 1e-3; + const Rect rect(FloatRect(10 - kError, 20 - kError, 30 - kError, 40 - kError)); + EXPECT_TRUE(rect.isValid()); + EXPECT_FALSE(rect.isEmpty()); + EXPECT_EQ(10, rect.left); + EXPECT_EQ(20, rect.top); + EXPECT_EQ(30, rect.right); + EXPECT_EQ(40, rect.bottom); + } +} + +TEST(RectTest, makeInvalid) { + Rect rect(10, 20, 60, 60); + EXPECT_TRUE(rect.isValid()); + rect.makeInvalid(); + EXPECT_FALSE(rect.isValid()); +} + +TEST(RectTest, clear) { + Rect rect(10, 20, 60, 60); + EXPECT_FALSE(rect.isEmpty()); + rect.clear(); + EXPECT_TRUE(rect.isEmpty()); +} + +TEST(RectTest, getSize) { + const Rect rect(10, 20, 60, 60); + EXPECT_EQ(Size(50, 40), rect.getSize()); +} + +TEST(RectTest, getBounds) { + const Rect rect(10, 20, 60, 60); + const Rect bounds = rect.getBounds(); + EXPECT_EQ(0, bounds.left); + EXPECT_EQ(0, bounds.top); + EXPECT_EQ(50, bounds.right); + EXPECT_EQ(40, bounds.bottom); + EXPECT_EQ(rect.getSize(), bounds.getSize()); +} + +TEST(RectTest, getCornerPoints) { + const Rect rect(10, 20, 50, 60); + EXPECT_EQ(Point(10, 20), rect.leftTop()); + EXPECT_EQ(Point(10, 60), rect.leftBottom()); + EXPECT_EQ(Point(50, 20), rect.rightTop()); + EXPECT_EQ(Point(50, 60), rect.rightBottom()); +} + +TEST(RectTest, operatorEquals) { + const Rect rect(10, 20, 50, 60); + EXPECT_EQ(rect, rect); + EXPECT_NE(Rect(0, 20, 50, 60), rect); + EXPECT_NE(Rect(10, 0, 50, 60), rect); + EXPECT_NE(Rect(10, 20, 0, 60), rect); + EXPECT_NE(Rect(10, 20, 50, 0), rect); +} + +TEST(RectTest, operatorsPlusMinus) { + Rect rect = Rect(10, 20, 50, 60) + Point(1, 2); + EXPECT_EQ(Rect(11, 22, 51, 62), rect); + rect -= Point(1, 2); + EXPECT_EQ(Rect(10, 20, 50, 60), rect); + + rect = Rect(10, 20, 50, 60) - Point(1, 2); + EXPECT_EQ(Rect(9, 18, 49, 58), rect); + rect += Point(1, 2); + EXPECT_EQ(Rect(10, 20, 50, 60), rect); +} + +TEST(RectTest, scale) { + Rect rect(10, 20, 50, 60); + EXPECT_EQ(Rect(20, 60, 100, 180), rect.scale(2.f, 3.f)); + rect.scaleSelf(2.f, 3.f); + EXPECT_EQ(Rect(20, 60, 100, 180), rect); + + rect = Rect(10, 20, 50, 60); + constexpr float kError = 1e-3; + EXPECT_EQ(Rect(20, 60, 100, 180), rect.scale(2.f - kError, 3.f - kError)); + rect.scaleSelf(2.f - kError, 3.f - kError); + EXPECT_EQ(Rect(20, 60, 100, 180), rect); +} + +TEST(RectTest, inset) { + Rect rect(10, 20, 50, 60); + rect.inset(0, 0, 0, 0); + EXPECT_EQ(Rect(10, 20, 50, 60), rect); + rect.inset(1, 2, 3, 4); + EXPECT_EQ(Rect(11, 22, 47, 56), rect); +} + +TEST(RectTest, intersect) { + const Rect rect(10, 20, 50, 60); + Rect intersection; + + // Intersect with self is self + intersection.makeInvalid(); + EXPECT_TRUE(rect.intersect(rect, &intersection)); + EXPECT_EQ(Rect(10, 20, 50, 60), intersection); + + // Intersect with rect contained in us + const Rect insideRect(11, 21, 45, 55); + intersection.makeInvalid(); + EXPECT_TRUE(rect.intersect(insideRect, &intersection)); + EXPECT_EQ(insideRect, intersection); + + // Intersect with rect we are contained in + intersection.makeInvalid(); + EXPECT_TRUE(insideRect.intersect(rect, &intersection)); + EXPECT_EQ(insideRect, intersection); + + // Empty intersection + intersection.makeInvalid(); + EXPECT_FALSE(rect.intersect(Rect(100, 202, 150, 260), &intersection)); + EXPECT_TRUE(intersection.isEmpty()); + + // Partial intersection + const Rect other(30, 40, 70, 80); + intersection.makeInvalid(); + EXPECT_TRUE(rect.intersect(other, &intersection)); + EXPECT_EQ(Rect(30, 40, 50, 60), intersection); + + // Intersetion is commutative + intersection.makeInvalid(); + EXPECT_TRUE(other.intersect(rect, &intersection)); + EXPECT_EQ(Rect(30, 40, 50, 60), intersection); +} + +TEST(RectTest, reduce) { + const Rect rect(10, 20, 50, 60); + + // Reduce with self is empty + EXPECT_TRUE(rect.reduce(rect).isEmpty()); + + // Reduce with rect entirely inside is a noop + const Rect insideRect(11, 21, 45, 55); + EXPECT_EQ(rect, rect.reduce(insideRect)); + + // Reduce with rect entirely outside is empty + EXPECT_TRUE(insideRect.reduce(rect).isEmpty()); + + // Reduce with rect on the right + EXPECT_EQ(Rect(10, 20, 20, 60), rect.reduce(Rect(20, 0, 60, 70))); + + // Reduce with rect on the left + EXPECT_EQ(Rect(40, 20, 50, 60), rect.reduce(Rect(0, 0, 40, 70))); + + // Reduce with rect at the top + EXPECT_EQ(Rect(10, 40, 50, 60), rect.reduce(Rect(0, 0, 70, 40))); + + // Reduce with rect at the bottom + EXPECT_EQ(Rect(10, 20, 50, 40), rect.reduce(Rect(0, 40, 70, 70))); +} + +TEST(RectTest, transform) { + const int32_t width = 100, height = 200; + const Rect rect(1, 1, 2, 3); + EXPECT_EQ(Rect(98, 1, 99, 3), rect.transform(HAL_TRANSFORM_FLIP_H, width, height)); + EXPECT_EQ(Rect(1, 197, 2, 199), rect.transform(HAL_TRANSFORM_FLIP_V, width, height)); + EXPECT_EQ(Rect(197, 1, 199, 2), rect.transform(HAL_TRANSFORM_ROT_90, width, height)); + EXPECT_EQ(Rect(98, 197, 99, 199), rect.transform(HAL_TRANSFORM_ROT_180, width, height)); + EXPECT_EQ(Rect(1, 98, 3, 99), rect.transform(HAL_TRANSFORM_ROT_270, width, height)); +} + +TEST(RectTest, toFloatRect) { + const Rect rect(10, 20, 50, 60); + const FloatRect floatRect = rect.toFloatRect(); + EXPECT_EQ(FloatRect(10.f, 20.f, 50.f, 60.f), floatRect); +} + +} // namespace android::ui diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp index 38f37ad827..5f75aeabeb 100644 --- a/libs/ui/tests/Size_test.cpp +++ b/libs/ui/tests/Size_test.cpp @@ -33,8 +33,7 @@ #include <gtest/gtest.h> -namespace android { -namespace ui { +namespace android::ui { TEST(SizeTest, BasicConstructionAndEqualityComparison) { Size s(123, 456); @@ -215,5 +214,4 @@ TEST(SizeTest, Uint32RangeIsClamped) { ClampTest(uint32_t(0), int32_t(0)); } -} // namespace ui -} // namespace android +} // namespace android::ui diff --git a/libs/ui/tests/TEST_MAPPING b/libs/ui/tests/TEST_MAPPING index 7fcd7de319..eece18ee82 100644 --- a/libs/ui/tests/TEST_MAPPING +++ b/libs/ui/tests/TEST_MAPPING @@ -2,6 +2,9 @@ "presubmit": [ { "name": "Size_test" + }, + { + "name": "Rect_test" } ] } diff --git a/libs/ui/tools/Android.bp b/libs/ui/tools/Android.bp index c28c303c0c..fb46c2b20c 100644 --- a/libs/ui/tools/Android.bp +++ b/libs/ui/tools/Android.bp @@ -14,15 +14,6 @@ // limitations under the License. // -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_libs_ui_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_libs_ui_license"], -} - cc_defaults { name: "libui_tools_default", clang_cflags: [ diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp index 11b09bdced..49bc6bf067 100644 --- a/libs/vibrator/Android.bp +++ b/libs/vibrator/Android.bp @@ -12,17 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library { name: "libvibrator", + vendor_available: true, + double_loadable: true, shared_libs: [ "libbinder", diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp new file mode 100644 index 0000000000..749c568457 --- /dev/null +++ b/libs/vibrator/ExternalVibrationUtils.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <cstring> + +#include <math.h> + +#include <vibrator/ExternalVibrationUtils.h> + +namespace android::os { + +namespace { +static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f; +static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f; +static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f; + +float getHapticScaleGamma(HapticScale scale) { + switch (scale) { + case HapticScale::VERY_LOW: + return 2.0f; + case HapticScale::LOW: + return 1.5f; + case HapticScale::HIGH: + return 0.5f; + case HapticScale::VERY_HIGH: + return 0.25f; + default: + return 1.0f; + } +} + +float getHapticMaxAmplitudeRatio(HapticScale scale) { + switch (scale) { + case HapticScale::VERY_LOW: + return HAPTIC_SCALE_VERY_LOW_RATIO; + case HapticScale::LOW: + return HAPTIC_SCALE_LOW_RATIO; + case HapticScale::NONE: + case HapticScale::HIGH: + case HapticScale::VERY_HIGH: + return 1.0f; + default: + return 0.0f; + } +} + +} // namespace + +bool isValidHapticScale(HapticScale scale) { + switch (scale) { + case HapticScale::MUTE: + case HapticScale::VERY_LOW: + case HapticScale::LOW: + case HapticScale::NONE: + case HapticScale::HIGH: + case HapticScale::VERY_HIGH: + return true; + } + return false; +} + +void scaleHapticData(float* buffer, size_t length, HapticScale scale) { + if (!isValidHapticScale(scale) || scale == HapticScale::NONE) { + return; + } + if (scale == HapticScale::MUTE) { + memset(buffer, 0, length * sizeof(float)); + return; + } + float gamma = getHapticScaleGamma(scale); + float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(scale); + for (size_t i = 0; i < length; i++) { + float sign = buffer[i] >= 0 ? 1.0 : -1.0; + buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma) + * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign; + } +} + +} // namespace android::os diff --git a/libs/vibrator/fuzzer/Android.bp b/libs/vibrator/fuzzer/Android.bp index f2a313cb8a..802015180e 100644 --- a/libs/vibrator/fuzzer/Android.bp +++ b/libs/vibrator/fuzzer/Android.bp @@ -17,15 +17,6 @@ ***************************************************************************** */ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_fuzz { name: "vibrator_fuzzer", diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h new file mode 100644 index 0000000000..20045d0769 --- /dev/null +++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_EXTERNAL_VIBRATION_UTILS_H +#define ANDROID_EXTERNAL_VIBRATION_UTILS_H + +#include <android/os/IExternalVibratorService.h> + +namespace android::os { + +enum class HapticScale { + MUTE = IExternalVibratorService::SCALE_MUTE, + VERY_LOW = IExternalVibratorService::SCALE_VERY_LOW, + LOW = IExternalVibratorService::SCALE_LOW, + NONE = IExternalVibratorService::SCALE_NONE, + HIGH = IExternalVibratorService::SCALE_HIGH, + VERY_HIGH = IExternalVibratorService::SCALE_VERY_HIGH, +}; + +bool isValidHapticScale(HapticScale scale); + +void scaleHapticData(float* buffer, size_t length, HapticScale scale); + +} // namespace android::os + +#endif // ANDROID_EXTERNAL_VIBRATION_UTILS_H diff --git a/libs/vr/Android.bp b/libs/vr/Android.bp index b308895faf..e8176cf6b6 100644 --- a/libs/vr/Android.bp +++ b/libs/vr/Android.bp @@ -1,14 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-BSD - // legacy_notice - default_applicable_licenses: ["frameworks_native_license"], -} - subdirs = [ "*", ] diff --git a/libs/vr/libbroadcastring/Android.bp b/libs/vr/libbroadcastring/Android.bp index 2eb2f9f149..13af470a26 100644 --- a/libs/vr/libbroadcastring/Android.bp +++ b/libs/vr/libbroadcastring/Android.bp @@ -1,14 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library_static { name: "libbroadcastring", clang: true, diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp index 45bdd35519..37c19d43f7 100644 --- a/libs/vr/libbufferhub/Android.bp +++ b/libs/vr/libbufferhub/Android.bp @@ -12,17 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library_headers { name: "libbufferhub_headers", export_include_dirs: ["include"], diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp index f372bd79c2..77c79112de 100644 --- a/libs/vr/libbufferhubqueue/Android.bp +++ b/libs/vr/libbufferhubqueue/Android.bp @@ -12,17 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_native_license"], -} - sourceFiles = [ "buffer_hub_queue_client.cpp", "buffer_hub_queue_parcelable.cpp", diff --git a/libs/vr/libbufferhubqueue/benchmarks/Android.bp b/libs/vr/libbufferhubqueue/benchmarks/Android.bp index fc1f376a73..ef1eed6d0a 100644 --- a/libs/vr/libbufferhubqueue/benchmarks/Android.bp +++ b/libs/vr/libbufferhubqueue/benchmarks/Android.bp @@ -1,15 +1,4 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_native_license"], -} - cc_benchmark { srcs: ["buffer_transport_benchmark.cpp"], shared_libs: [ diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp index e883916f1a..a33792177b 100644 --- a/libs/vr/libbufferhubqueue/tests/Android.bp +++ b/libs/vr/libbufferhubqueue/tests/Android.bp @@ -1,15 +1,4 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_native_license"], -} - header_libraries = [ "libdvr_headers", ] diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp index 365a676bc4..8c354fbc18 100644 --- a/libs/vr/libdisplay/Android.bp +++ b/libs/vr/libdisplay/Android.bp @@ -12,17 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_native_license"], -} - sourceFiles = [ "display_client.cpp", "display_manager_client.cpp", diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp index 83c30d7010..d5a19d3e6c 100644 --- a/libs/vr/libdvr/Android.bp +++ b/libs/vr/libdvr/Android.bp @@ -13,17 +13,6 @@ // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library_headers { name: "libdvr_headers", export_include_dirs: ["include"], diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp index 4ed80a4929..3260447390 100644 --- a/libs/vr/libdvr/tests/Android.bp +++ b/libs/vr/libdvr/tests/Android.bp @@ -12,17 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_native_license"], -} - cc_test { srcs: [ "dvr_display_manager-test.cpp", diff --git a/libs/vr/libdvrcommon/Android.bp b/libs/vr/libdvrcommon/Android.bp index 9e1e516724..e75176846e 100644 --- a/libs/vr/libdvrcommon/Android.bp +++ b/libs/vr/libdvrcommon/Android.bp @@ -12,17 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_native_license"], -} - localIncludeFiles = [ "include", ] diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp index c1f6da3b10..24ba83048d 100644 --- a/libs/vr/libpdx/Android.bp +++ b/libs/vr/libpdx/Android.bp @@ -1,12 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library_headers { name: "libpdx_headers", export_include_dirs: ["private"], diff --git a/libs/vr/libpdx/fuzz/Android.bp b/libs/vr/libpdx/fuzz/Android.bp index cc32b1822b..b36e0deea0 100644 --- a/libs/vr/libpdx/fuzz/Android.bp +++ b/libs/vr/libpdx/fuzz/Android.bp @@ -1,12 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_fuzz { name: "libpdx_service_dispatcher_fuzzer", clang: true, diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp index ea73d7a9db..b3534de101 100644 --- a/libs/vr/libpdx_default_transport/Android.bp +++ b/libs/vr/libpdx_default_transport/Android.bp @@ -1,14 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_native_license"], -} - cc_defaults { name: "pdx_default_transport_compiler_defaults", clang: true, @@ -86,3 +75,4 @@ cc_binary { "libpdx_default_transport", ], } + diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp index 532d1a767c..1d6eea29a6 100644 --- a/libs/vr/libpdx_uds/Android.bp +++ b/libs/vr/libpdx_uds/Android.bp @@ -1,14 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library_static { name: "libpdx_uds", clang: true, diff --git a/libs/vr/libperformance/Android.bp b/libs/vr/libperformance/Android.bp index 5beee359b5..35d3dea9de 100644 --- a/libs/vr/libperformance/Android.bp +++ b/libs/vr/libperformance/Android.bp @@ -12,17 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_native_license"], -} - sourceFiles = [ "performance_client.cpp", "performance_rpc.cpp", diff --git a/libs/vr/libvr_manager/Android.bp b/libs/vr/libvr_manager/Android.bp index 6f2ada4633..2cd6a28e2f 100644 --- a/libs/vr/libvr_manager/Android.bp +++ b/libs/vr/libvr_manager/Android.bp @@ -12,15 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library_static { name: "libvr_manager", srcs: [ diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp index 8aca9a5adc..abc64bde5a 100644 --- a/libs/vr/libvrflinger/Android.bp +++ b/libs/vr/libvrflinger/Android.bp @@ -12,17 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_native_license"], -} - sourceFiles = [ "acquired_buffer.cpp", "epoll_event_dispatcher.cpp", diff --git a/libs/vr/libvrflinger/tests/Android.bp b/libs/vr/libvrflinger/tests/Android.bp index dafd35454f..7fafd3bf50 100644 --- a/libs/vr/libvrflinger/tests/Android.bp +++ b/libs/vr/libvrflinger/tests/Android.bp @@ -1,14 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_native_license"], -} - shared_libs = [ "android.hardware.configstore-utils", "android.hardware.configstore@1.0", diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp index 8f566a0a20..85427906f1 100644 --- a/libs/vr/libvrsensor/Android.bp +++ b/libs/vr/libvrsensor/Android.bp @@ -12,17 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_native_license"], -} - sourceFiles = [ "pose_client.cpp", "latency_model.cpp", @@ -63,3 +52,4 @@ cc_library { header_libs: ["libdvr_headers"], name: "libvrsensor", } + |