diff options
Diffstat (limited to 'libs')
367 files changed, 63836 insertions, 3581 deletions
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp new file mode 100644 index 0000000000..0d25176f98 --- /dev/null +++ b/libs/arect/Android.bp @@ -0,0 +1,27 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +ndk_headers { + name: "libarect_headers", + from: "include/android", + to: "android", + srcs: ["include/android/*.h"], + license: "NOTICE", +} + +cc_library_static { + name: "libarect", + host_supported: true, + export_include_dirs: ["include"], +} diff --git a/libs/arect/MODULE_LICENSE_APACHE2 b/libs/arect/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/libs/arect/MODULE_LICENSE_APACHE2 diff --git a/libs/arect/NOTICE b/libs/arect/NOTICE new file mode 100644 index 0000000000..c5b1efa7aa --- /dev/null +++ b/libs/arect/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libs/arect/include/android/rect.h b/libs/arect/include/android/rect.h new file mode 100644 index 0000000000..80741c0442 --- /dev/null +++ b/libs/arect/include/android/rect.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 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. + */ + +/** + * @addtogroup NativeActivity Native Activity + * @{ + */ + +/** + * @file rect.h + */ + +#ifndef ANDROID_RECT_H +#define ANDROID_RECT_H + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * {@link ARect} is a struct that represents a rectangular window area. + * + * It is used with {@link + * ANativeActivityCallbacks::onContentRectChanged} event callback and + * ANativeWindow_lock() function. + */ +typedef struct ARect { +#ifdef __cplusplus + typedef int32_t value_type; +#endif + /** left position */ + int32_t left; + /** top position */ + int32_t top; + /** left position */ + int32_t right; + /** bottom position */ + int32_t bottom; +} ARect; + +#ifdef __cplusplus +}; +#endif + +#endif // ANDROID_RECT_H + +/** @} */ diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 087718eaa8..66ba7b312b 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -40,6 +40,7 @@ cc_library { "BpBinder.cpp", "BufferedTextOutput.cpp", "Debug.cpp", + "IActivityManager.cpp", "IAppOpsCallback.cpp", "IAppOpsService.cpp", "IBatteryStats.cpp", @@ -51,6 +52,7 @@ cc_library { "IProcessInfoService.cpp", "IResultReceiver.cpp", "IServiceManager.cpp", + "IShellCallback.cpp", "MemoryBase.cpp", "MemoryDealer.cpp", "MemoryHeapBase.cpp", @@ -62,6 +64,8 @@ cc_library { "Static.cpp", "Status.cpp", "TextOutput.cpp", + "IpPrefix.cpp", + "Value.cpp", ], cflags: [ @@ -90,6 +94,10 @@ cc_library { "libbinder_headers", ], + export_include_dirs: [ + "include", + ], + clang: true, sanitize: { misc_undefined: ["integer"], diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index 7ce2a318a9..890ef30ec7 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -21,6 +21,7 @@ #include <binder/BpBinder.h> #include <binder/IInterface.h> #include <binder/IResultReceiver.h> +#include <binder/IShellCallback.h> #include <binder/Parcel.h> #include <stdio.h> @@ -62,7 +63,8 @@ bool IBinder::checkSubclass(const void* /*subclassID*/) const status_t IBinder::shellCommand(const sp<IBinder>& target, int in, int out, int err, - Vector<String16>& args, const sp<IResultReceiver>& resultReceiver) + Vector<String16>& args, const sp<IShellCallback>& callback, + const sp<IResultReceiver>& resultReceiver) { Parcel send; Parcel reply; @@ -74,6 +76,7 @@ status_t IBinder::shellCommand(const sp<IBinder>& target, int in, int out, int e for (size_t i = 0; i < numArgs; i++) { send.writeString16(args[i]); } + send.writeStrongBinder(callback != NULL ? IInterface::asBinder(callback) : NULL); send.writeStrongBinder(resultReceiver != NULL ? IInterface::asBinder(resultReceiver) : NULL); return target->transact(SHELL_COMMAND_TRANSACTION, send, &reply); } @@ -232,6 +235,8 @@ status_t BBinder::onTransact( for (int i = 0; i < argc && data.dataAvail() > 0; i++) { args.add(data.readString16()); } + sp<IShellCallback> shellCallback = IShellCallback::asInterface( + data.readStrongBinder()); sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface( data.readStrongBinder()); diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp new file mode 100644 index 0000000000..50a8b28aae --- /dev/null +++ b/libs/binder/IActivityManager.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <unistd.h> +#include <fcntl.h> + +#include <binder/IActivityManager.h> + +#include <binder/Parcel.h> + +namespace android { + +// ------------------------------------------------------------------------------------ + +class BpActivityManager : public BpInterface<IActivityManager> +{ +public: + explicit BpActivityManager(const sp<IBinder>& impl) + : BpInterface<IActivityManager>(impl) + { + } + + virtual int openContentUri(const String16& stringUri) + { + Parcel data, reply; + data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor()); + data.writeString16(stringUri); + status_t ret = remote()->transact(OPEN_CONTENT_URI_TRANSACTION, data, & reply); + int fd = -1; + if (ret == NO_ERROR) { + int32_t exceptionCode = reply.readExceptionCode(); + if (!exceptionCode) { + // Success is indicated here by a nonzero int followed by the fd; + // failure by a zero int with no data following. + if (reply.readInt32() != 0) { + fd = fcntl(reply.readParcelFileDescriptor(), F_DUPFD_CLOEXEC, 0); + } + } else { + // An exception was thrown back; fall through to return failure + ALOGD("openContentUri(%s) caught exception %d\n", + String8(stringUri).string(), exceptionCode); + } + } + return fd; + } +}; + +// ------------------------------------------------------------------------------------ + +IMPLEMENT_META_INTERFACE(ActivityManager, "android.app.IActivityManager"); + +}; // namespace android diff --git a/libs/binder/IMediaResourceMonitor.cpp b/libs/binder/IMediaResourceMonitor.cpp index 4800f5ba61..77e3d239bc 100644 --- a/libs/binder/IMediaResourceMonitor.cpp +++ b/libs/binder/IMediaResourceMonitor.cpp @@ -25,7 +25,7 @@ namespace android { class BpMediaResourceMonitor : public BpInterface<IMediaResourceMonitor> { public: - BpMediaResourceMonitor(const sp<IBinder>& impl) + explicit BpMediaResourceMonitor(const sp<IBinder>& impl) : BpInterface<IMediaResourceMonitor>(impl) {} virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type) diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp index 6b5b1afc00..5c1a4f41e0 100644 --- a/libs/binder/IMemory.cpp +++ b/libs/binder/IMemory.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "IMemory" #include <atomic> +#include <stdatomic.h> + #include <fcntl.h> #include <stdint.h> #include <stdio.h> @@ -28,6 +30,7 @@ #include <binder/IMemory.h> #include <binder/Parcel.h> #include <log/log.h> + #include <utils/CallStack.h> #include <utils/KeyedVector.h> #include <utils/threads.h> @@ -287,7 +290,7 @@ void BpMemoryHeap::assertMapped() const mBase = heap->mBase; mSize = heap->mSize; mOffset = heap->mOffset; - int fd = dup(heap->mHeapId.load(memory_order_relaxed)); + int fd = fcntl(heap->mHeapId.load(memory_order_relaxed), F_DUPFD_CLOEXEC, 0); ALOGE_IF(fd==-1, "cannot dup fd=%d", heap->mHeapId.load(memory_order_relaxed)); mHeapId.store(fd, memory_order_release); @@ -322,7 +325,7 @@ void BpMemoryHeap::assertReallyMapped() const Mutex::Autolock _l(mLock); if (mHeapId.load(memory_order_relaxed) == -1) { - int fd = dup( parcel_fd ); + int fd = fcntl(parcel_fd, F_DUPFD_CLOEXEC, 0); ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, err=%d (%s)", parcel_fd, size, err, strerror(errno)); diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index d0cd8f2f22..e8329613ab 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -338,6 +338,11 @@ void IPCThreadState::disableBackgroundScheduling(bool disable) gDisableBackgroundScheduling = disable; } +bool IPCThreadState::backgroundSchedulingDisabled() +{ + return gDisableBackgroundScheduling; +} + sp<ProcessState> IPCThreadState::process() { return mProcess; diff --git a/libs/binder/IResultReceiver.cpp b/libs/binder/IResultReceiver.cpp index 2a22b69957..646809e089 100644 --- a/libs/binder/IResultReceiver.cpp +++ b/libs/binder/IResultReceiver.cpp @@ -31,7 +31,7 @@ namespace android { class BpResultReceiver : public BpInterface<IResultReceiver> { public: - BpResultReceiver(const sp<IBinder>& impl) + explicit BpResultReceiver(const sp<IBinder>& impl) : BpInterface<IResultReceiver>(impl) { } diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 3aeff2eda3..c7a0f43a9d 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -23,6 +23,7 @@ #include <binder/Parcel.h> #include <utils/String8.h> #include <utils/SystemClock.h> +#include <utils/CallStack.h> #include <private/binder/Static.h> @@ -136,7 +137,12 @@ public: unsigned n; for (n = 0; n < 5; n++){ if (n > 0) { - ALOGI("Waiting for service %s...", String8(name).string()); + if (!strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder")) { + ALOGI("Waiting for vendor service %s...", String8(name).string()); + CallStack stack(LOG_TAG); + } else { + ALOGI("Waiting for service %s...", String8(name).string()); + } sleep(1); } sp<IBinder> svc = checkService(name); diff --git a/libs/binder/IShellCallback.cpp b/libs/binder/IShellCallback.cpp new file mode 100644 index 0000000000..c793df3266 --- /dev/null +++ b/libs/binder/IShellCallback.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "ShellCallback" + +#include <unistd.h> +#include <fcntl.h> + +#include <binder/IShellCallback.h> + +#include <utils/Log.h> +#include <binder/Parcel.h> +#include <utils/String8.h> + +#include <private/binder/Static.h> + +namespace android { + +// ---------------------------------------------------------------------- + +class BpShellCallback : public BpInterface<IShellCallback> +{ +public: + explicit BpShellCallback(const sp<IBinder>& impl) + : BpInterface<IShellCallback>(impl) + { + } + + virtual int openOutputFile(const String16& path, const String16& seLinuxContext) { + Parcel data, reply; + data.writeInterfaceToken(IShellCallback::getInterfaceDescriptor()); + data.writeString16(path); + data.writeString16(seLinuxContext); + remote()->transact(OP_OPEN_OUTPUT_FILE, data, &reply, 0); + reply.readExceptionCode(); + int fd = reply.readParcelFileDescriptor(); + return fd >= 0 ? fcntl(fd, F_DUPFD_CLOEXEC, 0) : fd; + + } +}; + +IMPLEMENT_META_INTERFACE(ShellCallback, "com.android.internal.os.IShellCallback"); + +// ---------------------------------------------------------------------- + +status_t BnShellCallback::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case OP_OPEN_OUTPUT_FILE: { + CHECK_INTERFACE(IShellCallback, data, reply); + String16 path(data.readString16()); + String16 seLinuxContext(data.readString16()); + int fd = openOutputFile(path, seLinuxContext); + if (reply != NULL) { + reply->writeNoException(); + if (fd >= 0) { + reply->writeInt32(1); + reply->writeParcelFileDescriptor(fd, true); + } else { + reply->writeInt32(0); + } + } else if (fd >= 0) { + close(fd); + } + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android diff --git a/libs/binder/IpPrefix.cpp b/libs/binder/IpPrefix.cpp new file mode 100644 index 0000000000..3a8a63c46e --- /dev/null +++ b/libs/binder/IpPrefix.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "IpPrefix" + +#include <binder/IpPrefix.h> +#include <vector> + +#include <binder/IBinder.h> +#include <binder/Parcel.h> +#include <log/log.h> +#include <utils/Errors.h> + +using android::BAD_TYPE; +using android::BAD_VALUE; +using android::NO_ERROR; +using android::Parcel; +using android::status_t; +using android::UNEXPECTED_NULL; +using namespace ::android::binder; + +namespace android { + +namespace net { + +#define RETURN_IF_FAILED(calledOnce) \ + { \ + status_t returnStatus = calledOnce; \ + if (returnStatus) { \ + ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ + return returnStatus; \ + } \ + } + +status_t IpPrefix::writeToParcel(Parcel* parcel) const { + /* + * Keep implementation in sync with writeToParcel() in + * frameworks/base/core/java/android/net/IpPrefix.java. + */ + std::vector<uint8_t> byte_vector; + + if (mIsIpv6) { + const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mIn6Addr); + byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr)); + } else { + const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mInAddr); + byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr)); + } + + RETURN_IF_FAILED(parcel->writeByteVector(byte_vector)); + RETURN_IF_FAILED(parcel->writeInt32(static_cast<int32_t>(mPrefixLength))); + + return NO_ERROR; +} + +status_t IpPrefix::readFromParcel(const Parcel* parcel) { + /* + * Keep implementation in sync with readFromParcel() in + * frameworks/base/core/java/android/net/IpPrefix.java. + */ + std::vector<uint8_t> byte_vector; + + RETURN_IF_FAILED(parcel->readByteVector(&byte_vector)); + RETURN_IF_FAILED(parcel->readInt32(&mPrefixLength)); + + if (byte_vector.size() == 16) { + mIsIpv6 = true; + memcpy((void*)&mUnion.mIn6Addr, &byte_vector[0], sizeof(mUnion.mIn6Addr)); + + } else if (byte_vector.size() == 4) { + mIsIpv6 = false; + memcpy((void*)&mUnion.mInAddr, &byte_vector[0], sizeof(mUnion.mInAddr)); + + } else { + ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ + return BAD_VALUE; + } + + return NO_ERROR; +} + +const struct in6_addr& IpPrefix::getAddressAsIn6Addr() const +{ + return mUnion.mIn6Addr; +} + +const struct in_addr& IpPrefix::getAddressAsInAddr() const +{ + return mUnion.mInAddr; +} + +bool IpPrefix::getAddressAsIn6Addr(struct in6_addr* addr) const +{ + if (isIpv6()) { + *addr = mUnion.mIn6Addr; + return true; + } + return false; +} + +bool IpPrefix::getAddressAsInAddr(struct in_addr* addr) const +{ + if (isIpv4()) { + *addr = mUnion.mInAddr; + return true; + } + return false; +} + +bool IpPrefix::isIpv6() const +{ + return mIsIpv6; +} + +bool IpPrefix::isIpv4() const +{ + return !mIsIpv6; +} + +int32_t IpPrefix::getPrefixLength() const +{ + return mPrefixLength; +} + +void IpPrefix::setAddress(const struct in6_addr& addr) +{ + mUnion.mIn6Addr = addr; + mIsIpv6 = true; +} + +void IpPrefix::setAddress(const struct in_addr& addr) +{ + mUnion.mInAddr = addr; + mIsIpv6 = false; +} + +void IpPrefix::setPrefixLength(int32_t prefix) +{ + mPrefixLength = prefix; +} + +bool operator==(const IpPrefix& lhs, const IpPrefix& rhs) +{ + if (lhs.mIsIpv6 != rhs.mIsIpv6) { + return false; + } + + if (lhs.mPrefixLength != rhs.mPrefixLength) { + return false; + } + + if (lhs.mIsIpv6) { + return 0 == memcmp(lhs.mUnion.mIn6Addr.s6_addr, rhs.mUnion.mIn6Addr.s6_addr, sizeof(struct in6_addr)); + } + + return 0 == memcmp(&lhs.mUnion.mInAddr, &rhs.mUnion.mInAddr, sizeof(struct in_addr)); +} + +} // namespace net + +} // namespace android diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index aed0134327..03f00be6a2 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -26,9 +26,9 @@ #include <unistd.h> #include <binder/MemoryHeapBase.h> -#include <log/log.h> #include <cutils/ashmem.h> #include <cutils/atomic.h> +#include <log/log.h> namespace android { @@ -82,7 +82,7 @@ MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t off { const size_t pagesize = getpagesize(); size = ((size + pagesize-1) & ~(pagesize-1)); - mapfd(dup(fd), size, offset); + mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset); } status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device) diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index d753eb5fa8..aec8f107a3 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -37,6 +37,7 @@ #include <binder/ProcessState.h> #include <binder/Status.h> #include <binder/TextOutput.h> +#include <binder/Value.h> #include <cutils/ashmem.h> #include <utils/Debug.h> @@ -210,7 +211,14 @@ status_t flatten_binder(const sp<ProcessState>& /*proc*/, { flat_binder_object obj; - obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; + if (IPCThreadState::self()->backgroundSchedulingDisabled()) { + /* minimum priority for all nodes is nice 0 */ + obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS; + } else { + /* minimum priority for all nodes is MAX_NICE(19) */ + obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS; + } + if (binder != NULL) { IBinder *local = binder->localBinder(); if (!local) { @@ -539,7 +547,7 @@ status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len) // If this is a file descriptor, we need to dup it so the // new Parcel now owns its own fd, and can declare that we // officially know we have fds. - flat->handle = dup(flat->handle); + flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0); flat->cookie = 1; mHasFds = mFdsKnown = true; if (!mAllowFds) { @@ -552,6 +560,14 @@ status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len) return err; } +int Parcel::compareData(const Parcel& other) { + size_t size = dataSize(); + if (size != other.dataSize()) { + return size < other.dataSize() ? -1 : 1; + } + return memcmp(data(), other.data(), size); +} + bool Parcel::allowFds() const { return mAllowFds; @@ -1106,6 +1122,10 @@ status_t Parcel::writeParcelable(const Parcelable& parcelable) { return parcelable.writeToParcel(this); } +status_t Parcel::writeValue(const binder::Value& value) { + return value.writeToParcel(this); +} + status_t Parcel::writeNativeHandle(const native_handle* handle) { if (!handle || handle->version != sizeof(native_handle)) @@ -1142,7 +1162,7 @@ status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) status_t Parcel::writeDupFileDescriptor(int fd) { - int dupFd = dup(fd); + int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (dupFd < 0) { return -errno; } @@ -1153,6 +1173,12 @@ status_t Parcel::writeDupFileDescriptor(int fd) return err; } +status_t Parcel::writeParcelFileDescriptor(int fd, bool takeOwnership) +{ + writeInt32(0); + return writeFileDescriptor(fd, takeOwnership); +} + status_t Parcel::writeUniqueFileDescriptor(const base::unique_fd& fd) { return writeDupFileDescriptor(fd.get()); } @@ -1324,6 +1350,120 @@ status_t Parcel::writeNoException() return status.writeToParcel(this); } +status_t Parcel::writeMap(const ::android::binder::Map& map_in) +{ + using ::std::map; + using ::android::binder::Value; + using ::android::binder::Map; + + Map::const_iterator iter; + status_t ret; + + ret = writeInt32(map_in.size()); + + if (ret != NO_ERROR) { + return ret; + } + + for (iter = map_in.begin(); iter != map_in.end(); ++iter) { + ret = writeValue(Value(iter->first)); + if (ret != NO_ERROR) { + return ret; + } + + ret = writeValue(iter->second); + if (ret != NO_ERROR) { + return ret; + } + } + + return ret; +} + +status_t Parcel::writeNullableMap(const std::unique_ptr<binder::Map>& map) +{ + if (map == NULL) { + return writeInt32(-1); + } + + return writeMap(*map.get()); +} + +status_t Parcel::readMap(::android::binder::Map* map_out)const +{ + using ::std::map; + using ::android::String16; + using ::android::String8; + using ::android::binder::Value; + using ::android::binder::Map; + + status_t ret = NO_ERROR; + int32_t count; + + ret = readInt32(&count); + if (ret != NO_ERROR) { + return ret; + } + + if (count < 0) { + ALOGE("readMap: Unexpected count: %d", count); + return (count == -1) + ? UNEXPECTED_NULL + : BAD_VALUE; + } + + map_out->clear(); + + while (count--) { + Map::key_type key; + Value value; + + ret = readValue(&value); + if (ret != NO_ERROR) { + return ret; + } + + if (!value.getString(&key)) { + ALOGE("readMap: Key type not a string (parcelType = %d)", value.parcelType()); + return BAD_VALUE; + } + + ret = readValue(&value); + if (ret != NO_ERROR) { + return ret; + } + + (*map_out)[key] = value; + } + + return ret; +} + +status_t Parcel::readNullableMap(std::unique_ptr<binder::Map>* map) const +{ + const size_t start = dataPosition(); + int32_t count; + status_t status = readInt32(&count); + map->reset(); + + if (status != OK || count == -1) { + return status; + } + + setDataPosition(start); + map->reset(new binder::Map()); + + status = readMap(map->get()); + + if (status != OK) { + map->reset(); + } + + return status; +} + + + void Parcel::remove(size_t /*start*/, size_t /*amt*/) { LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!"); @@ -1427,13 +1567,13 @@ status_t readByteVectorInternal(const Parcel* parcel, return status; } - const void* data = parcel->readInplace(size); + T* data = const_cast<T*>(reinterpret_cast<const T*>(parcel->readInplace(size))); if (!data) { status = BAD_VALUE; return status; } - val->resize(size); - memcpy(val->data(), data, size); + val->reserve(size); + val->insert(val->end(), data, data + size); return status; } @@ -1944,6 +2084,10 @@ status_t Parcel::readParcelable(Parcelable* parcelable) const { return parcelable->readFromParcel(this); } +status_t Parcel::readValue(binder::Value* value) const { + return value->readFromParcel(this); +} + int32_t Parcel::readExceptionCode() const { binder::Status status; @@ -1966,7 +2110,7 @@ native_handle* Parcel::readNativeHandle() const } for (int i=0 ; err==NO_ERROR && i<numFds ; i++) { - h->data[i] = dup(readFileDescriptor()); + h->data[i] = fcntl(readFileDescriptor(), F_DUPFD_CLOEXEC, 0); if (h->data[i] < 0) { for (int j = 0; j < i; j++) { close(h->data[j]); @@ -1984,7 +2128,6 @@ native_handle* Parcel::readNativeHandle() const return h; } - int Parcel::readFileDescriptor() const { const flat_binder_object* flat = readObject(true); @@ -1996,6 +2139,17 @@ int Parcel::readFileDescriptor() const return BAD_TYPE; } +int Parcel::readParcelFileDescriptor() const +{ + int32_t hasComm = readInt32(); + int fd = readFileDescriptor(); + if (hasComm != 0) { + // skip + readFileDescriptor(); + } + return fd; +} + status_t Parcel::readUniqueFileDescriptor(base::unique_fd* val) const { int got = readFileDescriptor(); @@ -2004,7 +2158,7 @@ status_t Parcel::readUniqueFileDescriptor(base::unique_fd* val) const return BAD_TYPE; } - val->reset(dup(got)); + val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0)); if (val->get() < 0) { return BAD_VALUE; @@ -2078,11 +2232,15 @@ status_t Parcel::read(FlattenableHelperInterface& val) const status_t err = NO_ERROR; for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) { - fds[i] = dup(this->readFileDescriptor()); - if (fds[i] < 0) { + int fd = this->readFileDescriptor(); + if (fd < 0 || ((fds[i] = fcntl(fd, F_DUPFD_CLOEXEC, 0)) < 0)) { err = BAD_VALUE; - ALOGE("dup() failed in Parcel::read, i is %zu, fds[i] is %d, fd_count is %zu, error: %s", - i, fds[i], fd_count, strerror(errno)); + ALOGE("fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is %zu, fds[i] is %d, fd_count is %zu, error: %s", + i, fds[i], fd_count, strerror(fd < 0 ? -fd : errno)); + // Close all the file descriptors that were dup-ed. + for (size_t j=0; j<i ;j++) { + close(fds[j]); + } } } diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp index e7078baa82..d617b5a179 100644 --- a/libs/binder/PersistableBundle.cpp +++ b/libs/binder/PersistableBundle.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "PersistableBundle" #include <binder/PersistableBundle.h> +#include <private/binder/ParcelValTypes.h> #include <limits> @@ -35,27 +36,13 @@ using android::UNEXPECTED_NULL; using std::map; using std::set; using std::vector; +using namespace ::android::binder; enum { // Keep in sync with BUNDLE_MAGIC in frameworks/base/core/java/android/os/BaseBundle.java. BUNDLE_MAGIC = 0x4C444E42, }; -enum { - // Keep in sync with frameworks/base/core/java/android/os/Parcel.java. - VAL_STRING = 0, - VAL_INTEGER = 1, - VAL_LONG = 6, - VAL_DOUBLE = 8, - VAL_BOOLEAN = 9, - VAL_STRINGARRAY = 14, - VAL_INTARRAY = 18, - VAL_LONGARRAY = 19, - VAL_BOOLEANARRAY = 23, - VAL_PERSISTABLEBUNDLE = 25, - VAL_DOUBLEARRAY = 28, -}; - namespace { template <typename T> bool getValue(const android::String16& key, T* out, const map<android::String16, T>& map) { diff --git a/libs/binder/ProcessInfoService.cpp b/libs/binder/ProcessInfoService.cpp index fb2864355d..8939d9c9b2 100644 --- a/libs/binder/ProcessInfoService.cpp +++ b/libs/binder/ProcessInfoService.cpp @@ -57,6 +57,40 @@ status_t ProcessInfoService::getProcessStatesImpl(size_t length, /*in*/ int32_t* return TIMED_OUT; } +status_t ProcessInfoService::getProcessStatesScoresImpl(size_t length, + /*in*/ int32_t* pids, /*out*/ int32_t* states, + /*out*/ int32_t *scores) { + status_t err = NO_ERROR; + sp<IProcessInfoService> pis; + mProcessInfoLock.lock(); + pis = mProcessInfoService; + mProcessInfoLock.unlock(); + + for (int i = 0; i < BINDER_ATTEMPT_LIMIT; i++) { + + if (pis != NULL) { + err = pis->getProcessStatesAndOomScoresFromPids(length, + /*in*/ pids, /*out*/ states, /*out*/ scores); + if (err == NO_ERROR) return NO_ERROR; // success + if (IInterface::asBinder(pis)->isBinderAlive()) return err; + } + sleep(1); + + mProcessInfoLock.lock(); + if (pis == mProcessInfoService) { + updateBinderLocked(); + } + pis = mProcessInfoService; + mProcessInfoLock.unlock(); + } + + ALOGW("%s: Could not retrieve process states and scores " + "from ProcessInfoService after %d retries.", __FUNCTION__, + BINDER_ATTEMPT_LIMIT); + + return TIMED_OUT; +} + void ProcessInfoService::updateBinderLocked() { const sp<IServiceManager> sm(defaultServiceManager()); if (sm != NULL) { diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 7fcd8710a3..11dd5258a7 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -71,7 +71,22 @@ sp<ProcessState> ProcessState::self() if (gProcess != NULL) { return gProcess; } - gProcess = new ProcessState; + gProcess = new ProcessState("/dev/binder"); + return gProcess; +} + +sp<ProcessState> ProcessState::initWithDriver(const char* driver) +{ + Mutex::Autolock _l(gProcessMutex); + if (gProcess != NULL) { + // Allow for initWithDriver to be called repeatedly with the same + // driver. + if (!strcmp(gProcess->getDriverName().c_str(), driver)) { + return gProcess; + } + LOG_ALWAYS_FATAL("ProcessState was already initialized."); + } + gProcess = new ProcessState(driver); return gProcess; } @@ -353,9 +368,13 @@ void ProcessState::giveThreadPoolName() { androidSetThreadName( makeBinderThreadName().string() ); } -static int open_driver() +String8 ProcessState::getDriverName() { + return mDriverName; +} + +static int open_driver(const char *driver) { - int fd = open("/dev/binder", O_RDWR | O_CLOEXEC); + int fd = open(driver, O_RDWR | O_CLOEXEC); if (fd >= 0) { int vers = 0; status_t result = ioctl(fd, BINDER_VERSION, &vers); @@ -365,7 +384,8 @@ static int open_driver() fd = -1; } if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { - ALOGE("Binder driver protocol does not match user space protocol!"); + ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d", + vers, BINDER_CURRENT_PROTOCOL_VERSION, result); close(fd); fd = -1; } @@ -375,13 +395,14 @@ static int open_driver() ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); } } else { - ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno)); + ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno)); } return fd; } -ProcessState::ProcessState() - : mDriverFD(open_driver()) +ProcessState::ProcessState(const char *driver) + : mDriverName(String8(driver)) + , mDriverFD(open_driver(driver)) , mVMStart(MAP_FAILED) , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER) , mThreadCountDecrement(PTHREAD_COND_INITIALIZER) @@ -402,6 +423,7 @@ ProcessState::ProcessState() ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n"); close(mDriverFD); mDriverFD = -1; + mDriverName.clear(); } } diff --git a/libs/binder/Value.cpp b/libs/binder/Value.cpp new file mode 100644 index 0000000000..fd1dfd5ada --- /dev/null +++ b/libs/binder/Value.cpp @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Value" + +#include <binder/Value.h> + +#include <limits> + +#include <binder/IBinder.h> +#include <binder/Parcel.h> +#include <binder/Map.h> +#include <private/binder/ParcelValTypes.h> +#include <log/log.h> +#include <utils/Errors.h> + +using android::BAD_TYPE; +using android::BAD_VALUE; +using android::NO_ERROR; +using android::UNEXPECTED_NULL; +using android::Parcel; +using android::sp; +using android::status_t; +using std::map; +using std::set; +using std::vector; +using android::binder::Value; +using android::IBinder; +using android::os::PersistableBundle; +using namespace android::binder; + +// ==================================================================== + +#define RETURN_IF_FAILED(calledOnce) \ + do { \ + status_t returnStatus = calledOnce; \ + if (returnStatus) { \ + ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ + return returnStatus; \ + } \ + } while(false) + +// ==================================================================== + +/* These `internal_type_ptr()` functions allow this + * class to work without C++ RTTI support. This technique + * only works properly when called directly from this file, + * but that is OK because that is the only place we will + * be calling them from. */ +template<class T> const void* internal_type_ptr() +{ + static const T *marker; + return (void*)▮ +} + +/* Allows the type to be specified by the argument + * instead of inside angle brackets. */ +template<class T> const void* internal_type_ptr(const T&) +{ + return internal_type_ptr<T>(); +} + +// ==================================================================== + +namespace android { + +namespace binder { + +class Value::ContentBase { +public: + virtual ~ContentBase() = default; + virtual const void* type_ptr() const = 0; + virtual ContentBase * clone() const = 0; + virtual bool operator==(const ContentBase& rhs) const = 0; + +#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO + virtual const std::type_info &type() const = 0; +#endif + + template<typename T> bool get(T* out) const; +}; + +/* This is the actual class that holds the value. */ +template<typename T> class Value::Content : public Value::ContentBase { +public: + Content() = default; + Content(const T & value) : mValue(value) { } + + virtual ~Content() = default; + +#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO + virtual const std::type_info &type() const override + { + return typeid(T); + } +#endif + + virtual const void* type_ptr() const override + { + return internal_type_ptr<T>(); + } + + virtual ContentBase * clone() const override + { + return new Content(mValue); + }; + + virtual bool operator==(const ContentBase& rhs) const override + { + if (type_ptr() != rhs.type_ptr()) { + return false; + } + return mValue == static_cast<const Content<T>* >(&rhs)->mValue; + } + + T mValue; +}; + +template<typename T> bool Value::ContentBase::get(T* out) const +{ + if (internal_type_ptr(*out) != type_ptr()) + { + return false; + } + + *out = static_cast<const Content<T>*>(this)->mValue; + + return true; +} + +// ==================================================================== + +Value::Value() : mContent(NULL) +{ +} + +Value::Value(const Value& value) + : mContent(value.mContent ? value.mContent->clone() : NULL) +{ +} + +Value::~Value() +{ + delete mContent; +} + +bool Value::operator==(const Value& rhs) const +{ + const Value& lhs(*this); + + if (lhs.empty() && rhs.empty()) { + return true; + } + + if ( (lhs.mContent == NULL) + || (rhs.mContent == NULL) + ) { + return false; + } + + return *lhs.mContent == *rhs.mContent; +} + +Value& Value::swap(Value &rhs) +{ + std::swap(mContent, rhs.mContent); + return *this; +} + +Value& Value::operator=(const Value& rhs) +{ + delete mContent; + mContent = rhs.mContent + ? rhs.mContent->clone() + : NULL; + return *this; +} + +bool Value::empty() const +{ + return mContent == NULL; +} + +void Value::clear() +{ + delete mContent; + mContent = NULL; +} + +int32_t Value::parcelType() const +{ + const void* t_info(mContent ? mContent->type_ptr() : NULL); + + if (t_info == internal_type_ptr<bool>()) return VAL_BOOLEAN; + if (t_info == internal_type_ptr<uint8_t>()) return VAL_BYTE; + if (t_info == internal_type_ptr<int32_t>()) return VAL_INTEGER; + if (t_info == internal_type_ptr<int64_t>()) return VAL_LONG; + if (t_info == internal_type_ptr<double>()) return VAL_DOUBLE; + if (t_info == internal_type_ptr<String16>()) return VAL_STRING; + + if (t_info == internal_type_ptr<vector<bool>>()) return VAL_BOOLEANARRAY; + if (t_info == internal_type_ptr<vector<uint8_t>>()) return VAL_BYTEARRAY; + if (t_info == internal_type_ptr<vector<int32_t>>()) return VAL_INTARRAY; + if (t_info == internal_type_ptr<vector<int64_t>>()) return VAL_LONGARRAY; + if (t_info == internal_type_ptr<vector<double>>()) return VAL_DOUBLEARRAY; + if (t_info == internal_type_ptr<vector<String16>>()) return VAL_STRINGARRAY; + + if (t_info == internal_type_ptr<Map>()) return VAL_MAP; + if (t_info == internal_type_ptr<PersistableBundle>()) return VAL_PERSISTABLEBUNDLE; + + return VAL_NULL; +} + +#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO +const std::type_info& Value::type() const +{ + return mContent != NULL + ? mContent->type() + : typeid(void); +} +#endif // ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO + +#define DEF_TYPE_ACCESSORS(T, TYPENAME) \ + bool Value::is ## TYPENAME() const \ + { \ + return mContent \ + ? internal_type_ptr<T>() == mContent->type_ptr() \ + : false; \ + } \ + bool Value::get ## TYPENAME(T* out) const \ + { \ + return mContent \ + ? mContent->get(out) \ + : false; \ + } \ + void Value::put ## TYPENAME(const T& in) \ + { \ + *this = in; \ + } \ + Value& Value::operator=(const T& rhs) \ + { \ + delete mContent; \ + mContent = new Content< T >(rhs); \ + return *this; \ + } \ + Value::Value(const T& value) \ + : mContent(new Content< T >(value)) \ + { } + +DEF_TYPE_ACCESSORS(bool, Boolean) +DEF_TYPE_ACCESSORS(int8_t, Byte) +DEF_TYPE_ACCESSORS(int32_t, Int) +DEF_TYPE_ACCESSORS(int64_t, Long) +DEF_TYPE_ACCESSORS(double, Double) +DEF_TYPE_ACCESSORS(String16, String) + +DEF_TYPE_ACCESSORS(std::vector<bool>, BooleanVector) +DEF_TYPE_ACCESSORS(std::vector<uint8_t>, ByteVector) +DEF_TYPE_ACCESSORS(std::vector<int32_t>, IntVector) +DEF_TYPE_ACCESSORS(std::vector<int64_t>, LongVector) +DEF_TYPE_ACCESSORS(std::vector<double>, DoubleVector) +DEF_TYPE_ACCESSORS(std::vector<String16>, StringVector) + +DEF_TYPE_ACCESSORS(::android::binder::Map, Map) +DEF_TYPE_ACCESSORS(PersistableBundle, PersistableBundle) + +bool Value::getString(String8* out) const +{ + String16 val; + bool ret = getString(&val); + if (ret) { + *out = String8(val); + } + return ret; +} + +bool Value::getString(::std::string* out) const +{ + String8 val; + bool ret = getString(&val); + if (ret) { + *out = val.string(); + } + return ret; +} + +status_t Value::writeToParcel(Parcel* parcel) const +{ + // This implementation needs to be kept in sync with the writeValue + // implementation in frameworks/base/core/java/android/os/Parcel.java + +#define BEGIN_HANDLE_WRITE() \ + do { \ + const void* t_info(mContent?mContent->type_ptr():NULL); \ + if (false) { } +#define HANDLE_WRITE_TYPE(T, TYPEVAL, TYPEMETHOD) \ + else if (t_info == internal_type_ptr<T>()) { \ + RETURN_IF_FAILED(parcel->writeInt32(TYPEVAL)); \ + RETURN_IF_FAILED(parcel->TYPEMETHOD(static_cast<const Content<T>*>(mContent)->mValue)); \ + } +#define HANDLE_WRITE_PARCELABLE(T, TYPEVAL) \ + else if (t_info == internal_type_ptr<T>()) { \ + RETURN_IF_FAILED(parcel->writeInt32(TYPEVAL)); \ + RETURN_IF_FAILED(static_cast<const Content<T>*>(mContent)->mValue.writeToParcel(parcel)); \ + } +#define END_HANDLE_WRITE() \ + else { \ + ALOGE("writeToParcel: Type not supported"); \ + return BAD_TYPE; \ + } \ + } while (false); + + BEGIN_HANDLE_WRITE() + + HANDLE_WRITE_TYPE(bool, VAL_BOOLEAN, writeBool) + HANDLE_WRITE_TYPE(int8_t, VAL_BYTE, writeByte) + HANDLE_WRITE_TYPE(int8_t, VAL_BYTE, writeByte) + HANDLE_WRITE_TYPE(int32_t, VAL_INTEGER, writeInt32) + HANDLE_WRITE_TYPE(int64_t, VAL_LONG, writeInt64) + HANDLE_WRITE_TYPE(double, VAL_DOUBLE, writeDouble) + HANDLE_WRITE_TYPE(String16, VAL_STRING, writeString16) + + HANDLE_WRITE_TYPE(vector<bool>, VAL_BOOLEANARRAY, writeBoolVector) + HANDLE_WRITE_TYPE(vector<uint8_t>, VAL_BYTEARRAY, writeByteVector) + HANDLE_WRITE_TYPE(vector<int8_t>, VAL_BYTEARRAY, writeByteVector) + HANDLE_WRITE_TYPE(vector<int32_t>, VAL_INTARRAY, writeInt32Vector) + HANDLE_WRITE_TYPE(vector<int64_t>, VAL_LONGARRAY, writeInt64Vector) + HANDLE_WRITE_TYPE(vector<double>, VAL_DOUBLEARRAY, writeDoubleVector) + HANDLE_WRITE_TYPE(vector<String16>, VAL_STRINGARRAY, writeString16Vector) + + HANDLE_WRITE_PARCELABLE(PersistableBundle, VAL_PERSISTABLEBUNDLE) + + END_HANDLE_WRITE() + + return NO_ERROR; + +#undef BEGIN_HANDLE_WRITE +#undef HANDLE_WRITE_TYPE +#undef HANDLE_WRITE_PARCELABLE +#undef END_HANDLE_WRITE +} + +status_t Value::readFromParcel(const Parcel* parcel) +{ + // This implementation needs to be kept in sync with the readValue + // implementation in frameworks/base/core/java/android/os/Parcel.javai + +#define BEGIN_HANDLE_READ() \ + switch(value_type) { \ + default: \ + ALOGE("readFromParcel: Parcel type %d is not supported", value_type); \ + return BAD_TYPE; +#define HANDLE_READ_TYPE(T, TYPEVAL, TYPEMETHOD) \ + case TYPEVAL: \ + mContent = new Content<T>(); \ + RETURN_IF_FAILED(parcel->TYPEMETHOD(&static_cast<Content<T>*>(mContent)->mValue)); \ + break; +#define HANDLE_READ_PARCELABLE(T, TYPEVAL) \ + case TYPEVAL: \ + mContent = new Content<T>(); \ + RETURN_IF_FAILED(static_cast<Content<T>*>(mContent)->mValue.readFromParcel(parcel)); \ + break; +#define END_HANDLE_READ() \ + } + + int32_t value_type = VAL_NULL; + + delete mContent; + mContent = NULL; + + RETURN_IF_FAILED(parcel->readInt32(&value_type)); + + BEGIN_HANDLE_READ() + + HANDLE_READ_TYPE(bool, VAL_BOOLEAN, readBool) + HANDLE_READ_TYPE(int8_t, VAL_BYTE, readByte) + HANDLE_READ_TYPE(int32_t, VAL_INTEGER, readInt32) + HANDLE_READ_TYPE(int64_t, VAL_LONG, readInt64) + HANDLE_READ_TYPE(double, VAL_DOUBLE, readDouble) + HANDLE_READ_TYPE(String16, VAL_STRING, readString16) + + HANDLE_READ_TYPE(vector<bool>, VAL_BOOLEANARRAY, readBoolVector) + HANDLE_READ_TYPE(vector<uint8_t>, VAL_BYTEARRAY, readByteVector) + HANDLE_READ_TYPE(vector<int32_t>, VAL_INTARRAY, readInt32Vector) + HANDLE_READ_TYPE(vector<int64_t>, VAL_LONGARRAY, readInt64Vector) + HANDLE_READ_TYPE(vector<double>, VAL_DOUBLEARRAY, readDoubleVector) + HANDLE_READ_TYPE(vector<String16>, VAL_STRINGARRAY, readString16Vector) + + HANDLE_READ_PARCELABLE(PersistableBundle, VAL_PERSISTABLEBUNDLE) + + END_HANDLE_READ() + + return NO_ERROR; + +#undef BEGIN_HANDLE_READ +#undef HANDLE_READ_TYPE +#undef HANDLE_READ_PARCELABLE +#undef END_HANDLE_READ +} + +} // namespace binder + +} // namespace android + +/* vim: set ts=4 sw=4 tw=0 et :*/ diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h index 042927c176..4212776e89 100644 --- a/libs/binder/include/binder/AppOpsManager.h +++ b/libs/binder/include/binder/AppOpsManager.h @@ -91,7 +91,8 @@ public: OP_USE_SIP = 53, OP_PROCESS_OUTGOING_CALLS = 54, OP_USE_FINGERPRINT = 55, - OP_BODY_SENSORS = 56 + OP_BODY_SENSORS = 56, + OP_AUDIO_ACCESSIBILITY_VOLUME = 64, }; AppOpsManager(); diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h new file mode 100644 index 0000000000..5ad218035a --- /dev/null +++ b/libs/binder/include/binder/IActivityManager.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_IACTIVITY_MANAGER_H +#define ANDROID_IACTIVITY_MANAGER_H + +#include <binder/IInterface.h> + +namespace android { + +// ------------------------------------------------------------------------------------ + +class IActivityManager : public IInterface +{ +public: + DECLARE_META_INTERFACE(ActivityManager) + + virtual int openContentUri(const String16& /* stringUri */) = 0; + + enum { + OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + }; +}; + +// ------------------------------------------------------------------------------------ + +}; // namespace android + +#endif // ANDROID_IACTIVITY_MANAGER_H
\ No newline at end of file diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h index 9097cb3bb9..2e6295787d 100644 --- a/libs/binder/include/binder/IBinder.h +++ b/libs/binder/include/binder/IBinder.h @@ -38,6 +38,7 @@ class BpBinder; class IInterface; class Parcel; class IResultReceiver; +class IShellCallback; /** * Base class and low-level protocol for a remotable object. @@ -82,7 +83,7 @@ public: virtual status_t pingBinder() = 0; virtual status_t dump(int fd, const Vector<String16>& args) = 0; static status_t shellCommand(const sp<IBinder>& target, int in, int out, int err, - Vector<String16>& args, + Vector<String16>& args, const sp<IShellCallback>& callback, const sp<IResultReceiver>& resultReceiver); virtual status_t transact( uint32_t code, diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index be72d44759..0f1fe5b670 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -72,24 +72,24 @@ protected: // ---------------------------------------------------------------------- #define DECLARE_META_INTERFACE(INTERFACE) \ - static const android::String16 descriptor; \ - static android::sp<I##INTERFACE> asInterface( \ - const android::sp<android::IBinder>& obj); \ - virtual const android::String16& getInterfaceDescriptor() const; \ + static const ::android::String16 descriptor; \ + static ::android::sp<I##INTERFACE> asInterface( \ + const ::android::sp<::android::IBinder>& obj); \ + virtual const ::android::String16& getInterfaceDescriptor() const; \ I##INTERFACE(); \ virtual ~I##INTERFACE(); \ #define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ - const android::String16 I##INTERFACE::descriptor(NAME); \ - const android::String16& \ + const ::android::String16 I##INTERFACE::descriptor(NAME); \ + const ::android::String16& \ I##INTERFACE::getInterfaceDescriptor() const { \ return I##INTERFACE::descriptor; \ } \ - android::sp<I##INTERFACE> I##INTERFACE::asInterface( \ - const android::sp<android::IBinder>& obj) \ + ::android::sp<I##INTERFACE> I##INTERFACE::asInterface( \ + const ::android::sp<::android::IBinder>& obj) \ { \ - android::sp<I##INTERFACE> intr; \ + ::android::sp<I##INTERFACE> intr; \ if (obj != NULL) { \ intr = static_cast<I##INTERFACE*>( \ obj->queryLocalInterface( \ diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index 7b826d6dcd..245607e74e 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -83,6 +83,7 @@ public: // in to it but doesn't want to acquire locks in its services while in // the background. static void disableBackgroundScheduling(bool disable); + bool backgroundSchedulingDisabled(); // Call blocks until the number of executing binder threads is less than // the maximum number of binder threads threads allowed for this process. diff --git a/libs/binder/include/binder/IShellCallback.h b/libs/binder/include/binder/IShellCallback.h new file mode 100644 index 0000000000..fda9ee6ba7 --- /dev/null +++ b/libs/binder/include/binder/IShellCallback.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_ISHELL_CALLBACK_H +#define ANDROID_ISHELL_CALLBACK_H + +#include <binder/IInterface.h> + +namespace android { + +// ---------------------------------------------------------------------- + +class IShellCallback : public IInterface +{ +public: + DECLARE_META_INTERFACE(ShellCallback); + + virtual int openOutputFile(const String16& path, const String16& seLinuxContext) = 0; + + enum { + OP_OPEN_OUTPUT_FILE = IBinder::FIRST_CALL_TRANSACTION + }; +}; + +// ---------------------------------------------------------------------- + +class BnShellCallback : public BnInterface<IShellCallback> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_ISHELL_CALLBACK_H + diff --git a/libs/binder/include/binder/IpPrefix.h b/libs/binder/include/binder/IpPrefix.h new file mode 100644 index 0000000000..96ebaac437 --- /dev/null +++ b/libs/binder/include/binder/IpPrefix.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_IP_PREFIX_H +#define ANDROID_IP_PREFIX_H + +#include <netinet/in.h> + +#include <binder/Parcelable.h> +#include <utils/String16.h> +#include <utils/StrongPointer.h> + +namespace android { + +namespace net { + +/* + * C++ implementation of the Java class android.net.IpPrefix + */ +class IpPrefix : public Parcelable { +public: + IpPrefix() = default; + virtual ~IpPrefix() = default; + IpPrefix(const IpPrefix& prefix) = default; + + IpPrefix(const struct in6_addr& addr, int32_t plen): + mUnion(addr), mPrefixLength(plen), mIsIpv6(true) { } + + IpPrefix(const struct in_addr& addr, int32_t plen): + mUnion(addr), mPrefixLength(plen), mIsIpv6(false) { } + + bool getAddressAsIn6Addr(struct in6_addr* addr) const; + bool getAddressAsInAddr(struct in_addr* addr) const; + + const struct in6_addr& getAddressAsIn6Addr() const; + const struct in_addr& getAddressAsInAddr() const; + + bool isIpv6() const; + bool isIpv4() const; + + int32_t getPrefixLength() const; + + void setAddress(const struct in6_addr& addr); + void setAddress(const struct in_addr& addr); + + void setPrefixLength(int32_t prefix); + + friend bool operator==(const IpPrefix& lhs, const IpPrefix& rhs); + + friend bool operator!=(const IpPrefix& lhs, const IpPrefix& rhs) { + return !(lhs == rhs); + } + +public: + // Overrides + status_t writeToParcel(Parcel* parcel) const override; + status_t readFromParcel(const Parcel* parcel) override; + +private: + union InternalUnion { + InternalUnion() = default; + InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { }; + InternalUnion(const struct in_addr &addr):mInAddr(addr) { }; + struct in6_addr mIn6Addr; + struct in_addr mInAddr; + } mUnion; + int32_t mPrefixLength; + bool mIsIpv6; +}; + +} // namespace net + +} // namespace android + +#endif // ANDROID_IP_PREFIX_H diff --git a/libs/binder/include/binder/Map.h b/libs/binder/include/binder/Map.h new file mode 100644 index 0000000000..96a4f8a2a5 --- /dev/null +++ b/libs/binder/include/binder/Map.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_MAP_H +#define ANDROID_MAP_H + +#include <map> +#include <string> + +// --------------------------------------------------------------------------- +namespace android { +namespace binder { + +class Value; + +/** + * Convenience typedef for ::std::map<::std::string,::android::binder::Value> + */ +typedef ::std::map<::std::string, Value> Map; + +} // namespace binder +} // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_MAP_H diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index b0d53ef5c8..5d36526cb3 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -31,6 +31,7 @@ #include <binder/IInterface.h> #include <binder/Parcelable.h> +#include <binder/Map.h> // --------------------------------------------------------------------------- namespace android { @@ -43,6 +44,10 @@ class ProcessState; class String8; class TextOutput; +namespace binder { +class Value; +}; + class Parcel { friend class IPCThreadState; public: @@ -67,6 +72,8 @@ public: status_t appendFrom(const Parcel *parcel, size_t start, size_t len); + int compareData(const Parcel& other); + bool allowFds() const; bool pushAllowFds(bool allowFds); void restoreAllowFds(bool lastValue); @@ -153,6 +160,8 @@ public: template<typename T> status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val); template<typename T> + status_t writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val); + template<typename T> status_t writeParcelableVector(const std::vector<T>& val); template<typename T> @@ -160,6 +169,8 @@ public: status_t writeParcelable(const Parcelable& parcelable); + status_t writeValue(const binder::Value& value); + template<typename T> status_t write(const Flattenable<T>& val); @@ -171,21 +182,29 @@ public: template<typename T> status_t writeVectorSize(const std::unique_ptr<std::vector<T>>& val); + status_t writeMap(const binder::Map& map); + status_t writeNullableMap(const std::unique_ptr<binder::Map>& map); + // Place a native_handle into the parcel (the native_handle's file- // descriptors are dup'ed, so it is safe to delete the native_handle // when this function returns). // Doesn't take ownership of the native_handle. status_t writeNativeHandle(const native_handle* handle); - + // Place a file descriptor into the parcel. The given fd must remain // valid for the lifetime of the parcel. // The Parcel does not take ownership of the given fd unless you ask it to. status_t writeFileDescriptor(int fd, bool takeOwnership = false); - + // Place a file descriptor into the parcel. A dup of the fd is made, which // will be closed once the parcel is destroyed. status_t writeDupFileDescriptor(int fd); + // Place a Java "parcel file descriptor" into the parcel. The given fd must remain + // valid for the lifetime of the parcel. + // The Parcel does not take ownership of the given fd unless you ask it to. + status_t writeParcelFileDescriptor(int fd, bool takeOwnership = false); + // Place a file descriptor into the parcel. This will not affect the // semantics of the smart file descriptor. A new descriptor will be // created, and will be closed when the parcel is destroyed. @@ -271,6 +290,8 @@ public: template<typename T> status_t readParcelable(std::unique_ptr<T>* parcelable) const; + status_t readValue(binder::Value* value) const; + template<typename T> status_t readStrongBinder(sp<T>* val) const; @@ -314,6 +335,9 @@ public: template<typename T> status_t resizeOutVector(std::unique_ptr<std::vector<T>>* val) const; + status_t readMap(binder::Map* map)const; + status_t readNullableMap(std::unique_ptr<binder::Map>* map) const; + // Like Parcel.java's readExceptionCode(). Reads the first int32 // off of a Parcel's header, returning 0 or the negative error // code on exceptions, but also deals with skipping over rich @@ -332,6 +356,10 @@ public: // in the parcel, which you do not own -- use dup() to get your own copy. int readFileDescriptor() const; + // Retrieve a Java "parcel file descriptor" from the parcel. This returns the raw fd + // in the parcel, which you do not own -- use dup() to get your own copy. + int readParcelFileDescriptor() const; + // Retrieve a smart file descriptor from the parcel. status_t readUniqueFileDescriptor( base::unique_fd* val) const; @@ -468,6 +496,8 @@ private: #pragma clang diagnostic ignored "-Wweak-vtables" #endif + // FlattenableHelperInterface and FlattenableHelper avoid generating a vtable entry in objects + // following Flattenable template/protocol. class FlattenableHelperInterface { protected: ~FlattenableHelperInterface() { } @@ -482,6 +512,9 @@ private: #pragma clang diagnostic pop #endif + // Concrete implementation of FlattenableHelperInterface that delegates virtual calls to the + // specified class T implementing the Flattenable protocol. It "virtualizes" a compile-time + // protocol. template<typename T> class FlattenableHelper : public FlattenableHelperInterface { friend class Parcel; @@ -855,7 +888,16 @@ status_t Parcel::writeParcelableVector(const std::unique_ptr<std::vector<std::un return this->writeInt32(-1); } - return unsafeWriteTypedVector(*val, &Parcel::writeParcelable); + return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable<T>); +} + +template<typename T> +status_t Parcel::writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) { + if (val.get() == nullptr) { + return this->writeInt32(-1); + } + + return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable<T>); } // --------------------------------------------------------------------------- diff --git a/libs/binder/include/binder/ProcessInfoService.h b/libs/binder/include/binder/ProcessInfoService.h index c5ead20676..0da61ee3cb 100644 --- a/libs/binder/include/binder/ProcessInfoService.h +++ b/libs/binder/include/binder/ProcessInfoService.h @@ -35,6 +35,8 @@ class ProcessInfoService : public Singleton<ProcessInfoService> { ProcessInfoService(); status_t getProcessStatesImpl(size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states); + status_t getProcessStatesScoresImpl(size_t length, /*in*/ int32_t* pids, + /*out*/ int32_t* states, /*out*/ int32_t *scores); void updateBinderLocked(); static const int BINDER_ATTEMPT_LIMIT = 5; @@ -55,6 +57,21 @@ public: /*out*/ states); } + /** + * For each PID in the given "pids" input array, write the current process state + * for that process into the "states" output array, or + * ActivityManager.PROCESS_STATE_NONEXISTENT * to indicate that no process with the given PID + * exists. OoM scores will also be written in the "scores" output array. + * Please also note that clients calling this method need to have + * "GET_PROCESS_STATE_AND_OOM_SCORE" permission. + * + * Returns NO_ERROR if this operation was successful, or a negative error code otherwise. + */ + static status_t getProcessStatesScoresFromPids(size_t length, /*in*/ int32_t* pids, + /*out*/ int32_t* states, /*out*/ int32_t *scores) { + return ProcessInfoService::getInstance().getProcessStatesScoresImpl( + length, /*in*/ pids, /*out*/ states, /*out*/ scores); + } }; // ---------------------------------------------------------------------- diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index ae47bbf69d..f85c2612d4 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -36,6 +36,11 @@ class ProcessState : public virtual RefBase public: static sp<ProcessState> self(); static sp<ProcessState> selfOrNull(); + /* initWithDriver() can be used to configure libbinder to use + * a different binder driver dev node. It must be called *before* + * any call to ProcessState::self(). /dev/binder remains the default. + */ + static sp<ProcessState> initWithDriver(const char *driver); void setContextObject(const sp<IBinder>& object); sp<IBinder> getContextObject(const sp<IBinder>& caller); @@ -65,12 +70,14 @@ public: status_t setThreadPoolMaxThreadCount(size_t maxThreads); void giveThreadPoolName(); + String8 getDriverName(); + ssize_t getKernelReferences(size_t count, uintptr_t* buf); private: friend class IPCThreadState; - ProcessState(); + ProcessState(const char* driver); ~ProcessState(); ProcessState(const ProcessState& o); @@ -84,6 +91,7 @@ private: handle_entry* lookupHandleLocked(int32_t handle); + String8 mDriverName; int mDriverFD; void* mVMStart; diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h new file mode 100644 index 0000000000..3bfd462807 --- /dev/null +++ b/libs/binder/include/binder/SafeInterface.h @@ -0,0 +1,705 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <binder/IInterface.h> +#include <binder/Parcel.h> +#include <cutils/compiler.h> + +// Set to 1 to enable CallStacks when logging errors +#define SI_DUMP_CALLSTACKS 0 +#if SI_DUMP_CALLSTACKS +#include <utils/CallStack.h> +#endif + +#include <utils/NativeHandle.h> + +#include <functional> +#include <type_traits> + +namespace android { +namespace SafeInterface { + +// ParcelHandler is responsible for writing/reading various types to/from a Parcel in a generic way +class ParcelHandler { +public: + explicit ParcelHandler(const char* logTag) : mLogTag(logTag) {} + + // Specializations for types with dedicated handling in Parcel + status_t read(const Parcel& parcel, bool* b) const { + return callParcel("readBool", [&]() { return parcel.readBool(b); }); + } + status_t write(Parcel* parcel, bool b) const { + return callParcel("writeBool", [&]() { return parcel->writeBool(b); }); + } + template <typename E> + typename std::enable_if<std::is_enum<E>::value, status_t>::type read(const Parcel& parcel, + E* e) const { + typename std::underlying_type<E>::type u{}; + status_t result = read(parcel, &u); + *e = static_cast<E>(u); + return result; + } + template <typename E> + typename std::enable_if<std::is_enum<E>::value, status_t>::type write(Parcel* parcel, + E e) const { + return write(parcel, static_cast<typename std::underlying_type<E>::type>(e)); + } + template <typename T> + typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read( + const Parcel& parcel, T* t) const { + return callParcel("read(Flattenable)", [&]() { return parcel.read(*t); }); + } + template <typename T> + typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type write( + Parcel* parcel, const T& t) const { + return callParcel("write(Flattenable)", [&]() { return parcel->write(t); }); + } + template <typename T> + typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read( + const Parcel& parcel, sp<T>* t) const { + *t = new T{}; + return callParcel("read(sp<Flattenable>)", [&]() { return parcel.read(*(t->get())); }); + } + template <typename T> + typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type write( + Parcel* parcel, const sp<T>& t) const { + return callParcel("write(sp<Flattenable>)", [&]() { return parcel->write(*(t.get())); }); + } + template <typename T> + typename std::enable_if<std::is_base_of<LightFlattenable<T>, T>::value, status_t>::type read( + const Parcel& parcel, T* t) const { + return callParcel("read(LightFlattenable)", [&]() { return parcel.read(*t); }); + } + template <typename T> + typename std::enable_if<std::is_base_of<LightFlattenable<T>, T>::value, status_t>::type write( + Parcel* parcel, const T& t) const { + return callParcel("write(LightFlattenable)", [&]() { return parcel->write(t); }); + } + template <typename NH> + typename std::enable_if<std::is_same<NH, sp<NativeHandle>>::value, status_t>::type read( + const Parcel& parcel, NH* nh) { + *nh = NativeHandle::create(parcel.readNativeHandle(), true); + return NO_ERROR; + } + template <typename NH> + typename std::enable_if<std::is_same<NH, sp<NativeHandle>>::value, status_t>::type write( + Parcel* parcel, const NH& nh) { + return callParcel("write(sp<NativeHandle>)", + [&]() { return parcel->writeNativeHandle(nh->handle()); }); + } + template <typename T> + typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type read( + const Parcel& parcel, T* t) const { + return callParcel("readParcelable", [&]() { return parcel.readParcelable(t); }); + } + template <typename T> + typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type write( + Parcel* parcel, const T& t) const { + return callParcel("writeParcelable", [&]() { return parcel->writeParcelable(t); }); + } + status_t read(const Parcel& parcel, String8* str) const { + return callParcel("readString8", [&]() { return parcel.readString8(str); }); + } + status_t write(Parcel* parcel, const String8& str) const { + return callParcel("writeString8", [&]() { return parcel->writeString8(str); }); + } + template <typename T> + typename std::enable_if<std::is_same<IBinder, T>::value, status_t>::type read( + const Parcel& parcel, sp<T>* pointer) const { + return callParcel("readNullableStrongBinder", + [&]() { return parcel.readNullableStrongBinder(pointer); }); + } + template <typename T> + typename std::enable_if<std::is_same<IBinder, T>::value, status_t>::type write( + Parcel* parcel, const sp<T>& pointer) const { + return callParcel("writeStrongBinder", + [&]() { return parcel->writeStrongBinder(pointer); }); + } + template <typename T> + typename std::enable_if<std::is_base_of<IInterface, T>::value, status_t>::type read( + const Parcel& parcel, sp<T>* pointer) const { + return callParcel("readNullableStrongBinder[IInterface]", + [&]() { return parcel.readNullableStrongBinder(pointer); }); + } + template <typename T> + typename std::enable_if<std::is_base_of<IInterface, T>::value, status_t>::type write( + Parcel* parcel, const sp<T>& interface) const { + return write(parcel, IInterface::asBinder(interface)); + } + template <typename T> + typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type read( + const Parcel& parcel, std::vector<T>* v) const { + return callParcel("readParcelableVector", [&]() { return parcel.readParcelableVector(v); }); + } + template <typename T> + typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type write( + Parcel* parcel, const std::vector<T>& v) const { + return callParcel("writeParcelableVector", + [&]() { return parcel->writeParcelableVector(v); }); + } + + // Templates to handle integral types. We use a struct template to require that the called + // function exactly matches the signedness and size of the argument (e.g., the argument isn't + // silently widened). + template <bool isSigned, size_t size, typename I> + struct HandleInt; + template <typename I> + struct HandleInt<true, 4, I> { + static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) { + return handler.callParcel("readInt32", [&]() { return parcel.readInt32(i); }); + } + static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) { + return handler.callParcel("writeInt32", [&]() { return parcel->writeInt32(i); }); + } + }; + template <typename I> + struct HandleInt<false, 4, I> { + static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) { + return handler.callParcel("readUint32", [&]() { return parcel.readUint32(i); }); + } + static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) { + return handler.callParcel("writeUint32", [&]() { return parcel->writeUint32(i); }); + } + }; + template <typename I> + struct HandleInt<true, 8, I> { + static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) { + return handler.callParcel("readInt64", [&]() { return parcel.readInt64(i); }); + } + static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) { + return handler.callParcel("writeInt64", [&]() { return parcel->writeInt64(i); }); + } + }; + template <typename I> + struct HandleInt<false, 8, I> { + static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) { + return handler.callParcel("readUint64", [&]() { return parcel.readUint64(i); }); + } + static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) { + return handler.callParcel("writeUint64", [&]() { return parcel->writeUint64(i); }); + } + }; + template <typename I> + typename std::enable_if<std::is_integral<I>::value, status_t>::type read(const Parcel& parcel, + I* i) const { + return HandleInt<std::is_signed<I>::value, sizeof(I), I>::read(*this, parcel, i); + } + template <typename I> + typename std::enable_if<std::is_integral<I>::value, status_t>::type write(Parcel* parcel, + I i) const { + return HandleInt<std::is_signed<I>::value, sizeof(I), I>::write(*this, parcel, i); + } + +private: + const char* const mLogTag; + + // Helper to encapsulate error handling while calling the various Parcel methods + template <typename Function> + status_t callParcel(const char* name, Function f) const { + status_t error = f(); + if (CC_UNLIKELY(error != NO_ERROR)) { + ALOG(LOG_ERROR, mLogTag, "Failed to %s, (%d: %s)", name, error, strerror(-error)); +#if SI_DUMP_CALLSTACKS + CallStack callStack(mLogTag); +#endif + } + return error; + } +}; + +// Utility struct template which allows us to retrieve the types of the parameters of a member +// function pointer +template <typename T> +struct ParamExtractor; +template <typename Class, typename Return, typename... Params> +struct ParamExtractor<Return (Class::*)(Params...)> { + using ParamTuple = std::tuple<Params...>; +}; +template <typename Class, typename Return, typename... Params> +struct ParamExtractor<Return (Class::*)(Params...) const> { + using ParamTuple = std::tuple<Params...>; +}; + +} // namespace SafeInterface + +template <typename Interface> +class SafeBpInterface : public BpInterface<Interface> { +protected: + SafeBpInterface(const sp<IBinder>& impl, const char* logTag) + : BpInterface<Interface>(impl), mLogTag(logTag) {} + ~SafeBpInterface() override = default; + + // callRemote is used to invoke a synchronous procedure call over Binder + template <typename Method, typename TagType, typename... Args> + status_t callRemote(TagType tag, Args&&... args) const { + static_assert(sizeof(TagType) <= sizeof(uint32_t), "Tag must fit inside uint32_t"); + + // Verify that the arguments are compatible with the parameters + using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple; + static_assert(ArgsMatchParams<std::tuple<Args...>, ParamTuple>::value, + "Invalid argument type"); + + // Write the input arguments to the data Parcel + Parcel data; + data.writeInterfaceToken(this->getInterfaceDescriptor()); + + status_t error = writeInputs(&data, std::forward<Args>(args)...); + if (CC_UNLIKELY(error != NO_ERROR)) { + // A message will have been logged by writeInputs + return error; + } + + // Send the data Parcel to the remote and retrieve the reply parcel + Parcel reply; + error = this->remote()->transact(static_cast<uint32_t>(tag), data, &reply); + if (CC_UNLIKELY(error != NO_ERROR)) { + ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error); +#if SI_DUMP_CALLSTACKS + CallStack callStack(mLogTag); +#endif + return error; + } + + // Read the outputs from the reply Parcel into the output arguments + error = readOutputs(reply, std::forward<Args>(args)...); + if (CC_UNLIKELY(error != NO_ERROR)) { + // A message will have been logged by readOutputs + return error; + } + + // Retrieve the result code from the reply Parcel + status_t result = NO_ERROR; + error = reply.readInt32(&result); + if (CC_UNLIKELY(error != NO_ERROR)) { + ALOG(LOG_ERROR, mLogTag, "Failed to obtain result"); +#if SI_DUMP_CALLSTACKS + CallStack callStack(mLogTag); +#endif + return error; + } + return result; + } + + // callRemoteAsync is used to invoke an asynchronous procedure call over Binder + template <typename Method, typename TagType, typename... Args> + void callRemoteAsync(TagType tag, Args&&... args) const { + static_assert(sizeof(TagType) <= sizeof(uint32_t), "Tag must fit inside uint32_t"); + + // Verify that the arguments are compatible with the parameters + using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple; + static_assert(ArgsMatchParams<std::tuple<Args...>, ParamTuple>::value, + "Invalid argument type"); + + // Write the input arguments to the data Parcel + Parcel data; + data.writeInterfaceToken(this->getInterfaceDescriptor()); + status_t error = writeInputs(&data, std::forward<Args>(args)...); + if (CC_UNLIKELY(error != NO_ERROR)) { + // A message will have been logged by writeInputs + return; + } + + // There will be no data in the reply Parcel since the call is one-way + Parcel reply; + error = this->remote()->transact(static_cast<uint32_t>(tag), data, &reply, + IBinder::FLAG_ONEWAY); + if (CC_UNLIKELY(error != NO_ERROR)) { + ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error); +#if SI_DUMP_CALLSTACKS + CallStack callStack(mLogTag); +#endif + } + } + +private: + const char* const mLogTag; + + // This struct provides information on whether the decayed types of the elements at Index in the + // tuple types T and U (that is, the types after stripping cv-qualifiers, removing references, + // and a few other less common operations) are the same + template <size_t Index, typename T, typename U> + struct DecayedElementsMatch { + private: + using FirstT = typename std::tuple_element<Index, T>::type; + using DecayedT = typename std::decay<FirstT>::type; + using FirstU = typename std::tuple_element<Index, U>::type; + using DecayedU = typename std::decay<FirstU>::type; + + public: + static constexpr bool value = std::is_same<DecayedT, DecayedU>::value; + }; + + // When comparing whether the argument types match the parameter types, we first decay them (see + // DecayedElementsMatch) to avoid falsely flagging, say, T&& against T even though they are + // equivalent enough for our purposes + template <typename T, typename U> + struct ArgsMatchParams {}; + template <typename... Args, typename... Params> + struct ArgsMatchParams<std::tuple<Args...>, std::tuple<Params...>> { + static_assert(sizeof...(Args) <= sizeof...(Params), "Too many arguments"); + static_assert(sizeof...(Args) >= sizeof...(Params), "Not enough arguments"); + + private: + template <size_t Index> + static constexpr typename std::enable_if<(Index < sizeof...(Args)), bool>::type + elementsMatch() { + if (!DecayedElementsMatch<Index, std::tuple<Args...>, std::tuple<Params...>>::value) { + return false; + } + return elementsMatch<Index + 1>(); + } + template <size_t Index> + static constexpr typename std::enable_if<(Index >= sizeof...(Args)), bool>::type + elementsMatch() { + return true; + } + + public: + static constexpr bool value = elementsMatch<0>(); + }; + + // Since we assume that pointer arguments are outputs, we can use this template struct to + // determine whether or not a given argument is fundamentally a pointer type and thus an output + template <typename T> + struct IsPointerIfDecayed { + private: + using Decayed = typename std::decay<T>::type; + + public: + static constexpr bool value = std::is_pointer<Decayed>::value; + }; + + template <typename T> + typename std::enable_if<!IsPointerIfDecayed<T>::value, status_t>::type writeIfInput( + Parcel* data, T&& t) const { + return SafeInterface::ParcelHandler{mLogTag}.write(data, std::forward<T>(t)); + } + template <typename T> + typename std::enable_if<IsPointerIfDecayed<T>::value, status_t>::type writeIfInput( + Parcel* /*data*/, T&& /*t*/) const { + return NO_ERROR; + } + + // This method iterates through all of the arguments, writing them to the data Parcel if they + // are an input (i.e., if they are not a pointer type) + template <typename T, typename... Remaining> + status_t writeInputs(Parcel* data, T&& t, Remaining&&... remaining) const { + status_t error = writeIfInput(data, std::forward<T>(t)); + if (CC_UNLIKELY(error != NO_ERROR)) { + // A message will have been logged by writeIfInput + return error; + } + return writeInputs(data, std::forward<Remaining>(remaining)...); + } + static status_t writeInputs(Parcel* /*data*/) { return NO_ERROR; } + + template <typename T> + typename std::enable_if<IsPointerIfDecayed<T>::value, status_t>::type readIfOutput( + const Parcel& reply, T&& t) const { + return SafeInterface::ParcelHandler{mLogTag}.read(reply, std::forward<T>(t)); + } + template <typename T> + static typename std::enable_if<!IsPointerIfDecayed<T>::value, status_t>::type readIfOutput( + const Parcel& /*reply*/, T&& /*t*/) { + return NO_ERROR; + } + + // Similar to writeInputs except that it reads output arguments from the reply Parcel + template <typename T, typename... Remaining> + status_t readOutputs(const Parcel& reply, T&& t, Remaining&&... remaining) const { + status_t error = readIfOutput(reply, std::forward<T>(t)); + if (CC_UNLIKELY(error != NO_ERROR)) { + // A message will have been logged by readIfOutput + return error; + } + return readOutputs(reply, std::forward<Remaining>(remaining)...); + } + static status_t readOutputs(const Parcel& /*data*/) { return NO_ERROR; } +}; + +template <typename Interface> +class SafeBnInterface : public BnInterface<Interface> { +public: + explicit SafeBnInterface(const char* logTag) : mLogTag(logTag) {} + +protected: + template <typename Method> + status_t callLocal(const Parcel& data, Parcel* reply, Method method) { + CHECK_INTERFACE(this, data, reply); + + // Since we need to both pass inputs into the call as well as retrieve outputs, we create a + // "raw" tuple, where the inputs are interleaved with actual, non-pointer versions of the + // outputs. When we ultimately call into the method, we will pass the addresses of the + // output arguments instead of their tuple members directly, but the storage will live in + // the tuple. + using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple; + typename RawConverter<std::tuple<>, ParamTuple>::type rawArgs{}; + + // Read the inputs from the data Parcel into the argument tuple + status_t error = InputReader<ParamTuple>{mLogTag}.readInputs(data, &rawArgs); + if (CC_UNLIKELY(error != NO_ERROR)) { + // A message will have been logged by read + return error; + } + + // Call the local method + status_t result = MethodCaller<ParamTuple>::call(this, method, &rawArgs); + + // Extract the outputs from the argument tuple and write them into the reply Parcel + error = OutputWriter<ParamTuple>{mLogTag}.writeOutputs(reply, &rawArgs); + if (CC_UNLIKELY(error != NO_ERROR)) { + // A message will have been logged by write + return error; + } + + // Return the result code in the reply Parcel + error = reply->writeInt32(result); + if (CC_UNLIKELY(error != NO_ERROR)) { + ALOG(LOG_ERROR, mLogTag, "Failed to write result"); +#if SI_DUMP_CALLSTACKS + CallStack callStack(mLogTag); +#endif + return error; + } + return NO_ERROR; + } + + template <typename Method> + status_t callLocalAsync(const Parcel& data, Parcel* /*reply*/, Method method) { + // reply is not actually used by CHECK_INTERFACE + CHECK_INTERFACE(this, data, reply); + + // Since we need to both pass inputs into the call as well as retrieve outputs, we create a + // "raw" tuple, where the inputs are interleaved with actual, non-pointer versions of the + // outputs. When we ultimately call into the method, we will pass the addresses of the + // output arguments instead of their tuple members directly, but the storage will live in + // the tuple. + using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple; + typename RawConverter<std::tuple<>, ParamTuple>::type rawArgs{}; + + // Read the inputs from the data Parcel into the argument tuple + status_t error = InputReader<ParamTuple>{mLogTag}.readInputs(data, &rawArgs); + if (CC_UNLIKELY(error != NO_ERROR)) { + // A message will have been logged by read + return error; + } + + // Call the local method + MethodCaller<ParamTuple>::callVoid(this, method, &rawArgs); + + // After calling, there is nothing more to do since asynchronous calls do not return a value + // to the caller + return NO_ERROR; + } + +private: + const char* const mLogTag; + + // RemoveFirst strips the first element from a tuple. + // For example, given T = std::tuple<A, B, C>, RemoveFirst<T>::type = std::tuple<B, C> + template <typename T, typename... Args> + struct RemoveFirst; + template <typename T, typename... Args> + struct RemoveFirst<std::tuple<T, Args...>> { + using type = std::tuple<Args...>; + }; + + // RawConverter strips a tuple down to its fundamental types, discarding both pointers and + // references. This allows us to allocate storage for both input (non-pointer) arguments and + // output (pointer) arguments in one tuple. + // For example, given T = std::tuple<const A&, B*>, RawConverter<T>::type = std::tuple<A, B> + template <typename Unconverted, typename... Converted> + struct RawConverter; + template <typename Unconverted, typename... Converted> + struct RawConverter<std::tuple<Converted...>, Unconverted> { + private: + using ElementType = typename std::tuple_element<0, Unconverted>::type; + using Decayed = typename std::decay<ElementType>::type; + using WithoutPointer = typename std::remove_pointer<Decayed>::type; + + public: + using type = typename RawConverter<std::tuple<Converted..., WithoutPointer>, + typename RemoveFirst<Unconverted>::type>::type; + }; + template <typename... Converted> + struct RawConverter<std::tuple<Converted...>, std::tuple<>> { + using type = std::tuple<Converted...>; + }; + + // This provides a simple way to determine whether the indexed element of Args... is a pointer + template <size_t I, typename... Args> + struct ElementIsPointer { + private: + using ElementType = typename std::tuple_element<I, std::tuple<Args...>>::type; + + public: + static constexpr bool value = std::is_pointer<ElementType>::value; + }; + + // This class iterates over the parameter types, and if a given parameter is an input + // (i.e., is not a pointer), reads the corresponding argument tuple element from the data Parcel + template <typename... Params> + class InputReader; + template <typename... Params> + class InputReader<std::tuple<Params...>> { + public: + explicit InputReader(const char* logTag) : mLogTag(logTag) {} + + // Note that in this case (as opposed to in SafeBpInterface), we iterate using an explicit + // index (starting with 0 here) instead of using recursion and stripping the first element. + // This is because in SafeBpInterface we aren't actually operating on a real tuple, but are + // instead just using a tuple as a convenient container for variadic types, whereas here we + // can't modify the argument tuple without causing unnecessary copies or moves of the data + // contained therein. + template <typename RawTuple> + status_t readInputs(const Parcel& data, RawTuple* args) { + return dispatchArg<0>(data, args); + } + + private: + const char* const mLogTag; + + template <std::size_t I, typename RawTuple> + typename std::enable_if<!ElementIsPointer<I, Params...>::value, status_t>::type readIfInput( + const Parcel& data, RawTuple* args) { + return SafeInterface::ParcelHandler{mLogTag}.read(data, &std::get<I>(*args)); + } + template <std::size_t I, typename RawTuple> + typename std::enable_if<ElementIsPointer<I, Params...>::value, status_t>::type readIfInput( + const Parcel& /*data*/, RawTuple* /*args*/) { + return NO_ERROR; + } + + // Recursively iterate through the arguments + template <std::size_t I, typename RawTuple> + typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg( + const Parcel& data, RawTuple* args) { + status_t error = readIfInput<I>(data, args); + if (CC_UNLIKELY(error != NO_ERROR)) { + // A message will have been logged in read + return error; + } + return dispatchArg<I + 1>(data, args); + } + template <std::size_t I, typename RawTuple> + typename std::enable_if<(I >= sizeof...(Params)), status_t>::type dispatchArg( + const Parcel& /*data*/, RawTuple* /*args*/) { + return NO_ERROR; + } + }; + + // getForCall uses the types of the parameters to determine whether a given element of the + // argument tuple is an input, which should be passed directly into the call, or an output, for + // which its address should be passed into the call + template <size_t I, typename RawTuple, typename... Params> + static typename std::enable_if< + ElementIsPointer<I, Params...>::value, + typename std::tuple_element<I, std::tuple<Params...>>::type>::type + getForCall(RawTuple* args) { + return &std::get<I>(*args); + } + template <size_t I, typename RawTuple, typename... Params> + static typename std::enable_if< + !ElementIsPointer<I, Params...>::value, + typename std::tuple_element<I, std::tuple<Params...>>::type>::type& + getForCall(RawTuple* args) { + return std::get<I>(*args); + } + + // This template class uses std::index_sequence and parameter pack expansion to call the given + // method using the elements of the argument tuple (after those arguments are passed through + // getForCall to get addresses instead of values for output arguments) + template <typename... Params> + struct MethodCaller; + template <typename... Params> + struct MethodCaller<std::tuple<Params...>> { + public: + // The calls through these to the helper methods are necessary to generate the + // std::index_sequences used to unpack the argument tuple into the method call + template <typename Class, typename MemberFunction, typename RawTuple> + static status_t call(Class* instance, MemberFunction function, RawTuple* args) { + return callHelper(instance, function, args, std::index_sequence_for<Params...>{}); + } + template <typename Class, typename MemberFunction, typename RawTuple> + static void callVoid(Class* instance, MemberFunction function, RawTuple* args) { + callVoidHelper(instance, function, args, std::index_sequence_for<Params...>{}); + } + + private: + template <typename Class, typename MemberFunction, typename RawTuple, std::size_t... I> + static status_t callHelper(Class* instance, MemberFunction function, RawTuple* args, + std::index_sequence<I...> /*unused*/) { + return (instance->*function)(getForCall<I, RawTuple, Params...>(args)...); + } + template <typename Class, typename MemberFunction, typename RawTuple, std::size_t... I> + static void callVoidHelper(Class* instance, MemberFunction function, RawTuple* args, + std::index_sequence<I...> /*unused*/) { + (instance->*function)(getForCall<I, RawTuple, Params...>(args)...); + } + }; + + // This class iterates over the parameter types, and if a given parameter is an output + // (i.e., is a pointer), writes the corresponding argument tuple element into the reply Parcel + template <typename... Params> + struct OutputWriter; + template <typename... Params> + struct OutputWriter<std::tuple<Params...>> { + public: + explicit OutputWriter(const char* logTag) : mLogTag(logTag) {} + + // See the note on InputReader::readInputs for why this differs from the arguably simpler + // RemoveFirst approach in SafeBpInterface + template <typename RawTuple> + status_t writeOutputs(Parcel* reply, RawTuple* args) { + return dispatchArg<0>(reply, args); + } + + private: + const char* const mLogTag; + + template <std::size_t I, typename RawTuple> + typename std::enable_if<ElementIsPointer<I, Params...>::value, status_t>::type + writeIfOutput(Parcel* reply, RawTuple* args) { + return SafeInterface::ParcelHandler{mLogTag}.write(reply, std::get<I>(*args)); + } + template <std::size_t I, typename RawTuple> + typename std::enable_if<!ElementIsPointer<I, Params...>::value, status_t>::type + writeIfOutput(Parcel* /*reply*/, RawTuple* /*args*/) { + return NO_ERROR; + } + + // Recursively iterate through the arguments + template <std::size_t I, typename RawTuple> + typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg( + Parcel* reply, RawTuple* args) { + status_t error = writeIfOutput<I>(reply, args); + if (CC_UNLIKELY(error != NO_ERROR)) { + // A message will have been logged in read + return error; + } + return dispatchArg<I + 1>(reply, args); + } + template <std::size_t I, typename RawTuple> + typename std::enable_if<(I >= sizeof...(Params)), status_t>::type dispatchArg( + Parcel* /*reply*/, RawTuple* /*args*/) { + return NO_ERROR; + } + }; +}; + +} // namespace android diff --git a/libs/binder/include/binder/Value.h b/libs/binder/include/binder/Value.h new file mode 100644 index 0000000000..4dee3d86b0 --- /dev/null +++ b/libs/binder/include/binder/Value.h @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_VALUE_H +#define ANDROID_VALUE_H + +#include <stdint.h> +#include <map> +#include <set> +#include <vector> +#include <string> + +#include <binder/Parcelable.h> +#include <binder/PersistableBundle.h> +#include <binder/Map.h> +#include <utils/String8.h> +#include <utils/String16.h> +#include <utils/StrongPointer.h> + +namespace android { + +class Parcel; + +namespace binder { + +/** + * A limited C++ generic type. The purpose of this class is to allow C++ + * programs to make use of (or implement) Binder interfaces which make use + * the Java "Object" generic type (either via the use of the Map type or + * some other mechanism). + * + * This class only supports a limited set of types, but additional types + * may be easily added to this class in the future as needed---without + * breaking binary compatability. + * + * This class was written in such a way as to help avoid type errors by + * giving each type their own explicity-named accessor methods (rather than + * overloaded methods). + * + * When reading or writing this class to a Parcel, use the `writeValue()` + * and `readValue()` methods. + */ +class Value { +public: + Value(); + virtual ~Value(); + + Value& swap(Value &); + + bool empty() const; + + void clear(); + +#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO + const std::type_info& type() const; +#endif + + int32_t parcelType() const; + + bool operator==(const Value& rhs) const; + bool operator!=(const Value& rhs) const { return !this->operator==(rhs); } + + Value(const Value& value); + Value(const bool& value); + Value(const int8_t& value); + Value(const int32_t& value); + Value(const int64_t& value); + Value(const double& value); + Value(const String16& value); + Value(const std::vector<bool>& value); + Value(const std::vector<uint8_t>& value); + Value(const std::vector<int32_t>& value); + Value(const std::vector<int64_t>& value); + Value(const std::vector<double>& value); + Value(const std::vector<String16>& value); + Value(const os::PersistableBundle& value); + Value(const binder::Map& value); + + Value& operator=(const Value& rhs); + Value& operator=(const int8_t& rhs); + Value& operator=(const bool& rhs); + Value& operator=(const int32_t& rhs); + Value& operator=(const int64_t& rhs); + Value& operator=(const double& rhs); + Value& operator=(const String16& rhs); + Value& operator=(const std::vector<bool>& rhs); + Value& operator=(const std::vector<uint8_t>& rhs); + Value& operator=(const std::vector<int32_t>& rhs); + Value& operator=(const std::vector<int64_t>& rhs); + Value& operator=(const std::vector<double>& rhs); + Value& operator=(const std::vector<String16>& rhs); + Value& operator=(const os::PersistableBundle& rhs); + Value& operator=(const binder::Map& rhs); + + void putBoolean(const bool& value); + void putByte(const int8_t& value); + void putInt(const int32_t& value); + void putLong(const int64_t& value); + void putDouble(const double& value); + void putString(const String16& value); + void putBooleanVector(const std::vector<bool>& value); + void putByteVector(const std::vector<uint8_t>& value); + void putIntVector(const std::vector<int32_t>& value); + void putLongVector(const std::vector<int64_t>& value); + void putDoubleVector(const std::vector<double>& value); + void putStringVector(const std::vector<String16>& value); + void putPersistableBundle(const os::PersistableBundle& value); + void putMap(const binder::Map& value); + + bool getBoolean(bool* out) const; + bool getByte(int8_t* out) const; + bool getInt(int32_t* out) const; + bool getLong(int64_t* out) const; + bool getDouble(double* out) const; + bool getString(String16* out) const; + bool getBooleanVector(std::vector<bool>* out) const; + bool getByteVector(std::vector<uint8_t>* out) const; + bool getIntVector(std::vector<int32_t>* out) const; + bool getLongVector(std::vector<int64_t>* out) const; + bool getDoubleVector(std::vector<double>* out) const; + bool getStringVector(std::vector<String16>* out) const; + bool getPersistableBundle(os::PersistableBundle* out) const; + bool getMap(binder::Map* out) const; + + bool isBoolean() const; + bool isByte() const; + bool isInt() const; + bool isLong() const; + bool isDouble() const; + bool isString() const; + bool isBooleanVector() const; + bool isByteVector() const; + bool isIntVector() const; + bool isLongVector() const; + bool isDoubleVector() const; + bool isStringVector() const; + bool isPersistableBundle() const; + bool isMap() const; + + // String Convenience Adapters + // --------------------------- + + Value(const String8& value): Value(String16(value)) { } + Value(const ::std::string& value): Value(String8(value.c_str())) { } + void putString(const String8& value) { return putString(String16(value)); } + void putString(const ::std::string& value) { return putString(String8(value.c_str())); } + Value& operator=(const String8& rhs) { return *this = String16(rhs); } + Value& operator=(const ::std::string& rhs) { return *this = String8(rhs.c_str()); } + bool getString(String8* out) const; + bool getString(::std::string* out) const; + +private: + + // This allows ::android::Parcel to call the two methods below. + friend class ::android::Parcel; + + // This is called by ::android::Parcel::writeValue() + status_t writeToParcel(Parcel* parcel) const; + + // This is called by ::android::Parcel::readValue() + status_t readFromParcel(const Parcel* parcel); + + template<typename T> class Content; + class ContentBase; + + ContentBase* mContent; +}; + +} // namespace binder + +} // namespace android + +#endif // ANDROID_VALUE_H diff --git a/libs/binder/include/private/binder/ParcelValTypes.h b/libs/binder/include/private/binder/ParcelValTypes.h new file mode 100644 index 0000000000..666d22a57f --- /dev/null +++ b/libs/binder/include/private/binder/ParcelValTypes.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace android { +namespace binder { + +// Keep in sync with frameworks/base/core/java/android/os/Parcel.java. +enum { + VAL_NULL = -1, + VAL_STRING = 0, + VAL_INTEGER = 1, + VAL_MAP = 2, + VAL_BUNDLE = 3, + VAL_PARCELABLE = 4, + VAL_SHORT = 5, + VAL_LONG = 6, + VAL_DOUBLE = 8, + VAL_BOOLEAN = 9, + VAL_BYTEARRAY = 13, + VAL_STRINGARRAY = 14, + VAL_IBINDER = 15, + VAL_INTARRAY = 18, + VAL_LONGARRAY = 19, + VAL_BYTE = 20, + VAL_SERIALIZABLE = 21, + VAL_BOOLEANARRAY = 23, + VAL_PERSISTABLEBUNDLE = 25, + VAL_DOUBLEARRAY = 28, +}; + +} // namespace binder +} // namespace android diff --git a/libs/binder/include/private/binder/Static.h b/libs/binder/include/private/binder/Static.h index d104646804..3d10456a8d 100644 --- a/libs/binder/include/private/binder/Static.h +++ b/libs/binder/include/private/binder/Static.h @@ -20,7 +20,6 @@ #include <utils/threads.h> #include <binder/IBinder.h> -#include <binder/IMemory.h> #include <binder/ProcessState.h> #include <binder/IPermissionController.h> #include <binder/IServiceManager.h> diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 4a841499ff..bef9505821 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -33,6 +33,15 @@ cc_test { } cc_test { + name: "binderValueTypeTest", + srcs: ["binderValueTypeTest.cpp"], + shared_libs: [ + "libbinder", + "libutils", + ], +} + +cc_test { name: "binderLibTest", srcs: ["binderLibTest.cpp"], shared_libs: [ @@ -78,3 +87,28 @@ cc_test { "libbase", ], } + +cc_test { + name: "binderSafeInterfaceTest", + srcs: ["binderSafeInterfaceTest.cpp"], + + cppflags: [ + "-Werror", + "-Weverything", + "-Wno-c++98-compat", + "-Wno-c++98-compat-pedantic", + "-Wno-global-constructors", + "-Wno-padded", + "-Wno-weak-vtables", + ], + + cpp_std: "experimental", + gnu_extensions: false, + + shared_libs: [ + "libbinder", + "libcutils", + "liblog", + "libutils", + ], +} diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 54e12b6f37..757291cd2a 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -45,6 +45,7 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_ADD_SERVER, BINDER_LIB_TEST_CALL_BACK, BINDER_LIB_TEST_NOP_CALL_BACK, + BINDER_LIB_TEST_GET_SELF_TRANSACTION, BINDER_LIB_TEST_GET_ID_TRANSACTION, BINDER_LIB_TEST_INDIRECT_TRANSACTION, BINDER_LIB_TEST_SET_ERROR_TRANSACTION, @@ -56,6 +57,7 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_EXIT_TRANSACTION, BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION, BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, + BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, }; pid_t start_server_process(int arg2) @@ -389,7 +391,7 @@ TEST_F(BinderLibTest, IndirectGetId2) ret = reply.readInt32(&count); ASSERT_EQ(NO_ERROR, ret); - EXPECT_EQ(ARRAY_SIZE(serverId), count); + EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count); for (size_t i = 0; i < (size_t)count; i++) { BinderLibTestBundle replyi(&reply); @@ -439,7 +441,7 @@ TEST_F(BinderLibTest, IndirectGetId3) ret = reply.readInt32(&count); ASSERT_EQ(NO_ERROR, ret); - EXPECT_EQ(ARRAY_SIZE(serverId), count); + EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count); for (size_t i = 0; i < (size_t)count; i++) { int32_t counti; @@ -631,7 +633,7 @@ TEST_F(BinderLibTest, PassFile) { } ret = read(pipefd[0], buf, sizeof(buf)); - EXPECT_EQ(sizeof(buf), ret); + EXPECT_EQ(sizeof(buf), (size_t)ret); EXPECT_EQ(write_value, buf[0]); waitForReadData(pipefd[0], 5000); /* wait for other proccess to close pipe */ @@ -670,6 +672,62 @@ TEST_F(BinderLibTest, PromoteRemote) { EXPECT_GE(ret, 0); } +TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) { + status_t ret; + Parcel data, reply; + + ret = m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply); + EXPECT_EQ(NO_ERROR, ret); + + const flat_binder_object *fb = reply.readObject(false); + ASSERT_TRUE(fb != NULL); + EXPECT_EQ(fb->type, BINDER_TYPE_HANDLE); + EXPECT_EQ(ProcessState::self()->getStrongProxyForHandle(fb->handle), m_server); + EXPECT_EQ(fb->cookie, (binder_uintptr_t)0); + EXPECT_EQ(fb->binder >> 32, (binder_uintptr_t)0); +} + +TEST_F(BinderLibTest, FreedBinder) { + status_t ret; + + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != NULL); + + __u32 freedHandle; + wp<IBinder> keepFreedBinder; + { + Parcel data, reply; + data.writeBool(false); /* request weak reference */ + ret = server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply); + ASSERT_EQ(NO_ERROR, ret); + struct flat_binder_object *freed = (struct flat_binder_object *)(reply.data()); + freedHandle = freed->handle; + /* Add a weak ref to the freed binder so the driver does not + * delete its reference to it - otherwise the transaction + * fails regardless of whether the driver is fixed. + */ + keepFreedBinder = reply.readWeakBinder(); + } + { + Parcel data, reply; + data.writeStrongBinder(server); + /* Replace original handle with handle to the freed binder */ + struct flat_binder_object *strong = (struct flat_binder_object *)(data.data()); + __u32 oldHandle = strong->handle; + strong->handle = freedHandle; + ret = server->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data, &reply); + /* Returns DEAD_OBJECT (-32) if target crashes and + * FAILED_TRANSACTION if the driver rejects the invalid + * object. + */ + EXPECT_EQ((status_t)FAILED_TRANSACTION, ret); + /* Restore original handle so parcel destructor does not use + * the wrong handle. + */ + strong->handle = oldHandle; + } +} + class BinderLibTestService : public BBinder { public: @@ -771,6 +829,9 @@ class BinderLibTestService : public BBinder binder->transact(BINDER_LIB_TEST_CALL_BACK, data2, &reply2); return NO_ERROR; } + case BINDER_LIB_TEST_GET_SELF_TRANSACTION: + reply->writeStrongBinder(this); + return NO_ERROR; case BINDER_LIB_TEST_GET_ID_TRANSACTION: reply->writeInt32(m_id); return NO_ERROR; @@ -884,6 +945,16 @@ class BinderLibTestService : public BBinder while (wait(NULL) != -1 || errno != ECHILD) ; exit(EXIT_SUCCESS); + case BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION: { + bool strongRef = data.readBool(); + sp<IBinder> binder = new BBinder(); + if (strongRef) { + reply->writeStrongBinder(binder); + } else { + reply->writeWeakBinder(binder); + } + return NO_ERROR; + } default: return UNKNOWN_TRANSACTION; }; diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp new file mode 100644 index 0000000000..6a16e2496d --- /dev/null +++ b/libs/binder/tests/binderSafeInterfaceTest.cpp @@ -0,0 +1,819 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/SafeInterface.h> + +#include <binder/IInterface.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <binder/ProcessState.h> + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Weverything" +#include <gtest/gtest.h> +#pragma clang diagnostic pop + +#include <utils/LightRefBase.h> +#include <utils/NativeHandle.h> + +#include <cutils/native_handle.h> + +#include <optional> + +#include <sys/eventfd.h> + +using namespace std::chrono_literals; // NOLINT - google-build-using-namespace + +namespace android { +namespace tests { + +enum class TestEnum : uint32_t { + INVALID = 0, + INITIAL = 1, + FINAL = 2, +}; + +// This class serves two purposes: +// 1) It ensures that the implementation doesn't require copying or moving the data (for +// efficiency purposes) +// 2) It tests that Parcelables can be passed correctly +class NoCopyNoMove : public Parcelable { +public: + NoCopyNoMove() = default; + explicit NoCopyNoMove(int32_t value) : mValue(value) {} + ~NoCopyNoMove() override = default; + + // Not copyable + NoCopyNoMove(const NoCopyNoMove&) = delete; + NoCopyNoMove& operator=(const NoCopyNoMove&) = delete; + + // Not movable + NoCopyNoMove(NoCopyNoMove&&) = delete; + NoCopyNoMove& operator=(NoCopyNoMove&&) = delete; + + // Parcelable interface + status_t writeToParcel(Parcel* parcel) const override { return parcel->writeInt32(mValue); } + status_t readFromParcel(const Parcel* parcel) override { return parcel->readInt32(&mValue); } + + int32_t getValue() const { return mValue; } + void setValue(int32_t value) { mValue = value; } + +private: + int32_t mValue = 0; + uint8_t mPadding[4] = {}; // Avoids a warning from -Wpadded +}; + +struct TestFlattenable : Flattenable<TestFlattenable> { + TestFlattenable() = default; + explicit TestFlattenable(int32_t v) : value(v) {} + + // Flattenable protocol + size_t getFlattenedSize() const { return sizeof(value); } + size_t getFdCount() const { return 0; } + status_t flatten(void*& buffer, size_t& size, int*& /*fds*/, size_t& /*count*/) const { + FlattenableUtils::write(buffer, size, value); + return NO_ERROR; + } + status_t unflatten(void const*& buffer, size_t& size, int const*& /*fds*/, size_t& /*count*/) { + FlattenableUtils::read(buffer, size, value); + return NO_ERROR; + } + + int32_t value = 0; +}; + +struct TestLightFlattenable : LightFlattenablePod<TestLightFlattenable> { + TestLightFlattenable() = default; + explicit TestLightFlattenable(int32_t v) : value(v) {} + int32_t value = 0; +}; + +// It seems like this should be able to inherit from TestFlattenable (to avoid duplicating code), +// but the SafeInterface logic can't easily be extended to find an indirect Flattenable<T> +// base class +class TestLightRefBaseFlattenable : public Flattenable<TestLightRefBaseFlattenable>, + public LightRefBase<TestLightRefBaseFlattenable> { +public: + TestLightRefBaseFlattenable() = default; + explicit TestLightRefBaseFlattenable(int32_t v) : value(v) {} + + // Flattenable protocol + size_t getFlattenedSize() const { return sizeof(value); } + size_t getFdCount() const { return 0; } + status_t flatten(void*& buffer, size_t& size, int*& /*fds*/, size_t& /*count*/) const { + FlattenableUtils::write(buffer, size, value); + return NO_ERROR; + } + status_t unflatten(void const*& buffer, size_t& size, int const*& /*fds*/, size_t& /*count*/) { + FlattenableUtils::read(buffer, size, value); + return NO_ERROR; + } + + int32_t value = 0; +}; + +class TestParcelable : public Parcelable { +public: + TestParcelable() = default; + explicit TestParcelable(int32_t value) : mValue(value) {} + TestParcelable(const TestParcelable& other) : TestParcelable(other.mValue) {} + TestParcelable(TestParcelable&& other) : TestParcelable(other.mValue) {} + + // Parcelable interface + status_t writeToParcel(Parcel* parcel) const override { return parcel->writeInt32(mValue); } + status_t readFromParcel(const Parcel* parcel) override { return parcel->readInt32(&mValue); } + + int32_t getValue() const { return mValue; } + void setValue(int32_t value) { mValue = value; } + +private: + int32_t mValue = 0; +}; + +class ExitOnDeath : public IBinder::DeathRecipient { +public: + ~ExitOnDeath() override = default; + + void binderDied(const wp<IBinder>& /*who*/) override { + ALOG(LOG_INFO, "ExitOnDeath", "Exiting"); + exit(0); + } +}; + +// This callback class is used to test both one-way transactions and that sp<IInterface> can be +// passed correctly +class ICallback : public IInterface { +public: + DECLARE_META_INTERFACE(Callback) + + enum class Tag : uint32_t { + OnCallback = IBinder::FIRST_CALL_TRANSACTION, + Last, + }; + + virtual void onCallback(int32_t aPlusOne) = 0; +}; + +class BpCallback : public SafeBpInterface<ICallback> { +public: + explicit BpCallback(const sp<IBinder>& impl) : SafeBpInterface<ICallback>(impl, getLogTag()) {} + + void onCallback(int32_t aPlusOne) override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + return callRemoteAsync<decltype(&ICallback::onCallback)>(Tag::OnCallback, aPlusOne); + } + +private: + static constexpr const char* getLogTag() { return "BpCallback"; } +}; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wexit-time-destructors" +IMPLEMENT_META_INTERFACE(Callback, "android.gfx.tests.ICallback"); +#pragma clang diagnostic pop + +class BnCallback : public SafeBnInterface<ICallback> { +public: + BnCallback() : SafeBnInterface("BnCallback") {} + + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t /*flags*/) override { + EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION); + EXPECT_LT(code, static_cast<uint32_t>(ICallback::Tag::Last)); + ICallback::Tag tag = static_cast<ICallback::Tag>(code); + switch (tag) { + case ICallback::Tag::OnCallback: { + return callLocalAsync(data, reply, &ICallback::onCallback); + } + case ICallback::Tag::Last: + // Should not be possible because of the asserts at the beginning of the method + [&]() { FAIL(); }(); + return UNKNOWN_ERROR; + } + } +}; + +class ISafeInterfaceTest : public IInterface { +public: + DECLARE_META_INTERFACE(SafeInterfaceTest) + + enum class Tag : uint32_t { + SetDeathToken = IBinder::FIRST_CALL_TRANSACTION, + ReturnsNoMemory, + LogicalNot, + ModifyEnum, + IncrementFlattenable, + IncrementLightFlattenable, + IncrementLightRefBaseFlattenable, + IncrementNativeHandle, + IncrementNoCopyNoMove, + IncrementParcelableVector, + ToUpper, + CallMeBack, + IncrementInt32, + IncrementUint32, + IncrementInt64, + IncrementUint64, + IncrementTwo, + Last, + }; + + // This is primarily so that the remote service dies when the test does, but it also serves to + // test the handling of sp<IBinder> and non-const methods + virtual status_t setDeathToken(const sp<IBinder>& token) = 0; + + // This is the most basic test since it doesn't require parceling any arguments + virtual status_t returnsNoMemory() const = 0; + + // These are ordered according to their corresponding methods in SafeInterface::ParcelHandler + virtual status_t logicalNot(bool a, bool* notA) const = 0; + virtual status_t modifyEnum(TestEnum a, TestEnum* b) const = 0; + virtual status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const = 0; + virtual status_t increment(const TestLightFlattenable& a, + TestLightFlattenable* aPlusOne) const = 0; + virtual status_t increment(const sp<TestLightRefBaseFlattenable>& a, + sp<TestLightRefBaseFlattenable>* aPlusOne) const = 0; + virtual status_t increment(const sp<NativeHandle>& a, sp<NativeHandle>* aPlusOne) const = 0; + virtual status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const = 0; + virtual status_t increment(const std::vector<TestParcelable>& a, + std::vector<TestParcelable>* aPlusOne) const = 0; + virtual status_t toUpper(const String8& str, String8* upperStr) const = 0; + // As mentioned above, sp<IBinder> is already tested by setDeathToken + virtual void callMeBack(const sp<ICallback>& callback, int32_t a) const = 0; + virtual status_t increment(int32_t a, int32_t* aPlusOne) const = 0; + virtual status_t increment(uint32_t a, uint32_t* aPlusOne) const = 0; + virtual status_t increment(int64_t a, int64_t* aPlusOne) const = 0; + virtual status_t increment(uint64_t a, uint64_t* aPlusOne) const = 0; + + // This tests that input/output parameter interleaving works correctly + virtual status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, + int32_t* bPlusOne) const = 0; +}; + +class BpSafeInterfaceTest : public SafeBpInterface<ISafeInterfaceTest> { +public: + explicit BpSafeInterfaceTest(const sp<IBinder>& impl) + : SafeBpInterface<ISafeInterfaceTest>(impl, getLogTag()) {} + + status_t setDeathToken(const sp<IBinder>& token) override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + return callRemote<decltype(&ISafeInterfaceTest::setDeathToken)>(Tag::SetDeathToken, token); + } + status_t returnsNoMemory() const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + return callRemote<decltype(&ISafeInterfaceTest::returnsNoMemory)>(Tag::ReturnsNoMemory); + } + status_t logicalNot(bool a, bool* notA) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + return callRemote<decltype(&ISafeInterfaceTest::logicalNot)>(Tag::LogicalNot, a, notA); + } + status_t modifyEnum(TestEnum a, TestEnum* b) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + return callRemote<decltype(&ISafeInterfaceTest::modifyEnum)>(Tag::ModifyEnum, a, b); + } + status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const override { + using Signature = + status_t (ISafeInterfaceTest::*)(const TestFlattenable&, TestFlattenable*) const; + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + return callRemote<Signature>(Tag::IncrementFlattenable, a, aPlusOne); + } + status_t increment(const TestLightFlattenable& a, + TestLightFlattenable* aPlusOne) const override { + using Signature = status_t (ISafeInterfaceTest::*)(const TestLightFlattenable&, + TestLightFlattenable*) const; + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + return callRemote<Signature>(Tag::IncrementLightFlattenable, a, aPlusOne); + } + status_t increment(const sp<TestLightRefBaseFlattenable>& a, + sp<TestLightRefBaseFlattenable>* aPlusOne) const override { + using Signature = status_t (ISafeInterfaceTest::*)(const sp<TestLightRefBaseFlattenable>&, + sp<TestLightRefBaseFlattenable>*) const; + return callRemote<Signature>(Tag::IncrementLightRefBaseFlattenable, a, aPlusOne); + } + status_t increment(const sp<NativeHandle>& a, sp<NativeHandle>* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + using Signature = + status_t (ISafeInterfaceTest::*)(const sp<NativeHandle>&, sp<NativeHandle>*) const; + return callRemote<Signature>(Tag::IncrementNativeHandle, a, aPlusOne); + } + status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + using Signature = status_t (ISafeInterfaceTest::*)(const NoCopyNoMove& a, + NoCopyNoMove* aPlusOne) const; + return callRemote<Signature>(Tag::IncrementNoCopyNoMove, a, aPlusOne); + } + status_t increment(const std::vector<TestParcelable>& a, + std::vector<TestParcelable>* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + using Signature = status_t (ISafeInterfaceTest::*)(const std::vector<TestParcelable>&, + std::vector<TestParcelable>*); + return callRemote<Signature>(Tag::IncrementParcelableVector, a, aPlusOne); + } + status_t toUpper(const String8& str, String8* upperStr) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + return callRemote<decltype(&ISafeInterfaceTest::toUpper)>(Tag::ToUpper, str, upperStr); + } + void callMeBack(const sp<ICallback>& callback, int32_t a) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + return callRemoteAsync<decltype(&ISafeInterfaceTest::callMeBack)>(Tag::CallMeBack, callback, + a); + } + status_t increment(int32_t a, int32_t* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*) const; + return callRemote<Signature>(Tag::IncrementInt32, a, aPlusOne); + } + status_t increment(uint32_t a, uint32_t* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + using Signature = status_t (ISafeInterfaceTest::*)(uint32_t, uint32_t*) const; + return callRemote<Signature>(Tag::IncrementUint32, a, aPlusOne); + } + status_t increment(int64_t a, int64_t* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + using Signature = status_t (ISafeInterfaceTest::*)(int64_t, int64_t*) const; + return callRemote<Signature>(Tag::IncrementInt64, a, aPlusOne); + } + status_t increment(uint64_t a, uint64_t* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const; + return callRemote<Signature>(Tag::IncrementUint64, a, aPlusOne); + } + status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + using Signature = + status_t (ISafeInterfaceTest::*)(int32_t, int32_t*, int32_t, int32_t*) const; + return callRemote<Signature>(Tag::IncrementTwo, a, aPlusOne, b, bPlusOne); + } + +private: + static constexpr const char* getLogTag() { return "BpSafeInterfaceTest"; } +}; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wexit-time-destructors" +IMPLEMENT_META_INTERFACE(SafeInterfaceTest, "android.gfx.tests.ISafeInterfaceTest"); + +static sp<IBinder::DeathRecipient> getDeathRecipient() { + static sp<IBinder::DeathRecipient> recipient = new ExitOnDeath; + return recipient; +} +#pragma clang diagnostic pop + +class BnSafeInterfaceTest : public SafeBnInterface<ISafeInterfaceTest> { +public: + BnSafeInterfaceTest() : SafeBnInterface(getLogTag()) {} + + status_t setDeathToken(const sp<IBinder>& token) override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + token->linkToDeath(getDeathRecipient()); + return NO_ERROR; + } + status_t returnsNoMemory() const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + return NO_MEMORY; + } + status_t logicalNot(bool a, bool* notA) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + *notA = !a; + return NO_ERROR; + } + status_t modifyEnum(TestEnum a, TestEnum* b) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + *b = (a == TestEnum::INITIAL) ? TestEnum::FINAL : TestEnum::INVALID; + return NO_ERROR; + } + status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + aPlusOne->value = a.value + 1; + return NO_ERROR; + } + status_t increment(const TestLightFlattenable& a, + TestLightFlattenable* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + aPlusOne->value = a.value + 1; + return NO_ERROR; + } + status_t increment(const sp<TestLightRefBaseFlattenable>& a, + sp<TestLightRefBaseFlattenable>* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + *aPlusOne = new TestLightRefBaseFlattenable(a->value + 1); + return NO_ERROR; + } + status_t increment(const sp<NativeHandle>& a, sp<NativeHandle>* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + native_handle* rawHandle = native_handle_create(1 /*numFds*/, 1 /*numInts*/); + if (rawHandle == nullptr) return NO_MEMORY; + + // Copy the fd over directly + rawHandle->data[0] = dup(a->handle()->data[0]); + + // Increment the int + rawHandle->data[1] = a->handle()->data[1] + 1; + + // This cannot fail, as it is just the sp<NativeHandle> taking responsibility for closing + // the native_handle when it goes out of scope + *aPlusOne = NativeHandle::create(rawHandle, true); + return NO_ERROR; + } + status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + aPlusOne->setValue(a.getValue() + 1); + return NO_ERROR; + } + status_t increment(const std::vector<TestParcelable>& a, + std::vector<TestParcelable>* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + aPlusOne->resize(a.size()); + for (size_t i = 0; i < a.size(); ++i) { + (*aPlusOne)[i].setValue(a[i].getValue() + 1); + } + return NO_ERROR; + } + status_t toUpper(const String8& str, String8* upperStr) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + *upperStr = str; + upperStr->toUpper(); + return NO_ERROR; + } + void callMeBack(const sp<ICallback>& callback, int32_t a) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + callback->onCallback(a + 1); + } + status_t increment(int32_t a, int32_t* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + *aPlusOne = a + 1; + return NO_ERROR; + } + status_t increment(uint32_t a, uint32_t* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + *aPlusOne = a + 1; + return NO_ERROR; + } + status_t increment(int64_t a, int64_t* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + *aPlusOne = a + 1; + return NO_ERROR; + } + status_t increment(uint64_t a, uint64_t* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + *aPlusOne = a + 1; + return NO_ERROR; + } + status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + *aPlusOne = a + 1; + *bPlusOne = b + 1; + return NO_ERROR; + } + + // BnInterface + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t /*flags*/) override { + EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION); + EXPECT_LT(code, static_cast<uint32_t>(Tag::Last)); + ISafeInterfaceTest::Tag tag = static_cast<ISafeInterfaceTest::Tag>(code); + switch (tag) { + case ISafeInterfaceTest::Tag::SetDeathToken: { + return callLocal(data, reply, &ISafeInterfaceTest::setDeathToken); + } + case ISafeInterfaceTest::Tag::ReturnsNoMemory: { + return callLocal(data, reply, &ISafeInterfaceTest::returnsNoMemory); + } + case ISafeInterfaceTest::Tag::LogicalNot: { + return callLocal(data, reply, &ISafeInterfaceTest::logicalNot); + } + case ISafeInterfaceTest::Tag::ModifyEnum: { + return callLocal(data, reply, &ISafeInterfaceTest::modifyEnum); + } + case ISafeInterfaceTest::Tag::IncrementFlattenable: { + using Signature = status_t (ISafeInterfaceTest::*)(const TestFlattenable& a, + TestFlattenable* aPlusOne) const; + return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment); + } + case ISafeInterfaceTest::Tag::IncrementLightFlattenable: { + using Signature = + status_t (ISafeInterfaceTest::*)(const TestLightFlattenable& a, + TestLightFlattenable* aPlusOne) const; + return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment); + } + case ISafeInterfaceTest::Tag::IncrementLightRefBaseFlattenable: { + using Signature = + status_t (ISafeInterfaceTest::*)(const sp<TestLightRefBaseFlattenable>&, + sp<TestLightRefBaseFlattenable>*) const; + return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment); + } + case ISafeInterfaceTest::Tag::IncrementNativeHandle: { + using Signature = status_t (ISafeInterfaceTest::*)(const sp<NativeHandle>&, + sp<NativeHandle>*) const; + return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment); + } + case ISafeInterfaceTest::Tag::IncrementNoCopyNoMove: { + using Signature = status_t (ISafeInterfaceTest::*)(const NoCopyNoMove& a, + NoCopyNoMove* aPlusOne) const; + return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment); + } + case ISafeInterfaceTest::Tag::IncrementParcelableVector: { + using Signature = + status_t (ISafeInterfaceTest::*)(const std::vector<TestParcelable>&, + std::vector<TestParcelable>*) const; + return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment); + } + case ISafeInterfaceTest::Tag::ToUpper: { + return callLocal(data, reply, &ISafeInterfaceTest::toUpper); + } + case ISafeInterfaceTest::Tag::CallMeBack: { + return callLocalAsync(data, reply, &ISafeInterfaceTest::callMeBack); + } + case ISafeInterfaceTest::Tag::IncrementInt32: { + using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*) const; + return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment); + } + case ISafeInterfaceTest::Tag::IncrementUint32: { + using Signature = status_t (ISafeInterfaceTest::*)(uint32_t, uint32_t*) const; + return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment); + } + case ISafeInterfaceTest::Tag::IncrementInt64: { + using Signature = status_t (ISafeInterfaceTest::*)(int64_t, int64_t*) const; + return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment); + } + case ISafeInterfaceTest::Tag::IncrementUint64: { + using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const; + return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment); + } + case ISafeInterfaceTest::Tag::IncrementTwo: { + using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*, int32_t, + int32_t*) const; + return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment); + } + case ISafeInterfaceTest::Tag::Last: + // Should not be possible because of the asserts at the beginning of the method + [&]() { FAIL(); }(); + return UNKNOWN_ERROR; + } + } + +private: + static constexpr const char* getLogTag() { return "BnSafeInterfaceTest"; } +}; + +class SafeInterfaceTest : public ::testing::Test { +public: + SafeInterfaceTest() : mSafeInterfaceTest(getRemoteService()) { + ProcessState::self()->startThreadPool(); + } + ~SafeInterfaceTest() override = default; + +protected: + sp<ISafeInterfaceTest> mSafeInterfaceTest; + +private: + static constexpr const char* getLogTag() { return "SafeInterfaceTest"; } + + sp<ISafeInterfaceTest> getRemoteService() { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wexit-time-destructors" + static std::mutex sMutex; + static sp<ISafeInterfaceTest> sService; + static sp<IBinder> sDeathToken = new BBinder; +#pragma clang diagnostic pop + + std::unique_lock<decltype(sMutex)> lock; + if (sService == nullptr) { + ALOG(LOG_INFO, getLogTag(), "Forking remote process"); + pid_t forkPid = fork(); + EXPECT_NE(forkPid, -1); + + const String16 serviceName("SafeInterfaceTest"); + + if (forkPid == 0) { + ALOG(LOG_INFO, getLogTag(), "Remote process checking in"); + sp<ISafeInterfaceTest> nativeService = new BnSafeInterfaceTest; + defaultServiceManager()->addService(serviceName, + IInterface::asBinder(nativeService)); + ProcessState::self()->startThreadPool(); + IPCThreadState::self()->joinThreadPool(); + // We shouldn't get to this point + [&]() { FAIL(); }(); + } + + sp<IBinder> binder = defaultServiceManager()->getService(serviceName); + sService = interface_cast<ISafeInterfaceTest>(binder); + EXPECT_TRUE(sService != nullptr); + + sService->setDeathToken(sDeathToken); + } + + return sService; + } +}; + +TEST_F(SafeInterfaceTest, TestReturnsNoMemory) { + status_t result = mSafeInterfaceTest->returnsNoMemory(); + ASSERT_EQ(NO_MEMORY, result); +} + +TEST_F(SafeInterfaceTest, TestLogicalNot) { + const bool a = true; + bool notA = true; + status_t result = mSafeInterfaceTest->logicalNot(a, ¬A); + ASSERT_EQ(NO_ERROR, result); + ASSERT_EQ(!a, notA); + // Test both since we don't want to accidentally catch a default false somewhere + const bool b = false; + bool notB = false; + result = mSafeInterfaceTest->logicalNot(b, ¬B); + ASSERT_EQ(NO_ERROR, result); + ASSERT_EQ(!b, notB); +} + +TEST_F(SafeInterfaceTest, TestModifyEnum) { + const TestEnum a = TestEnum::INITIAL; + TestEnum b = TestEnum::INVALID; + status_t result = mSafeInterfaceTest->modifyEnum(a, &b); + ASSERT_EQ(NO_ERROR, result); + ASSERT_EQ(TestEnum::FINAL, b); +} + +TEST_F(SafeInterfaceTest, TestIncrementFlattenable) { + const TestFlattenable a{1}; + TestFlattenable aPlusOne{0}; + status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); + ASSERT_EQ(NO_ERROR, result); + ASSERT_EQ(a.value + 1, aPlusOne.value); +} + +TEST_F(SafeInterfaceTest, TestIncrementLightFlattenable) { + const TestLightFlattenable a{1}; + TestLightFlattenable aPlusOne{0}; + status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); + ASSERT_EQ(NO_ERROR, result); + ASSERT_EQ(a.value + 1, aPlusOne.value); +} + +TEST_F(SafeInterfaceTest, TestIncrementLightRefBaseFlattenable) { + sp<TestLightRefBaseFlattenable> a = new TestLightRefBaseFlattenable{1}; + sp<TestLightRefBaseFlattenable> aPlusOne; + status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); + ASSERT_EQ(NO_ERROR, result); + ASSERT_NE(nullptr, aPlusOne.get()); + ASSERT_EQ(a->value + 1, aPlusOne->value); +} + +namespace { // Anonymous namespace + +bool fdsAreEquivalent(int a, int b) { + struct stat statA {}; + struct stat statB {}; + if (fstat(a, &statA) != 0) return false; + if (fstat(b, &statB) != 0) return false; + return (statA.st_dev == statB.st_dev) && (statA.st_ino == statB.st_ino); +} + +} // Anonymous namespace + +TEST_F(SafeInterfaceTest, TestIncrementNativeHandle) { + // Create an fd we can use to send and receive from the remote process + base::unique_fd eventFd{eventfd(0 /*initval*/, 0 /*flags*/)}; + ASSERT_NE(-1, eventFd); + + // Determine the maximum number of fds this process can have open + struct rlimit limit {}; + ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &limit)); + uint32_t maxFds = static_cast<uint32_t>(limit.rlim_cur); + + // Perform this test enough times to rule out fd leaks + for (uint32_t iter = 0; iter < (2 * maxFds); ++iter) { + native_handle* handle = native_handle_create(1 /*numFds*/, 1 /*numInts*/); + ASSERT_NE(nullptr, handle); + handle->data[0] = dup(eventFd.get()); + handle->data[1] = 1; + + // This cannot fail, as it is just the sp<NativeHandle> taking responsibility for closing + // the native_handle when it goes out of scope + sp<NativeHandle> a = NativeHandle::create(handle, true); + + sp<NativeHandle> aPlusOne; + status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); + ASSERT_EQ(NO_ERROR, result); + ASSERT_TRUE(fdsAreEquivalent(a->handle()->data[0], aPlusOne->handle()->data[0])); + ASSERT_EQ(a->handle()->data[1] + 1, aPlusOne->handle()->data[1]); + } +} + +TEST_F(SafeInterfaceTest, TestIncrementNoCopyNoMove) { + const NoCopyNoMove a{1}; + NoCopyNoMove aPlusOne{0}; + status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); + ASSERT_EQ(NO_ERROR, result); + ASSERT_EQ(a.getValue() + 1, aPlusOne.getValue()); +} + +TEST_F(SafeInterfaceTest, TestIncremementParcelableVector) { + const std::vector<TestParcelable> a{TestParcelable{1}, TestParcelable{2}}; + std::vector<TestParcelable> aPlusOne; + status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); + ASSERT_EQ(a.size(), aPlusOne.size()); + for (size_t i = 0; i < a.size(); ++i) { + ASSERT_EQ(a[i].getValue() + 1, aPlusOne[i].getValue()); + } +} + +TEST_F(SafeInterfaceTest, TestToUpper) { + const String8 str{"Hello, world!"}; + String8 upperStr; + status_t result = mSafeInterfaceTest->toUpper(str, &upperStr); + ASSERT_EQ(NO_ERROR, result); + ASSERT_TRUE(upperStr == String8{"HELLO, WORLD!"}); +} + +TEST_F(SafeInterfaceTest, TestCallMeBack) { + class CallbackReceiver : public BnCallback { + public: + void onCallback(int32_t aPlusOne) override { + ALOG(LOG_INFO, "CallbackReceiver", "%s", __PRETTY_FUNCTION__); + std::unique_lock<decltype(mMutex)> lock(mMutex); + mValue = aPlusOne; + mCondition.notify_one(); + } + + std::optional<int32_t> waitForCallback() { + std::unique_lock<decltype(mMutex)> lock(mMutex); + bool success = + mCondition.wait_for(lock, 100ms, [&]() { return static_cast<bool>(mValue); }); + return success ? mValue : std::nullopt; + } + + private: + std::mutex mMutex; + std::condition_variable mCondition; + std::optional<int32_t> mValue; + }; + + sp<CallbackReceiver> receiver = new CallbackReceiver; + const int32_t a = 1; + mSafeInterfaceTest->callMeBack(receiver, a); + auto result = receiver->waitForCallback(); + ASSERT_TRUE(result); + ASSERT_EQ(a + 1, *result); +} + +TEST_F(SafeInterfaceTest, TestIncrementInt32) { + const int32_t a = 1; + int32_t aPlusOne = 0; + status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); + ASSERT_EQ(NO_ERROR, result); + ASSERT_EQ(a + 1, aPlusOne); +} + +TEST_F(SafeInterfaceTest, TestIncrementUint32) { + const uint32_t a = 1; + uint32_t aPlusOne = 0; + status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); + ASSERT_EQ(NO_ERROR, result); + ASSERT_EQ(a + 1, aPlusOne); +} + +TEST_F(SafeInterfaceTest, TestIncrementInt64) { + const int64_t a = 1; + int64_t aPlusOne = 0; + status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); + ASSERT_EQ(NO_ERROR, result); + ASSERT_EQ(a + 1, aPlusOne); +} + +TEST_F(SafeInterfaceTest, TestIncrementUint64) { + const uint64_t a = 1; + uint64_t aPlusOne = 0; + status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); + ASSERT_EQ(NO_ERROR, result); + ASSERT_EQ(a + 1, aPlusOne); +} + +TEST_F(SafeInterfaceTest, TestIncrementTwo) { + const int32_t a = 1; + int32_t aPlusOne = 0; + const int32_t b = 2; + int32_t bPlusOne = 0; + status_t result = mSafeInterfaceTest->increment(1, &aPlusOne, 2, &bPlusOne); + ASSERT_EQ(NO_ERROR, result); + ASSERT_EQ(a + 1, aPlusOne); + ASSERT_EQ(b + 1, bPlusOne); +} + +} // namespace tests +} // namespace android diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp index 71b96d4947..6e8f7df84d 100644 --- a/libs/binder/tests/binderThroughputTest.cpp +++ b/libs/binder/tests/binderThroughputTest.cpp @@ -170,6 +170,8 @@ void worker_fx( int num, int worker_count, int iterations, + int payload_size, + bool cs_pair, Pipe p) { // Create BinderWorkerService and for go. @@ -182,22 +184,32 @@ void worker_fx( p.signal(); p.wait(); + // If client/server pairs, then half the workers are + // servers and half are clients + int server_count = cs_pair ? worker_count / 2 : worker_count; + // Get references to other binder services. cout << "Created BinderWorker" << num << endl; (void)worker_count; vector<sp<IBinder> > workers; - for (int i = 0; i < worker_count; i++) { + for (int i = 0; i < server_count; i++) { if (num == i) continue; workers.push_back(serviceMgr->getService(generateServiceName(i))); } - // Run the benchmark. + // Run the benchmark if client ProcResults results; chrono::time_point<chrono::high_resolution_clock> start, end; - for (int i = 0; i < iterations; i++) { - int target = rand() % workers.size(); + for (int i = 0; (!cs_pair || num >= server_count) && i < iterations; i++) { Parcel data, reply; + int target = cs_pair ? num % server_count : rand() % workers.size(); + int sz = payload_size; + + while (sz > sizeof(uint32_t)) { + data.writeInt32(0); + sz -= sizeof(uint32_t); + } start = chrono::high_resolution_clock::now(); status_t ret = workers[target]->transact(BINDER_NOP, data, &reply); end = chrono::high_resolution_clock::now(); @@ -210,6 +222,7 @@ void worker_fx( exit(EXIT_FAILURE); } } + // Signal completion to master and wait. p.signal(); p.wait(); @@ -221,7 +234,7 @@ void worker_fx( exit(EXIT_SUCCESS); } -Pipe make_worker(int num, int iterations, int worker_count) +Pipe make_worker(int num, int iterations, int worker_count, int payload_size, bool cs_pair) { auto pipe_pair = Pipe::createPipePair(); pid_t pid = fork(); @@ -230,7 +243,7 @@ Pipe make_worker(int num, int iterations, int worker_count) return move(get<0>(pipe_pair)); } else { /* child */ - worker_fx(num, worker_count, iterations, move(get<1>(pipe_pair))); + worker_fx(num, worker_count, iterations, payload_size, cs_pair, move(get<1>(pipe_pair))); /* never get here */ return move(get<0>(pipe_pair)); } @@ -255,6 +268,8 @@ int main(int argc, char *argv[]) { int workers = 2; int iterations = 10000; + int payload_size = 0; + bool cs_pair = false; (void)argc; (void)argv; vector<Pipe> pipes; @@ -271,11 +286,21 @@ int main(int argc, char *argv[]) i++; continue; } + if (string(argv[i]) == "-s") { + payload_size = atoi(argv[i+1]); + i++; + } + if (string(argv[i]) == "-p") { + // client/server pairs instead of spreading + // requests to all workers. If true, half + // the workers become clients and half servers + cs_pair = true; + } } // Create all the workers and wait for them to spawn. for (int i = 0; i < workers; i++) { - pipes.push_back(make_worker(i, iterations, workers)); + pipes.push_back(make_worker(i, iterations, workers, payload_size, cs_pair)); } wait_all(pipes); diff --git a/libs/binder/tests/binderValueTypeTest.cpp b/libs/binder/tests/binderValueTypeTest.cpp new file mode 100644 index 0000000000..c8f46977cd --- /dev/null +++ b/libs/binder/tests/binderValueTypeTest.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits> +#include <cstddef> +#include <vector> + +#include "android-base/file.h" +#include "android-base/test_utils.h" +#include <gtest/gtest.h> + +#include <binder/Parcel.h> +#include <binder/Value.h> +#include <binder/Debug.h> + +using ::android::binder::Value; +using ::android::os::PersistableBundle; +using ::android::String16; +using ::std::vector; + +#define VALUE_TYPE_TEST(T, TYPENAME, VAL) \ + TEST(ValueType, Handles ## TYPENAME) { \ + T x = VAL; \ + T y = T(); \ + Value value = VAL; \ + ASSERT_FALSE(value.empty()); \ + ASSERT_TRUE(value.is ## TYPENAME ()); \ + ASSERT_TRUE(value.get ## TYPENAME (&y)); \ + ASSERT_EQ(x, y); \ + ASSERT_EQ(value, Value(y)); \ + value.put ## TYPENAME (x); \ + ASSERT_EQ(value, Value(y)); \ + value = Value(); \ + ASSERT_TRUE(value.empty()); \ + ASSERT_NE(value, Value(y)); \ + value = y; \ + ASSERT_EQ(value, Value(x)); \ + } + +#define VALUE_TYPE_VECTOR_TEST(T, TYPENAME, VAL) \ + TEST(ValueType, Handles ## TYPENAME ## Vector) { \ + vector<T> x; \ + vector<T> y; \ + x.push_back(VAL); \ + x.push_back(T()); \ + Value value(x); \ + ASSERT_FALSE(value.empty()); \ + ASSERT_TRUE(value.is ## TYPENAME ## Vector()); \ + ASSERT_TRUE(value.get ## TYPENAME ## Vector(&y)); \ + ASSERT_EQ(x, y); \ + ASSERT_EQ(value, Value(y)); \ + value.put ## TYPENAME ## Vector(x); \ + ASSERT_EQ(value, Value(y)); \ + value = Value(); \ + ASSERT_TRUE(value.empty()); \ + ASSERT_NE(value, Value(y)); \ + value = y; \ + ASSERT_EQ(value, Value(x)); \ + } + +VALUE_TYPE_TEST(bool, Boolean, true) +VALUE_TYPE_TEST(int32_t, Int, 31337) +VALUE_TYPE_TEST(int64_t, Long, 13370133701337l) +VALUE_TYPE_TEST(double, Double, 3.14159265358979323846) +VALUE_TYPE_TEST(String16, String, String16("Lovely")) + +VALUE_TYPE_VECTOR_TEST(bool, Boolean, true) +VALUE_TYPE_VECTOR_TEST(int32_t, Int, 31337) +VALUE_TYPE_VECTOR_TEST(int64_t, Long, 13370133701337l) +VALUE_TYPE_VECTOR_TEST(double, Double, 3.14159265358979323846) +VALUE_TYPE_VECTOR_TEST(String16, String, String16("Lovely")) + +VALUE_TYPE_TEST(PersistableBundle, PersistableBundle, PersistableBundle()) + +TEST(ValueType, HandlesClear) { + Value value; + ASSERT_TRUE(value.empty()); + value.putInt(31337); + ASSERT_FALSE(value.empty()); + value.clear(); + ASSERT_TRUE(value.empty()); +} + +TEST(ValueType, HandlesSwap) { + Value value_a, value_b; + int32_t int_x; + value_a.putInt(31337); + ASSERT_FALSE(value_a.empty()); + ASSERT_TRUE(value_b.empty()); + value_a.swap(value_b); + ASSERT_FALSE(value_b.empty()); + ASSERT_TRUE(value_a.empty()); + ASSERT_TRUE(value_b.getInt(&int_x)); + ASSERT_EQ(31337, int_x); +} diff --git a/libs/binder/tests/schd-dbg.cpp b/libs/binder/tests/schd-dbg.cpp index fe9e05a4f1..13f03b1ae0 100644 --- a/libs/binder/tests/schd-dbg.cpp +++ b/libs/binder/tests/schd-dbg.cpp @@ -40,7 +40,7 @@ vector<sp<IBinder> > workers; // GOOD_SYNC_MIN is considered as good #define GOOD_SYNC_MIN (0.6) -#define DUMP_PRICISION 3 +#define DUMP_PRESICION 2 string trace_path = "/sys/kernel/debug/tracing"; @@ -246,10 +246,11 @@ struct Results { double worst = (double)m_worst / 1.0E6; double average = (double)m_total_time / m_transactions / 1.0E6; // FIXME: libjson? - cout << std::setprecision(DUMP_PRICISION) << "{ \"avg\":" << setw(5) << left - << average << ", \"wst\":" << setw(5) << left << worst - << ", \"bst\":" << setw(5) << left << best << ", \"miss\":" << setw(5) - << left << m_miss << ", \"meetR\":" << setw(3) << left + int W = DUMP_PRESICION + 2; + cout << setprecision(DUMP_PRESICION) << "{ \"avg\":" << setw(W) << left + << average << ",\"wst\":" << setw(W) << left << worst + << ",\"bst\":" << setw(W) << left << best << ",\"miss\":" << left + << m_miss << ",\"meetR\":" << left << setprecision(DUMP_PRESICION + 3) << (1.0 - (double)m_miss / m_transactions) << "}"; } }; @@ -272,8 +273,15 @@ static void parcel_fill(Parcel& data, int sz, int priority, int cpu) { } } +typedef struct { + void* result; + int target; +} thread_priv_t; + static void* thread_start(void* p) { - Results* results_fifo = (Results*)p; + thread_priv_t* priv = (thread_priv_t*)p; + int target = priv->target; + Results* results_fifo = (Results*)priv->result; Parcel data, reply; Tick sta, end; @@ -281,7 +289,7 @@ static void* thread_start(void* p) { thread_dump("fifo-caller"); sta = tickNow(); - status_t ret = workers[0]->transact(BINDER_NOP, data, &reply); + status_t ret = workers[target]->transact(BINDER_NOP, data, &reply); end = tickNow(); results_fifo->add_time(tickNano(sta, end)); @@ -291,16 +299,19 @@ static void* thread_start(void* p) { } // create a fifo thread to transact and wait it to finished -static void thread_transaction(Results* results_fifo) { +static void thread_transaction(int target, Results* results_fifo) { + thread_priv_t thread_priv; void* dummy; pthread_t thread; pthread_attr_t attr; struct sched_param param; + thread_priv.target = target; + thread_priv.result = results_fifo; ASSERT(!pthread_attr_init(&attr)); ASSERT(!pthread_attr_setschedpolicy(&attr, SCHED_FIFO)); param.sched_priority = sched_get_priority_max(SCHED_FIFO); ASSERT(!pthread_attr_setschedparam(&attr, ¶m)); - ASSERT(!pthread_create(&thread, &attr, &thread_start, results_fifo)); + ASSERT(!pthread_create(&thread, &attr, &thread_start, &thread_priv)); ASSERT(!pthread_join(thread, &dummy)); } @@ -316,7 +327,9 @@ void worker_fx(int num, int no_process, int iterations, int payload_size, sp<IServiceManager> serviceMgr = defaultServiceManager(); sp<BinderWorkerService> service = new BinderWorkerService; serviceMgr->addService(generateServiceName(num), service); + // init done p.signal(); + // wait for kick-off p.wait(); // If client/server pairs, then half the workers are @@ -338,7 +351,7 @@ void worker_fx(int num, int no_process, int iterations, int payload_size, int target = num % server_count; // 1. transaction by fifo thread - thread_transaction(&results_fifo); + thread_transaction(target, &results_fifo); parcel_fill(data, payload_size, thread_pri(), sched_getcpu()); thread_dump("other-caller"); @@ -356,6 +369,7 @@ void worker_fx(int num, int no_process, int iterations, int payload_size, p.wait(); p.send(&dummy); + // wait for kill p.wait(); // Client for each pair dump here if (is_client(num)) { @@ -367,10 +381,10 @@ void worker_fx(int num, int no_process, int iterations, int payload_size, << "\"S\":" << (no_trans - no_sync) << ",\"I\":" << no_trans << "," << "\"R\":" << sync_ratio << "," << endl; - cout << " \"other_ms\":"; + cout << " \"other_ms\":"; results_other.dump(); cout << "," << endl; - cout << " \"fifo_ms\": "; + cout << " \"fifo_ms\": "; results_fifo.dump(); cout << endl; cout << "}," << endl; @@ -463,12 +477,17 @@ int main(int argc, char** argv) { for (int i = 0; i < no_process; i++) { pipes.push_back(make_process(i, iterations, no_process, payload_size)); } + // wait for init done wait_all(pipes); + // kick-off iterations signal_all(pipes); + // wait for completion wait_all(pipes); + // start to send result signal_all(pipes); for (int i = 0; i < no_process; i++) { int status; + // kill pipes[i].signal(); wait(&status); // the exit status is number of transactions without priority inheritance diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 7ac03f19d5..f1d1346366 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -38,9 +38,17 @@ cc_library_shared { // Don't warn about struct padding "-Wno-padded", - // android/sensors.h uses nested anonymous unions and anonymous structs - "-Wno-nested-anon-types", - "-Wno-gnu-anonymous-struct", + // We are aware of the risks inherent in comparing floats for equality + "-Wno-float-equal", + + // Pure abstract classes trigger this warning + "-Wno-weak-vtables", + + // Allow four-character integer literals + "-Wno-four-char-constants", + + // Allow documentation warnings + "-Wno-documentation", "-DDEBUG_ONLY_CODE=0", ], @@ -49,7 +57,7 @@ cc_library_shared { brillo: { cflags: ["-DHAVE_NO_SURFACE_FLINGER"], }, - debuggable: { + eng: { cppflags: [ "-UDEBUG_ONLY_CODE", "-DDEBUG_ONLY_CODE=1", @@ -58,8 +66,6 @@ cc_library_shared { }, srcs: [ - "IGraphicBufferConsumer.cpp", - "IConsumerListener.cpp", "BitTube.cpp", "BufferItem.cpp", "BufferItemConsumer.cpp", @@ -71,43 +77,56 @@ cc_library_shared { "ConsumerBase.cpp", "CpuConsumer.cpp", "DisplayEventReceiver.cpp", + "FrameTimestamps.cpp", "GLConsumer.cpp", - "GraphicBufferAlloc.cpp", - "GraphicsEnv.cpp", "GuiConfig.cpp", "IDisplayEventConnection.cpp", - "IGraphicBufferAlloc.cpp", + "IConsumerListener.cpp", + "IGraphicBufferConsumer.cpp", "IGraphicBufferProducer.cpp", "IProducerListener.cpp", - "ISensorEventConnection.cpp", - "ISensorServer.cpp", "ISurfaceComposer.cpp", "ISurfaceComposerClient.cpp", "LayerState.cpp", "OccupancyTracker.cpp", - "Sensor.cpp", - "SensorEventQueue.cpp", - "SensorManager.cpp", "StreamSplitter.cpp", "Surface.cpp", "SurfaceControl.cpp", "SurfaceComposerClient.cpp", "SyncFeatures.cpp", + "view/Surface.cpp", + "bufferqueue/1.0/B2HProducerListener.cpp", + "bufferqueue/1.0/H2BGraphicBufferProducer.cpp" ], shared_libs: [ - "libnativeloader", + "libsync", "libbinder", "libcutils", "libEGL", "libGLESv2", - "libsync", "libui", "libutils", + "libnativewindow", "liblog", + "libhidlbase", + "libhidltransport", + "android.hidl.token@1.0-utils", + "android.hardware.graphics.bufferqueue@1.0", + "android.hardware.configstore@1.0", + "android.hardware.configstore-utils", ], - export_shared_lib_headers: ["libbinder"], + export_shared_lib_headers: [ + "libbinder", + "libui", + "android.hidl.token@1.0-utils", + "android.hardware.graphics.bufferqueue@1.0", + ], + + export_include_dirs: [ + "include", + ], } subdirs = ["tests"] diff --git a/libs/gui/BitTube.cpp b/libs/gui/BitTube.cpp index b653c5b69c..ef7a6f54d9 100644 --- a/libs/gui/BitTube.cpp +++ b/libs/gui/BitTube.cpp @@ -14,9 +14,11 @@ * limitations under the License. */ +#include <private/gui/BitTube.h> + #include <stdint.h> -#include <sys/types.h> #include <sys/socket.h> +#include <sys/types.h> #include <fcntl.h> #include <unistd.h> @@ -25,46 +27,21 @@ #include <binder/Parcel.h> -#include <gui/BitTube.h> - namespace android { -// ---------------------------------------------------------------------------- +namespace gui { -// Socket buffer size. The default is typically about 128KB, which is much larger than -// we really need. So we make it smaller. +// Socket buffer size. The default is typically about 128KB, which is much larger than we really +// need. So we make it smaller. static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024; - -BitTube::BitTube() - : mSendFd(-1), mReceiveFd(-1) -{ - init(DEFAULT_SOCKET_BUFFER_SIZE, DEFAULT_SOCKET_BUFFER_SIZE); -} - -BitTube::BitTube(size_t bufsize) - : mSendFd(-1), mReceiveFd(-1) -{ +BitTube::BitTube(size_t bufsize) { init(bufsize, bufsize); } -BitTube::BitTube(const Parcel& data) - : mSendFd(-1), mReceiveFd(-1) -{ - mReceiveFd = dup(data.readFileDescriptor()); - if (mReceiveFd < 0) { - mReceiveFd = -errno; - ALOGE("BitTube(Parcel): can't dup filedescriptor (%s)", - strerror(-mReceiveFd)); - } -} +BitTube::BitTube(DefaultSizeType) : BitTube(DEFAULT_SOCKET_BUFFER_SIZE) {} -BitTube::~BitTube() -{ - if (mSendFd >= 0) - close(mSendFd); - - if (mReceiveFd >= 0) - close(mReceiveFd); +BitTube::BitTube(const Parcel& data) { + readFromParcel(&data); } void BitTube::init(size_t rcvbuf, size_t sndbuf) { @@ -73,39 +50,43 @@ void BitTube::init(size_t rcvbuf, size_t sndbuf) { size_t size = DEFAULT_SOCKET_BUFFER_SIZE; setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)); - // sine we don't use the "return channel", we keep it small... + // since we don't use the "return channel", we keep it small... setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); fcntl(sockets[0], F_SETFL, O_NONBLOCK); fcntl(sockets[1], F_SETFL, O_NONBLOCK); - mReceiveFd = sockets[0]; - mSendFd = sockets[1]; + mReceiveFd.reset(sockets[0]); + mSendFd.reset(sockets[1]); } else { - mReceiveFd = -errno; - ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd)); + mReceiveFd.reset(); + ALOGE("BitTube: pipe creation failed (%s)", strerror(errno)); } } -status_t BitTube::initCheck() const -{ +status_t BitTube::initCheck() const { if (mReceiveFd < 0) { return status_t(mReceiveFd); } return NO_ERROR; } -int BitTube::getFd() const -{ +int BitTube::getFd() const { return mReceiveFd; } -int BitTube::getSendFd() const -{ +int BitTube::getSendFd() const { return mSendFd; } -ssize_t BitTube::write(void const* vaddr, size_t size) -{ +base::unique_fd BitTube::moveReceiveFd() { + return std::move(mReceiveFd); +} + +void BitTube::setReceiveFd(base::unique_fd&& receiveFd) { + mReceiveFd = std::move(receiveFd); +} + +ssize_t BitTube::write(void const* vaddr, size_t size) { ssize_t err, len; do { len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL); @@ -115,62 +96,66 @@ ssize_t BitTube::write(void const* vaddr, size_t size) return err == 0 ? len : -err; } -ssize_t BitTube::read(void* vaddr, size_t size) -{ +ssize_t BitTube::read(void* vaddr, size_t size) { ssize_t err, len; do { len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT); err = len < 0 ? errno : 0; } while (err == EINTR); if (err == EAGAIN || err == EWOULDBLOCK) { - // EAGAIN means that we have non-blocking I/O but there was - // no data to be read. Nothing the client should care about. + // EAGAIN means that we have non-blocking I/O but there was no data to be read. Nothing the + // client should care about. return 0; } return err == 0 ? len : -err; } -status_t BitTube::writeToParcel(Parcel* reply) const -{ - if (mReceiveFd < 0) - return -EINVAL; +status_t BitTube::writeToParcel(Parcel* reply) const { + if (mReceiveFd < 0) return -EINVAL; status_t result = reply->writeDupFileDescriptor(mReceiveFd); - close(mReceiveFd); - mReceiveFd = -1; + mReceiveFd.reset(); return result; } +status_t BitTube::readFromParcel(const Parcel* parcel) { + mReceiveFd.reset(dup(parcel->readFileDescriptor())); + if (mReceiveFd < 0) { + mReceiveFd.reset(); + int error = errno; + ALOGE("BitTube::readFromParcel: can't dup file descriptor (%s)", strerror(error)); + return -error; + } + return NO_ERROR; +} -ssize_t BitTube::sendObjects(const sp<BitTube>& tube, - void const* events, size_t count, size_t objSize) -{ +ssize_t BitTube::sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize) { const char* vaddr = reinterpret_cast<const char*>(events); - ssize_t size = tube->write(vaddr, count*objSize); + ssize_t size = tube->write(vaddr, count * objSize); // should never happen because of SOCK_SEQPACKET LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)), - "BitTube::sendObjects(count=%zu, size=%zu), res=%zd (partial events were sent!)", - count, objSize, size); + "BitTube::sendObjects(count=%zu, size=%zu), res=%zd (partial events were " + "sent!)", + count, objSize, size); - //ALOGE_IF(size<0, "error %d sending %d events", size, count); + // ALOGE_IF(size<0, "error %d sending %d events", size, count); return size < 0 ? size : size / static_cast<ssize_t>(objSize); } -ssize_t BitTube::recvObjects(const sp<BitTube>& tube, - void* events, size_t count, size_t objSize) -{ +ssize_t BitTube::recvObjects(BitTube* tube, void* events, size_t count, size_t objSize) { char* vaddr = reinterpret_cast<char*>(events); - ssize_t size = tube->read(vaddr, count*objSize); + ssize_t size = tube->read(vaddr, count * objSize); // should never happen because of SOCK_SEQPACKET LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)), - "BitTube::recvObjects(count=%zu, size=%zu), res=%zd (partial events were received!)", - count, objSize, size); + "BitTube::recvObjects(count=%zu, size=%zu), res=%zd (partial events were " + "received!)", + count, objSize, size); - //ALOGE_IF(size<0, "error %d receiving %d events", size, count); + // ALOGE_IF(size<0, "error %d receiving %d events", size, count); return size < 0 ? size : size / static_cast<ssize_t>(objSize); } -// ---------------------------------------------------------------------------- -}; // namespace android +} // namespace gui +} // namespace android diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp index 1357a4aa1a..69b5962441 100644 --- a/libs/gui/BufferItem.cpp +++ b/libs/gui/BufferItem.cpp @@ -81,6 +81,9 @@ size_t BufferItem::getPodSize() const { addAligned(size, mIsDroppable); addAligned(size, mAcquireCalled); addAligned(size, mTransformToDisplayInverse); + addAligned(size, mAutoRefresh); + addAligned(size, mQueuedBuffer); + addAligned(size, mIsStale); return size; } @@ -88,11 +91,11 @@ size_t BufferItem::getFlattenedSize() const { size_t size = sizeof(uint32_t); // Flags if (mGraphicBuffer != 0) { size += mGraphicBuffer->getFlattenedSize(); - FlattenableUtils::align<4>(size); + size = FlattenableUtils::align<4>(size); } if (mFence != 0) { size += mFence->getFlattenedSize(); - FlattenableUtils::align<4>(size); + size = FlattenableUtils::align<4>(size); } size += mSurfaceDamage.getFlattenedSize(); size = FlattenableUtils::align<8>(size); @@ -166,6 +169,9 @@ status_t BufferItem::flatten( writeAligned(buffer, size, mIsDroppable); writeAligned(buffer, size, mAcquireCalled); writeAligned(buffer, size, mTransformToDisplayInverse); + writeAligned(buffer, size, mAutoRefresh); + writeAligned(buffer, size, mQueuedBuffer); + writeAligned(buffer, size, mIsStale); return NO_ERROR; } @@ -198,6 +204,8 @@ status_t BufferItem::unflatten( status_t err = mFence->unflatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer); + + mFenceTime = std::make_shared<FenceTime>(mFence); } status_t err = mSurfaceDamage.unflatten(buffer, size); @@ -227,6 +235,9 @@ status_t BufferItem::unflatten( readAligned(buffer, size, mIsDroppable); readAligned(buffer, size, mAcquireCalled); readAligned(buffer, size, mTransformToDisplayInverse); + readAligned(buffer, size, mAutoRefresh); + readAligned(buffer, size, mQueuedBuffer); + readAligned(buffer, size, mIsStale); return NO_ERROR; } diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index 3491043811..d9d50dbeb4 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -22,7 +22,7 @@ #include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> -//#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) +#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) //#define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) //#define BI_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) //#define BI_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) @@ -57,6 +57,12 @@ void BufferItemConsumer::setName(const String8& name) { mConsumer->setConsumerName(name); } +void BufferItemConsumer::setBufferFreedListener( + const wp<BufferFreedListener>& listener) { + Mutex::Autolock _l(mMutex); + mBufferFreedListener = listener; +} + status_t BufferItemConsumer::acquireBuffer(BufferItem *item, nsecs_t presentWhen, bool waitForFence) { status_t err; @@ -104,4 +110,14 @@ status_t BufferItemConsumer::releaseBuffer(const BufferItem &item, return err; } +void BufferItemConsumer::freeBufferLocked(int slotIndex) { + sp<BufferFreedListener> listener = mBufferFreedListener.promote(); + if (listener != NULL && mSlots[slotIndex].mGraphicBuffer != NULL) { + // Fire callback if we have a listener registered and the buffer being freed is valid. + BI_LOGV("actually calling onBufferFreed"); + listener->onBufferFreed(mSlots[slotIndex].mGraphicBuffer); + } + ConsumerBase::freeBufferLocked(slotIndex); +} + } // namespace android diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index 6de98f5a25..41512127f2 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -31,6 +31,13 @@ BufferQueue::ProxyConsumerListener::ProxyConsumerListener( BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {} +void BufferQueue::ProxyConsumerListener::onDisconnect() { + sp<ConsumerListener> listener(mConsumerListener.promote()); + if (listener != NULL) { + listener->onDisconnect(); + } +} + void BufferQueue::ProxyConsumerListener::onFrameAvailable( const BufferItem& item) { sp<ConsumerListener> listener(mConsumerListener.promote()); @@ -61,28 +68,28 @@ void BufferQueue::ProxyConsumerListener::onSidebandStreamChanged() { } } -bool BufferQueue::ProxyConsumerListener::getFrameTimestamps( - uint64_t frameNumber, FrameTimestamps* outTimestamps) const { +void BufferQueue::ProxyConsumerListener::addAndGetFrameTimestamps( + const NewFrameEventsEntry* newTimestamps, + FrameEventHistoryDelta* outDelta) { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { - return listener->getFrameTimestamps(frameNumber, outTimestamps); + if (listener != nullptr) { + listener->addAndGetFrameTimestamps(newTimestamps, outDelta); } - return false; } void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer, - const sp<IGraphicBufferAlloc>& allocator) { + bool consumerIsSurfaceFlinger) { LOG_ALWAYS_FATAL_IF(outProducer == NULL, "BufferQueue: outProducer must not be NULL"); LOG_ALWAYS_FATAL_IF(outConsumer == NULL, "BufferQueue: outConsumer must not be NULL"); - sp<BufferQueueCore> core(new BufferQueueCore(allocator)); + sp<BufferQueueCore> core(new BufferQueueCore()); LOG_ALWAYS_FATAL_IF(core == NULL, "BufferQueue: failed to create BufferQueueCore"); - sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core)); + sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger)); LOG_ALWAYS_FATAL_IF(producer == NULL, "BufferQueue: failed to create BufferQueueProducer"); diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index ee4c58cfce..5e5de443f4 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -205,6 +205,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, // was cached when it was last queued. outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer; outBuffer->mFence = Fence::NO_FENCE; + outBuffer->mFenceTime = FenceTime::NO_FENCE; outBuffer->mCrop = mCore->mSharedBufferCache.crop; outBuffer->mTransform = mCore->mSharedBufferCache.transform & ~static_cast<uint32_t>( @@ -674,12 +675,13 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( return NO_ERROR; } -void BufferQueueConsumer::setConsumerName(const String8& name) { +status_t BufferQueueConsumer::setConsumerName(const String8& name) { ATRACE_CALL(); BQ_LOGV("setConsumerName: '%s'", name.string()); Mutex::Autolock lock(mCore->mMutex); mCore->mConsumerName = name; mConsumerName = name; + return NO_ERROR; } status_t BufferQueueConsumer::setDefaultBufferFormat(PixelFormat defaultFormat) { @@ -707,6 +709,14 @@ status_t BufferQueueConsumer::setConsumerUsageBits(uint32_t usage) { return NO_ERROR; } +status_t BufferQueueConsumer::setConsumerIsProtected(bool isProtected) { + ATRACE_CALL(); + BQ_LOGV("setConsumerIsProtected: %s", isProtected ? "true" : "false"); + Mutex::Autolock lock(mCore->mMutex); + mCore->mConsumerIsProtected = isProtected; + return NO_ERROR; +} + status_t BufferQueueConsumer::setTransformHint(uint32_t hint) { ATRACE_CALL(); BQ_LOGV("setTransformHint: %#x", hint); @@ -715,9 +725,10 @@ status_t BufferQueueConsumer::setTransformHint(uint32_t hint) { return NO_ERROR; } -sp<NativeHandle> BufferQueueConsumer::getSidebandStream() const { +status_t BufferQueueConsumer::getSidebandStream(sp<NativeHandle>* outStream) const { Mutex::Autolock lock(mCore->mMutex); - return mCore->mSidebandStream; + *outStream = mCore->mSidebandStream; + return NO_ERROR; } status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush, @@ -733,20 +744,22 @@ status_t BufferQueueConsumer::discardFreeBuffers() { return NO_ERROR; } -void BufferQueueConsumer::dumpState(String8& result, const char* prefix) const { +status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResult) const { const IPCThreadState* ipc = IPCThreadState::self(); const pid_t pid = ipc->getCallingPid(); const uid_t uid = ipc->getCallingUid(); if ((uid != AID_SHELL) && !PermissionCache::checkPermission(String16( "android.permission.DUMP"), pid, uid)) { - result.appendFormat("Permission Denial: can't dump BufferQueueConsumer " + outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer " "from pid=%d, uid=%d\n", pid, uid); android_errorWriteWithInfoLog(0x534e4554, "27046057", static_cast<int32_t>(uid), NULL, 0); - } else { - mCore->dumpState(result, prefix); + return PERMISSION_DENIED; } + + mCore->dumpState(prefix, outResult); + return NO_ERROR; } } // namespace android diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index 6e6cce28eb..cfb25e0503 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -29,12 +29,11 @@ #include <inttypes.h> #include <cutils/properties.h> +#include <cutils/atomic.h> #include <gui/BufferItem.h> #include <gui/BufferQueueCore.h> -#include <gui/GraphicBufferAlloc.h> #include <gui/IConsumerListener.h> -#include <gui/IGraphicBufferAlloc.h> #include <gui/IProducerListener.h> #include <gui/ISurfaceComposer.h> #include <private/gui/ComposerService.h> @@ -53,14 +52,14 @@ static uint64_t getUniqueId() { return id | counter++; } -BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) : - mAllocator(allocator), +BufferQueueCore::BufferQueueCore() : mMutex(), mIsAbandoned(false), mConsumerControlledByApp(false), mConsumerName(getUniqueName()), mConsumerListener(), mConsumerUsageBits(0), + mConsumerIsProtected(false), mConnectedApi(NO_CONNECTED_API), mLinkedToDeath(), mConnectedProducerListener(), @@ -96,30 +95,6 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) : mLastQueuedSlot(INVALID_BUFFER_SLOT), mUniqueId(getUniqueId()) { - if (allocator == NULL) { - -#ifdef HAVE_NO_SURFACE_FLINGER - // Without a SurfaceFlinger, allocate in-process. This only makes - // sense in systems with static SELinux configurations and no - // applications (since applications need dynamic SELinux policy). - mAllocator = new GraphicBufferAlloc(); -#else - // Run time check for headless, where we also allocate in-process. - char value[PROPERTY_VALUE_MAX]; - property_get("config.headless", value, "0"); - if (atoi(value) == 1) { - mAllocator = new GraphicBufferAlloc(); - } else { - sp<ISurfaceComposer> composer(ComposerService::getComposerService()); - mAllocator = composer->createGraphicBufferAlloc(); - } -#endif // HAVE_NO_SURFACE_FLINGER - - if (mAllocator == NULL) { - BQ_LOGE("createGraphicBufferAlloc failed"); - } - } - int numStartingBuffers = getMaxBufferCountLocked(); for (int s = 0; s < numStartingBuffers; s++) { mFreeSlots.insert(s); @@ -132,7 +107,7 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) : BufferQueueCore::~BufferQueueCore() {} -void BufferQueueCore::dumpState(String8& result, const char* prefix) const { +void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const { Mutex::Autolock lock(mMutex); String8 fifo; @@ -147,10 +122,10 @@ void BufferQueueCore::dumpState(String8& result, const char* prefix) const { ++current; } - result.appendFormat("%s-BufferQueue mMaxAcquiredBufferCount=%d, " + outResult->appendFormat("%s-BufferQueue mMaxAcquiredBufferCount=%d, " "mMaxDequeuedBufferCount=%d, mDequeueBufferCannotBlock=%d " "mAsyncMode=%d, default-size=[%dx%d], default-format=%d, " - "transform-hint=%02x, FIFO(%zu)={%s}\n", prefix, + "transform-hint=%02x, FIFO(%zu)={%s}\n", prefix.string(), mMaxAcquiredBufferCount, mMaxDequeuedBufferCount, mDequeueBufferCannotBlock, mAsyncMode, mDefaultWidth, mDefaultHeight, mDefaultBufferFormat, mTransformHint, mQueue.size(), @@ -160,28 +135,28 @@ void BufferQueueCore::dumpState(String8& result, const char* prefix) const { const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer); // A dequeued buffer might be null if it's still being allocated if (buffer.get()) { - result.appendFormat("%s%s[%02d:%p] state=%-8s, %p " - "[%4ux%4u:%4u,%3X]\n", prefix, + outResult->appendFormat("%s%s[%02d:%p] state=%-8s, %p " + "[%4ux%4u:%4u,%3X]\n", prefix.string(), (mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s, buffer.get(), mSlots[s].mBufferState.string(), buffer->handle, buffer->width, buffer->height, buffer->stride, buffer->format); } else { - result.appendFormat("%s [%02d:%p] state=%-8s\n", prefix, s, + outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.string(), s, buffer.get(), mSlots[s].mBufferState.string()); } } for (int s : mFreeBuffers) { const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer); - result.appendFormat("%s [%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n", - prefix, s, buffer.get(), mSlots[s].mBufferState.string(), + outResult->appendFormat("%s [%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n", + prefix.string(), s, buffer.get(), mSlots[s].mBufferState.string(), buffer->handle, buffer->width, buffer->height, buffer->stride, buffer->format); } for (int s : mFreeSlots) { const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer); - result.appendFormat("%s [%02d:%p] state=%-8s\n", prefix, s, + outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.string(), s, buffer.get(), mSlots[s].mBufferState.string()); } } @@ -260,6 +235,13 @@ void BufferQueueCore::freeAllBuffersLocked() { for (auto& b : mQueue) { b.mIsStale = true; + + // We set this to false to force the BufferQueue to resend the buffer + // handle upon acquire, since if we're here due to a producer + // disconnect, the consumer will have been told to purge its cache of + // slot-to-buffer-handle mappings and will not be able to otherwise + // obtain a valid buffer handle. + b.mAcquireCalled = false; } VALIDATE_CONSISTENCY(); diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index f0e701e9f3..838586411d 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -34,7 +34,6 @@ #include <gui/BufferQueueProducer.h> #include <gui/GLConsumer.h> #include <gui/IConsumerListener.h> -#include <gui/IGraphicBufferAlloc.h> #include <gui/IProducerListener.h> #include <utils/Log.h> @@ -42,12 +41,17 @@ namespace android { -BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core) : +static constexpr uint32_t BQ_LAYER_COUNT = 1; + +BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core, + bool consumerIsSurfaceFlinger) : mCore(core), mSlots(core->mSlots), mConsumerName(), mStickyTransform(0), + mConsumerIsSurfaceFlinger(consumerIsSurfaceFlinger), mLastQueueBufferFence(Fence::NO_FENCE), + mLastQueuedTransform(0), mCallbackMutex(), mNextCallbackTicket(0), mCurrentCallbackTicket(0), @@ -343,7 +347,8 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller, status_t BufferQueueProducer::dequeueBuffer(int *outSlot, sp<android::Fence> *outFence, uint32_t width, uint32_t height, - PixelFormat format, uint32_t usage) { + PixelFormat format, uint32_t usage, + FrameEventHistoryDelta* outTimestamps) { ATRACE_CALL(); { // Autolock scope Mutex::Autolock lock(mCore->mMutex); @@ -411,7 +416,8 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, // buffer. If this buffer would require reallocation to meet the // requested attributes, we free it and attempt to get another one. if (!mCore->mAllowAllocation) { - if (buffer->needsReallocation(width, height, format, usage)) { + if (buffer->needsReallocation(width, height, format, + BQ_LAYER_COUNT, usage)) { if (mCore->mSharedBufferSlot == found) { BQ_LOGE("dequeueBuffer: cannot re-allocate a shared" "buffer"); @@ -427,7 +433,8 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer); if (mCore->mSharedBufferSlot == found && - buffer->needsReallocation(width, height, format, usage)) { + buffer->needsReallocation(width, height, format, + BQ_LAYER_COUNT, usage)) { BQ_LOGE("dequeueBuffer: cannot re-allocate a shared" "buffer"); @@ -446,7 +453,7 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, mSlots[found].mBufferState.dequeue(); if ((buffer == NULL) || - buffer->needsReallocation(width, height, format, usage)) + buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) { mSlots[found].mAcquireCalled = false; mSlots[found].mGraphicBuffer = NULL; @@ -494,15 +501,17 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, } // Autolock scope if (returnFlags & BUFFER_NEEDS_REALLOCATION) { - status_t error; BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot); - sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer( - width, height, format, usage, - {mConsumerName.string(), mConsumerName.size()}, &error)); + sp<GraphicBuffer> graphicBuffer = new GraphicBuffer( + width, height, format, BQ_LAYER_COUNT, usage, + {mConsumerName.string(), mConsumerName.size()}); + + status_t error = graphicBuffer->initCheck(); + { // Autolock scope Mutex::Autolock lock(mCore->mMutex); - if (graphicBuffer != NULL && !mCore->mIsAbandoned) { + if (error == NO_ERROR && !mCore->mIsAbandoned) { graphicBuffer->setGenerationNumber(mCore->mGenerationNumber); mSlots[*outSlot].mGraphicBuffer = graphicBuffer; } @@ -510,7 +519,7 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, mCore->mIsAllocating = false; mCore->mIsAllocatingCondition.broadcast(); - if (graphicBuffer == NULL) { + if (error != NO_ERROR) { mCore->mFreeSlots.insert(*outSlot); mCore->clearBufferSlotLocked(*outSlot); BQ_LOGE("dequeueBuffer: createGraphicBuffer failed"); @@ -552,6 +561,8 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, mSlots[*outSlot].mFrameNumber, mSlots[*outSlot].mGraphicBuffer->handle, returnFlags); + addAndGetFrameTimestamps(nullptr, outTimestamps); + return returnFlags; } @@ -621,40 +632,48 @@ status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer, return BAD_VALUE; } - Mutex::Autolock lock(mCore->mMutex); + sp<IConsumerListener> listener; + { + Mutex::Autolock lock(mCore->mMutex); - if (mCore->mIsAbandoned) { - BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned"); - return NO_INIT; - } + if (mCore->mIsAbandoned) { + BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned"); + return NO_INIT; + } - if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { - BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer"); - return NO_INIT; - } + if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { + BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer"); + return NO_INIT; + } - if (mCore->mSharedBufferMode) { - BQ_LOGE("detachNextBuffer: cannot detach a buffer in shared buffer " - "mode"); - return BAD_VALUE; - } + if (mCore->mSharedBufferMode) { + BQ_LOGE("detachNextBuffer: cannot detach a buffer in shared buffer " + "mode"); + return BAD_VALUE; + } - mCore->waitWhileAllocatingLocked(); + mCore->waitWhileAllocatingLocked(); - if (mCore->mFreeBuffers.empty()) { - return NO_MEMORY; - } + if (mCore->mFreeBuffers.empty()) { + return NO_MEMORY; + } - int found = mCore->mFreeBuffers.front(); - mCore->mFreeBuffers.remove(found); - mCore->mFreeSlots.insert(found); + int found = mCore->mFreeBuffers.front(); + mCore->mFreeBuffers.remove(found); + mCore->mFreeSlots.insert(found); - BQ_LOGV("detachNextBuffer detached slot %d", found); + BQ_LOGV("detachNextBuffer detached slot %d", found); - *outBuffer = mSlots[found].mGraphicBuffer; - *outFence = mSlots[found].mFence; - mCore->clearBufferSlotLocked(found); - VALIDATE_CONSISTENCY(); + *outBuffer = mSlots[found].mGraphicBuffer; + *outFence = mSlots[found].mFence; + mCore->clearBufferSlotLocked(found); + VALIDATE_CONSISTENCY(); + listener = mCore->mConsumerListener; + } + + if (listener != NULL) { + listener->onBuffersReleased(); + } return NO_ERROR; } @@ -733,23 +752,27 @@ status_t BufferQueueProducer::queueBuffer(int slot, ATRACE_CALL(); ATRACE_BUFFER_INDEX(slot); - int64_t timestamp; + int64_t requestedPresentTimestamp; bool isAutoTimestamp; android_dataspace dataSpace; Rect crop(Rect::EMPTY_RECT); int scalingMode; uint32_t transform; uint32_t stickyTransform; - sp<Fence> fence; - input.deflate(×tamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode, - &transform, &fence, &stickyTransform); + sp<Fence> acquireFence; + bool getFrameTimestamps = false; + input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace, + &crop, &scalingMode, &transform, &acquireFence, &stickyTransform, + &getFrameTimestamps); Region surfaceDamage = input.getSurfaceDamage(); - if (fence == NULL) { + if (acquireFence == NULL) { BQ_LOGE("queueBuffer: fence is NULL"); return BAD_VALUE; } + auto acquireFenceTime = std::make_shared<FenceTime>(acquireFence); + switch (scalingMode) { case NATIVE_WINDOW_SCALING_MODE_FREEZE: case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: @@ -764,6 +787,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, sp<IConsumerListener> frameAvailableListener; sp<IConsumerListener> frameReplacedListener; int callbackTicket = 0; + uint64_t currentFrameNumber = 0; BufferItem item; { // Autolock scope Mutex::Autolock lock(mCore->mMutex); @@ -802,8 +826,9 @@ status_t BufferQueueProducer::queueBuffer(int slot, BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 " dataSpace=%d" " crop=[%d,%d,%d,%d] transform=%#x scale=%s", - slot, mCore->mFrameCounter + 1, timestamp, dataSpace, - crop.left, crop.top, crop.right, crop.bottom, transform, + slot, mCore->mFrameCounter + 1, requestedPresentTimestamp, + dataSpace, crop.left, crop.top, crop.right, crop.bottom, + transform, BufferItem::scalingModeName(static_cast<uint32_t>(scalingMode))); const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer); @@ -821,11 +846,14 @@ status_t BufferQueueProducer::queueBuffer(int slot, dataSpace = mCore->mDefaultBufferDataSpace; } - mSlots[slot].mFence = fence; + mSlots[slot].mFence = acquireFence; mSlots[slot].mBufferState.queue(); + // Increment the frame counter and store a local version of it + // for use outside the lock on mCore->mMutex. ++mCore->mFrameCounter; - mSlots[slot].mFrameNumber = mCore->mFrameCounter; + currentFrameNumber = mCore->mFrameCounter; + mSlots[slot].mFrameNumber = currentFrameNumber; item.mAcquireCalled = mSlots[slot].mAcquireCalled; item.mGraphicBuffer = mSlots[slot].mGraphicBuffer; @@ -835,12 +863,13 @@ status_t BufferQueueProducer::queueBuffer(int slot, item.mTransformToDisplayInverse = (transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0; item.mScalingMode = static_cast<uint32_t>(scalingMode); - item.mTimestamp = timestamp; + item.mTimestamp = requestedPresentTimestamp; item.mIsAutoTimestamp = isAutoTimestamp; item.mDataSpace = dataSpace; - item.mFrameNumber = mCore->mFrameCounter; + item.mFrameNumber = currentFrameNumber; item.mSlot = slot; - item.mFence = fence; + item.mFence = acquireFence; + item.mFenceTime = acquireFenceTime; item.mIsDroppable = mCore->mAsyncMode || mCore->mDequeueBufferCannotBlock || (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot); @@ -859,6 +888,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, mCore->mSharedBufferCache.dataspace = dataSpace; } + output->bufferReplaced = false; if (mCore->mQueue.empty()) { // When the queue is empty, we can ignore mDequeueBufferCannotBlock // and simply queue this buffer @@ -885,6 +915,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, if (!mSlots[last.mSlot].mBufferState.isShared()) { mCore->mActiveBuffers.erase(last.mSlot); mCore->mFreeBuffers.push_back(last.mSlot); + output->bufferReplaced = true; } } @@ -901,10 +932,11 @@ status_t BufferQueueProducer::queueBuffer(int slot, mCore->mDequeueCondition.broadcast(); mCore->mLastQueuedSlot = slot; - output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, - mCore->mTransformHint, - static_cast<uint32_t>(mCore->mQueue.size()), - mCore->mFrameCounter + 1); + output->width = mCore->mDefaultWidth; + output->height = mCore->mDefaultHeight; + output->transformHint = mCore->mTransformHint; + output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size()); + output->nextFrameNumber = mCore->mFrameCounter + 1; ATRACE_INT(mCore->mConsumerName.string(), static_cast<int32_t>(mCore->mQueue.size())); @@ -916,14 +948,23 @@ status_t BufferQueueProducer::queueBuffer(int slot, VALIDATE_CONSISTENCY(); } // Autolock scope - // Don't send the GraphicBuffer through the callback, and don't send - // the slot number, since the consumer shouldn't need it - item.mGraphicBuffer.clear(); + // It is okay not to clear the GraphicBuffer when the consumer is SurfaceFlinger because + // it is guaranteed that the BufferQueue is inside SurfaceFlinger's process and + // there will be no Binder call + if (!mConsumerIsSurfaceFlinger) { + item.mGraphicBuffer.clear(); + } + + // Don't send the slot number through the callback since the consumer shouldn't need it item.mSlot = BufferItem::INVALID_BUFFER_SLOT; // Call back without the main BufferQueue lock held, but with the callback // lock held so we can ensure that callbacks occur in order - { + + int connectedApi; + sp<Fence> lastQueuedFence; + + { // scope for the lock Mutex::Autolock lock(mCallbackMutex); while (callbackTicket != mCurrentCallbackTicket) { mCallbackCondition.wait(mCallbackMutex); @@ -935,20 +976,35 @@ status_t BufferQueueProducer::queueBuffer(int slot, frameReplacedListener->onFrameReplaced(item); } + connectedApi = mCore->mConnectedApi; + lastQueuedFence = std::move(mLastQueueBufferFence); + + mLastQueueBufferFence = std::move(acquireFence); + mLastQueuedCrop = item.mCrop; + mLastQueuedTransform = item.mTransform; + ++mCurrentCallbackTicket; mCallbackCondition.broadcast(); } // Wait without lock held - if (mCore->mConnectedApi == NATIVE_WINDOW_API_EGL) { + if (connectedApi == NATIVE_WINDOW_API_EGL) { // Waiting here allows for two full buffers to be queued but not a // third. In the event that frames take varying time, this makes a // small trade-off in favor of latency rather than throughput. - mLastQueueBufferFence->waitForever("Throttling EGL Production"); + lastQueuedFence->waitForever("Throttling EGL Production"); } - mLastQueueBufferFence = fence; - mLastQueuedCrop = item.mCrop; - mLastQueuedTransform = item.mTransform; + + // Update and get FrameEventHistory. + nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC); + NewFrameEventsEntry newFrameEventsEntry = { + currentFrameNumber, + postedTime, + requestedPresentTimestamp, + std::move(acquireFenceTime) + }; + addAndGetFrameTimestamps(&newFrameEventsEntry, + getFrameTimestamps ? &output->frameTimestamps : nullptr); return NO_ERROR; } @@ -1032,6 +1088,10 @@ int BufferQueueProducer::query(int what, int *outValue) { case NATIVE_WINDOW_FORMAT: value = static_cast<int32_t>(mCore->mDefaultBufferFormat); break; + case NATIVE_WINDOW_LAYER_COUNT: + // All BufferQueue buffers have a single layer. + value = BQ_LAYER_COUNT; + break; case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: value = mCore->getMinUndequeuedBufferCountLocked(); break; @@ -1054,6 +1114,9 @@ int BufferQueueProducer::query(int what, int *outValue) { value = static_cast<int32_t>(mCore->mBufferAge); } break; + case NATIVE_WINDOW_CONSUMER_IS_PROTECTED: + value = static_cast<int32_t>(mCore->mConsumerIsProtected); + break; default: return BAD_VALUE; } @@ -1110,10 +1173,14 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: mCore->mConnectedApi = api; - output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, - mCore->mTransformHint, - static_cast<uint32_t>(mCore->mQueue.size()), - mCore->mFrameCounter + 1); + + output->width = mCore->mDefaultWidth; + output->height = mCore->mDefaultHeight; + output->transformHint = mCore->mTransformHint; + output->numPendingBuffers = + static_cast<uint32_t>(mCore->mQueue.size()); + output->nextFrameNumber = mCore->mFrameCounter + 1; + output->bufferReplaced = false; if (listener != NULL) { // Set up a death notification so that we can disconnect @@ -1175,6 +1242,9 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { } if (api == BufferQueueCore::CURRENTLY_CONNECTED_API) { + if (mCore->mConnectedApi == NATIVE_WINDOW_API_MEDIA) { + ALOGD("About to force-disconnect API_MEDIA, mode=%d", mode); + } api = mCore->mConnectedApi; // If we're asked to disconnect the currently connected api but // nobody is connected, it's not really an error. @@ -1209,7 +1279,10 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { mCore->mSidebandStream.clear(); mCore->mDequeueCondition.broadcast(); listener = mCore->mConsumerListener; - } else if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) { + } else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { + BQ_LOGE("disconnect: not connected (req=%d)", api); + status = NO_INIT; + } else { BQ_LOGE("disconnect: still connected to another API " "(cur=%d req=%d)", mCore->mConnectedApi, api); status = BAD_VALUE; @@ -1225,6 +1298,7 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { // Call back without lock held if (listener != NULL) { listener->onBuffersReleased(); + listener->onDisconnect(); } return status; @@ -1278,10 +1352,12 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, Vector<sp<GraphicBuffer>> buffers; for (size_t i = 0; i < newBufferCount; ++i) { - status_t result = NO_ERROR; - sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer( - allocWidth, allocHeight, allocFormat, allocUsage, - {mConsumerName.string(), mConsumerName.size()}, &result)); + sp<GraphicBuffer> graphicBuffer = new GraphicBuffer( + allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT, + allocUsage, {mConsumerName.string(), mConsumerName.size()}); + + status_t result = graphicBuffer->initCheck(); + if (result != NO_ERROR) { BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format" " %u, usage %u)", width, height, format, usage); @@ -1432,20 +1508,27 @@ status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, return NO_ERROR; } -bool BufferQueueProducer::getFrameTimestamps(uint64_t frameNumber, - FrameTimestamps* outTimestamps) const { +void BufferQueueProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) { + addAndGetFrameTimestamps(nullptr, outDelta); +} + +void BufferQueueProducer::addAndGetFrameTimestamps( + const NewFrameEventsEntry* newTimestamps, + FrameEventHistoryDelta* outDelta) { + if (newTimestamps == nullptr && outDelta == nullptr) { + return; + } + ATRACE_CALL(); - BQ_LOGV("getFrameTimestamps, %" PRIu64, frameNumber); + BQ_LOGV("addAndGetFrameTimestamps"); sp<IConsumerListener> listener; - { Mutex::Autolock lock(mCore->mMutex); listener = mCore->mConsumerListener; } if (listener != NULL) { - return listener->getFrameTimestamps(frameNumber, outTimestamps); + listener->addAndGetFrameTimestamps(newTimestamps, outDelta); } - return false; } void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) { diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index 3cf3078345..c2b10a91dd 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -27,8 +27,9 @@ #include <hardware/hardware.h> +#include <cutils/atomic.h> + #include <gui/BufferItem.h> -#include <gui/IGraphicBufferAlloc.h> #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> #include <gui/ConsumerBase.h> @@ -56,7 +57,8 @@ static int32_t createProcessUniqueId() { ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) : mAbandoned(false), - mConsumer(bufferQueue) { + mConsumer(bufferQueue), + mPrevFinalReleaseFence(Fence::NO_FENCE) { // Choose a name using the PID and a process-unique ID. mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); @@ -251,7 +253,18 @@ status_t ConsumerBase::discardFreeBuffers() { CB_LOGE("discardFreeBuffers: ConsumerBase is abandoned!"); return NO_INIT; } - return mConsumer->discardFreeBuffers(); + status_t err = mConsumer->discardFreeBuffers(); + if (err != OK) { + return err; + } + uint64_t mask; + mConsumer->getReleasedBuffers(&mask); + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + if (mask & (1ULL << i)) { + freeBufferLocked(i); + } + } + return OK; } void ConsumerBase::dumpState(String8& result) const { @@ -267,7 +280,9 @@ void ConsumerBase::dumpLocked(String8& result, const char* prefix) const { result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned)); if (!mAbandoned) { - mConsumer->dumpState(result, prefix); + String8 consumerState; + mConsumer->dumpState(String8(prefix), &consumerState); + result.append(consumerState); } } @@ -284,6 +299,9 @@ status_t ConsumerBase::acquireBufferLocked(BufferItem *item, } if (item->mGraphicBuffer != NULL) { + if (mSlots[item->mSlot].mGraphicBuffer != NULL) { + freeBufferLocked(item->mSlot); + } mSlots[item->mSlot].mGraphicBuffer = item->mGraphicBuffer; } @@ -317,16 +335,16 @@ status_t ConsumerBase::addReleaseFenceLocked(int slot, return OK; } - auto signaled = mSlots[slot].mFence->hasSignaled(); + auto status = mSlots[slot].mFence->getStatus(); - if (!signaled) { + if (status == Fence::Status::Invalid) { CB_LOGE("fence has invalid state"); return BAD_VALUE; } - if (*signaled) { + if (status == Fence::Status::Signaled) { mSlots[slot].mFence = fence; - } else { + } else { // status == Fence::Status::Unsignaled char fenceName[32] = {}; snprintf(fenceName, 32, "%.28s:%d", mName.string(), slot); sp<Fence> mergedFence = Fence::merge( @@ -366,6 +384,7 @@ status_t ConsumerBase::releaseBufferLocked( freeBufferLocked(slot); } + mPrevFinalReleaseFence = mSlots[slot].mFence; mSlots[slot].mFence = Fence::NO_FENCE; return err; diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp index 839316021e..ae7c65c441 100644 --- a/libs/gui/CpuConsumer.cpp +++ b/libs/gui/CpuConsumer.cpp @@ -64,6 +64,8 @@ static bool isPossiblyYUV(PixelFormat format) { switch (static_cast<int>(format)) { case HAL_PIXEL_FORMAT_RGBA_8888: case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_RGBA_FP16: + case HAL_PIXEL_FORMAT_RGBA_1010102: case HAL_PIXEL_FORMAT_RGB_888: case HAL_PIXEL_FORMAT_RGB_565: case HAL_PIXEL_FORMAT_BGRA_8888: diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp index 9973e8daef..1757ec1cd3 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -18,25 +18,27 @@ #include <utils/Errors.h> -#include <gui/BitTube.h> #include <gui/DisplayEventReceiver.h> #include <gui/IDisplayEventConnection.h> #include <gui/ISurfaceComposer.h> #include <private/gui/ComposerService.h> +#include <private/gui/BitTube.h> + // --------------------------------------------------------------------------- namespace android { // --------------------------------------------------------------------------- -DisplayEventReceiver::DisplayEventReceiver() { +DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); if (sf != NULL) { - mEventConnection = sf->createDisplayEventConnection(); + mEventConnection = sf->createDisplayEventConnection(vsyncSource); if (mEventConnection != NULL) { - mDataChannel = mEventConnection->getDataChannel(); + mDataChannel = std::make_unique<gui::BitTube>(); + mEventConnection->stealReceiveChannel(mDataChannel.get()); } } } @@ -79,19 +81,19 @@ status_t DisplayEventReceiver::requestNextVsync() { ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events, size_t count) { - return DisplayEventReceiver::getEvents(mDataChannel, events, count); + return DisplayEventReceiver::getEvents(mDataChannel.get(), events, count); } -ssize_t DisplayEventReceiver::getEvents(const sp<BitTube>& dataChannel, +ssize_t DisplayEventReceiver::getEvents(gui::BitTube* dataChannel, Event* events, size_t count) { - return BitTube::recvObjects(dataChannel, events, count); + return gui::BitTube::recvObjects(dataChannel, events, count); } -ssize_t DisplayEventReceiver::sendEvents(const sp<BitTube>& dataChannel, +ssize_t DisplayEventReceiver::sendEvents(gui::BitTube* dataChannel, Event const* events, size_t count) { - return BitTube::sendObjects(dataChannel, events, count); + return gui::BitTube::sendObjects(dataChannel, events, count); } // --------------------------------------------------------------------------- diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp new file mode 100644 index 0000000000..fccca97f54 --- /dev/null +++ b/libs/gui/FrameTimestamps.cpp @@ -0,0 +1,701 @@ +/* +* Copyright 2016 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT 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/FrameTimestamps.h> + +#define LOG_TAG "FrameEvents" + +#include <cutils/compiler.h> // For CC_[UN]LIKELY +#include <inttypes.h> +#include <utils/Log.h> +#include <utils/String8.h> + +#include <algorithm> +#include <limits> +#include <numeric> + +namespace android { + + +// ============================================================================ +// FrameEvents +// ============================================================================ + +bool FrameEvents::hasPostedInfo() const { + return FrameEvents::isValidTimestamp(postedTime); +} + +bool FrameEvents::hasRequestedPresentInfo() const { + return FrameEvents::isValidTimestamp(requestedPresentTime); +} + +bool FrameEvents::hasLatchInfo() const { + return FrameEvents::isValidTimestamp(latchTime); +} + +bool FrameEvents::hasFirstRefreshStartInfo() const { + return FrameEvents::isValidTimestamp(firstRefreshStartTime); +} + +bool FrameEvents::hasLastRefreshStartInfo() const { + // The last refresh start time may continue to update until a new frame + // is latched. We know we have the final value once the release info is set. + return addReleaseCalled; +} + +bool FrameEvents::hasDequeueReadyInfo() const { + return FrameEvents::isValidTimestamp(dequeueReadyTime); +} + +bool FrameEvents::hasAcquireInfo() const { + return acquireFence->isValid(); +} + +bool FrameEvents::hasGpuCompositionDoneInfo() const { + // We may not get a gpuCompositionDone in addPostComposite if + // client/gles compositing isn't needed. + return addPostCompositeCalled; +} + +bool FrameEvents::hasDisplayPresentInfo() const { + // We may not get a displayPresent in addPostComposite for HWC1. + return addPostCompositeCalled; +} + +bool FrameEvents::hasReleaseInfo() const { + return addReleaseCalled; +} + +void FrameEvents::checkFencesForCompletion() { + acquireFence->getSignalTime(); + gpuCompositionDoneFence->getSignalTime(); + displayPresentFence->getSignalTime(); + releaseFence->getSignalTime(); +} + +static void dumpFenceTime(String8& outString, const char* name, + bool pending, const FenceTime& fenceTime) { + outString.appendFormat("--- %s", name); + nsecs_t signalTime = fenceTime.getCachedSignalTime(); + if (Fence::isValidTimestamp(signalTime)) { + outString.appendFormat("%" PRId64 "\n", signalTime); + } else if (pending || signalTime == Fence::SIGNAL_TIME_PENDING) { + outString.appendFormat("Pending\n"); + } else if (&fenceTime == FenceTime::NO_FENCE.get()){ + outString.appendFormat("N/A\n"); + } else { + outString.appendFormat("Error\n"); + } +} + +void FrameEvents::dump(String8& outString) const +{ + if (!valid) { + return; + } + + outString.appendFormat("-- Frame %" PRIu64 "\n", frameNumber); + outString.appendFormat("--- Posted \t%" PRId64 "\n", postedTime); + outString.appendFormat("--- Req. Present\t%" PRId64 "\n", requestedPresentTime); + + outString.appendFormat("--- Latched \t"); + if (FrameEvents::isValidTimestamp(latchTime)) { + outString.appendFormat("%" PRId64 "\n", latchTime); + } else { + outString.appendFormat("Pending\n"); + } + + outString.appendFormat("--- Refresh (First)\t"); + if (FrameEvents::isValidTimestamp(firstRefreshStartTime)) { + outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime); + } else { + outString.appendFormat("Pending\n"); + } + + outString.appendFormat("--- Refresh (Last)\t"); + if (FrameEvents::isValidTimestamp(lastRefreshStartTime)) { + outString.appendFormat("%" PRId64 "\n", lastRefreshStartTime); + } else { + outString.appendFormat("Pending\n"); + } + + dumpFenceTime(outString, "Acquire \t", + true, *acquireFence); + dumpFenceTime(outString, "GPU Composite Done\t", + !addPostCompositeCalled, *gpuCompositionDoneFence); + dumpFenceTime(outString, "Display Present \t", + !addPostCompositeCalled, *displayPresentFence); + + outString.appendFormat("--- DequeueReady \t"); + if (FrameEvents::isValidTimestamp(dequeueReadyTime)) { + outString.appendFormat("%" PRId64 "\n", dequeueReadyTime); + } else { + outString.appendFormat("Pending\n"); + } + + dumpFenceTime(outString, "Release \t", + true, *releaseFence); +} + + +// ============================================================================ +// FrameEventHistory +// ============================================================================ + +namespace { + +struct FrameNumberEqual { + FrameNumberEqual(uint64_t frameNumber) : mFrameNumber(frameNumber) {} + bool operator()(const FrameEvents& frame) { + return frame.valid && mFrameNumber == frame.frameNumber; + } + const uint64_t mFrameNumber; +}; + +} // namespace + +FrameEventHistory::~FrameEventHistory() = default; + +FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber) { + auto frame = std::find_if( + mFrames.begin(), mFrames.end(), FrameNumberEqual(frameNumber)); + return frame == mFrames.end() ? nullptr : &(*frame); +} + +FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber, size_t* iHint) { + *iHint = std::min(*iHint, mFrames.size()); + auto hint = mFrames.begin() + *iHint; + auto frame = std::find_if( + hint, mFrames.end(), FrameNumberEqual(frameNumber)); + if (frame == mFrames.end()) { + frame = std::find_if( + mFrames.begin(), hint, FrameNumberEqual(frameNumber)); + if (frame == hint) { + return nullptr; + } + } + *iHint = static_cast<size_t>(std::distance(mFrames.begin(), frame)); + return &(*frame); +} + +void FrameEventHistory::checkFencesForCompletion() { + for (auto& frame : mFrames) { + frame.checkFencesForCompletion(); + } +} + +// Uses !|valid| as the MSB. +static bool FrameNumberLessThan( + const FrameEvents& lhs, const FrameEvents& rhs) { + if (lhs.valid == rhs.valid) { + return lhs.frameNumber < rhs.frameNumber; + } + return lhs.valid; +} + +void FrameEventHistory::dump(String8& outString) const { + auto earliestFrame = std::min_element( + mFrames.begin(), mFrames.end(), &FrameNumberLessThan); + if (!earliestFrame->valid) { + outString.appendFormat("-- N/A\n"); + return; + } + for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) { + frame->dump(outString); + } + for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) { + frame->dump(outString); + } +} + + +// ============================================================================ +// ProducerFrameEventHistory +// ============================================================================ + +ProducerFrameEventHistory::~ProducerFrameEventHistory() = default; + +nsecs_t ProducerFrameEventHistory::snapToNextTick( + nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval) { + nsecs_t tickOffset = (tickPhase - timestamp) % tickInterval; + // Integer modulo rounds towards 0 and not -inf before taking the remainder, + // so adjust the offset if it is negative. + if (tickOffset < 0) { + tickOffset += tickInterval; + } + return timestamp + tickOffset; +} + +nsecs_t ProducerFrameEventHistory::getNextCompositeDeadline( + const nsecs_t now) const{ + return snapToNextTick( + now, mCompositorTiming.deadline, mCompositorTiming.interval); +} + +void ProducerFrameEventHistory::updateAcquireFence( + uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire) { + FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset); + if (frame == nullptr) { + ALOGE("updateAcquireFence: Did not find frame."); + return; + } + + if (acquire->isValid()) { + mAcquireTimeline.push(acquire); + frame->acquireFence = std::move(acquire); + } else { + // If there isn't an acquire fence, assume that buffer was + // ready for the consumer when posted. + frame->acquireFence = std::make_shared<FenceTime>(frame->postedTime); + } +} + +void ProducerFrameEventHistory::applyDelta( + const FrameEventHistoryDelta& delta) { + mCompositorTiming = delta.mCompositorTiming; + + for (auto& d : delta.mDeltas) { + // Avoid out-of-bounds access. + if (CC_UNLIKELY(d.mIndex >= mFrames.size())) { + ALOGE("applyDelta: Bad index."); + return; + } + + FrameEvents& frame = mFrames[d.mIndex]; + + frame.addPostCompositeCalled = d.mAddPostCompositeCalled != 0; + frame.addReleaseCalled = d.mAddReleaseCalled != 0; + + frame.postedTime = d.mPostedTime; + frame.requestedPresentTime = d.mRequestedPresentTime; + frame.latchTime = d.mLatchTime; + frame.firstRefreshStartTime = d.mFirstRefreshStartTime; + frame.lastRefreshStartTime = d.mLastRefreshStartTime; + frame.dequeueReadyTime = d.mDequeueReadyTime; + + if (frame.frameNumber != d.mFrameNumber) { + // We got a new frame. Initialize some of the fields. + frame.frameNumber = d.mFrameNumber; + frame.acquireFence = FenceTime::NO_FENCE; + frame.gpuCompositionDoneFence = FenceTime::NO_FENCE; + frame.displayPresentFence = FenceTime::NO_FENCE; + frame.releaseFence = FenceTime::NO_FENCE; + // The consumer only sends valid frames. + frame.valid = true; + } + + applyFenceDelta(&mGpuCompositionDoneTimeline, + &frame.gpuCompositionDoneFence, d.mGpuCompositionDoneFence); + applyFenceDelta(&mPresentTimeline, + &frame.displayPresentFence, d.mDisplayPresentFence); + applyFenceDelta(&mReleaseTimeline, + &frame.releaseFence, d.mReleaseFence); + } +} + +void ProducerFrameEventHistory::updateSignalTimes() { + mAcquireTimeline.updateSignalTimes(); + mGpuCompositionDoneTimeline.updateSignalTimes(); + mPresentTimeline.updateSignalTimes(); + mReleaseTimeline.updateSignalTimes(); +} + +void ProducerFrameEventHistory::applyFenceDelta(FenceTimeline* timeline, + std::shared_ptr<FenceTime>* dst, const FenceTime::Snapshot& src) const { + if (CC_UNLIKELY(dst == nullptr || dst->get() == nullptr)) { + ALOGE("applyFenceDelta: dst is null."); + return; + } + + switch (src.state) { + case FenceTime::Snapshot::State::EMPTY: + return; + case FenceTime::Snapshot::State::FENCE: + ALOGE_IF((*dst)->isValid(), "applyFenceDelta: Unexpected fence."); + *dst = createFenceTime(src.fence); + timeline->push(*dst); + return; + case FenceTime::Snapshot::State::SIGNAL_TIME: + if ((*dst)->isValid()) { + (*dst)->applyTrustedSnapshot(src); + } else { + *dst = std::make_shared<FenceTime>(src.signalTime); + } + return; + } +} + +std::shared_ptr<FenceTime> ProducerFrameEventHistory::createFenceTime( + const sp<Fence>& fence) const { + return std::make_shared<FenceTime>(fence); +} + + +// ============================================================================ +// ConsumerFrameEventHistory +// ============================================================================ + +ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default; + +void ConsumerFrameEventHistory::onDisconnect() { + mCurrentConnectId++; + mProducerWantsEvents = false; +} + +void ConsumerFrameEventHistory::initializeCompositorTiming( + const CompositorTiming& compositorTiming) { + mCompositorTiming = compositorTiming; +} + +void ConsumerFrameEventHistory::addQueue(const NewFrameEventsEntry& newEntry) { + // Overwrite all fields of the frame with default values unless set here. + FrameEvents newTimestamps; + newTimestamps.connectId = mCurrentConnectId; + newTimestamps.frameNumber = newEntry.frameNumber; + newTimestamps.postedTime = newEntry.postedTime; + newTimestamps.requestedPresentTime = newEntry.requestedPresentTime; + newTimestamps.acquireFence = newEntry.acquireFence; + newTimestamps.valid = true; + mFrames[mQueueOffset] = newTimestamps; + + // Note: We avoid sending the acquire fence back to the caller since + // they have the original one already, so there is no need to set the + // acquire dirty bit. + mFramesDirty[mQueueOffset].setDirty<FrameEvent::POSTED>(); + + mQueueOffset = (mQueueOffset + 1) % mFrames.size(); +} + +void ConsumerFrameEventHistory::addLatch( + uint64_t frameNumber, nsecs_t latchTime) { + FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset); + if (frame == nullptr) { + ALOGE_IF(mProducerWantsEvents, "addLatch: Did not find frame."); + return; + } + frame->latchTime = latchTime; + mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LATCH>(); +} + +void ConsumerFrameEventHistory::addPreComposition( + uint64_t frameNumber, nsecs_t refreshStartTime) { + FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset); + if (frame == nullptr) { + ALOGE_IF(mProducerWantsEvents, + "addPreComposition: Did not find frame."); + return; + } + frame->lastRefreshStartTime = refreshStartTime; + mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LAST_REFRESH_START>(); + if (!FrameEvents::isValidTimestamp(frame->firstRefreshStartTime)) { + frame->firstRefreshStartTime = refreshStartTime; + mFramesDirty[mCompositionOffset].setDirty<FrameEvent::FIRST_REFRESH_START>(); + } +} + +void ConsumerFrameEventHistory::addPostComposition(uint64_t frameNumber, + const std::shared_ptr<FenceTime>& gpuCompositionDone, + const std::shared_ptr<FenceTime>& displayPresent, + const CompositorTiming& compositorTiming) { + mCompositorTiming = compositorTiming; + + FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset); + if (frame == nullptr) { + ALOGE_IF(mProducerWantsEvents, + "addPostComposition: Did not find frame."); + return; + } + // Only get GPU and present info for the first composite. + if (!frame->addPostCompositeCalled) { + frame->addPostCompositeCalled = true; + frame->gpuCompositionDoneFence = gpuCompositionDone; + mFramesDirty[mCompositionOffset].setDirty<FrameEvent::GPU_COMPOSITION_DONE>(); + if (!frame->displayPresentFence->isValid()) { + frame->displayPresentFence = displayPresent; + mFramesDirty[mCompositionOffset].setDirty<FrameEvent::DISPLAY_PRESENT>(); + } + } +} + +void ConsumerFrameEventHistory::addRelease(uint64_t frameNumber, + nsecs_t dequeueReadyTime, std::shared_ptr<FenceTime>&& release) { + FrameEvents* frame = getFrame(frameNumber, &mReleaseOffset); + if (frame == nullptr) { + ALOGE_IF(mProducerWantsEvents, "addRelease: Did not find frame."); + return; + } + frame->addReleaseCalled = true; + frame->dequeueReadyTime = dequeueReadyTime; + frame->releaseFence = std::move(release); + mFramesDirty[mReleaseOffset].setDirty<FrameEvent::RELEASE>(); +} + +void ConsumerFrameEventHistory::getFrameDelta( + FrameEventHistoryDelta* delta, + const std::array<FrameEvents, MAX_FRAME_HISTORY>::iterator& frame) { + mProducerWantsEvents = true; + size_t i = static_cast<size_t>(std::distance(mFrames.begin(), frame)); + if (mFramesDirty[i].anyDirty()) { + // Make sure only to send back deltas for the current connection + // since the producer won't have the correct state to apply a delta + // from a previous connection. + if (mFrames[i].connectId == mCurrentConnectId) { + delta->mDeltas.emplace_back(i, *frame, mFramesDirty[i]); + } + mFramesDirty[i].reset(); + } +} + +void ConsumerFrameEventHistory::getAndResetDelta( + FrameEventHistoryDelta* delta) { + mProducerWantsEvents = true; + delta->mCompositorTiming = mCompositorTiming; + + // Write these in order of frame number so that it is easy to + // add them to a FenceTimeline in the proper order producer side. + delta->mDeltas.reserve(mFramesDirty.size()); + auto earliestFrame = std::min_element( + mFrames.begin(), mFrames.end(), &FrameNumberLessThan); + for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) { + getFrameDelta(delta, frame); + } + for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) { + getFrameDelta(delta, frame); + } +} + + +// ============================================================================ +// FrameEventsDelta +// ============================================================================ + +FrameEventsDelta::FrameEventsDelta( + size_t index, + const FrameEvents& frameTimestamps, + const FrameEventDirtyFields& dirtyFields) + : mIndex(index), + mFrameNumber(frameTimestamps.frameNumber), + mAddPostCompositeCalled(frameTimestamps.addPostCompositeCalled), + mAddReleaseCalled(frameTimestamps.addReleaseCalled), + mPostedTime(frameTimestamps.postedTime), + mRequestedPresentTime(frameTimestamps.requestedPresentTime), + mLatchTime(frameTimestamps.latchTime), + mFirstRefreshStartTime(frameTimestamps.firstRefreshStartTime), + mLastRefreshStartTime(frameTimestamps.lastRefreshStartTime), + mDequeueReadyTime(frameTimestamps.dequeueReadyTime) { + if (dirtyFields.isDirty<FrameEvent::GPU_COMPOSITION_DONE>()) { + mGpuCompositionDoneFence = + frameTimestamps.gpuCompositionDoneFence->getSnapshot(); + } + if (dirtyFields.isDirty<FrameEvent::DISPLAY_PRESENT>()) { + mDisplayPresentFence = + frameTimestamps.displayPresentFence->getSnapshot(); + } + if (dirtyFields.isDirty<FrameEvent::RELEASE>()) { + mReleaseFence = frameTimestamps.releaseFence->getSnapshot(); + } +} + +constexpr size_t FrameEventsDelta::minFlattenedSize() { + return sizeof(FrameEventsDelta::mFrameNumber) + + sizeof(uint16_t) + // mIndex + sizeof(uint8_t) + // mAddPostCompositeCalled + sizeof(uint8_t) + // mAddReleaseCalled + sizeof(FrameEventsDelta::mPostedTime) + + sizeof(FrameEventsDelta::mRequestedPresentTime) + + sizeof(FrameEventsDelta::mLatchTime) + + sizeof(FrameEventsDelta::mFirstRefreshStartTime) + + sizeof(FrameEventsDelta::mLastRefreshStartTime) + + sizeof(FrameEventsDelta::mDequeueReadyTime); +} + +// Flattenable implementation +size_t FrameEventsDelta::getFlattenedSize() const { + auto fences = allFences(this); + return minFlattenedSize() + + std::accumulate(fences.begin(), fences.end(), size_t(0), + [](size_t a, const FenceTime::Snapshot* fence) { + return a + fence->getFlattenedSize(); + }); +} + +size_t FrameEventsDelta::getFdCount() const { + auto fences = allFences(this); + return std::accumulate(fences.begin(), fences.end(), size_t(0), + [](size_t a, const FenceTime::Snapshot* fence) { + return a + fence->getFdCount(); + }); +} + +status_t FrameEventsDelta::flatten(void*& buffer, size_t& size, int*& fds, + size_t& count) const { + if (size < getFlattenedSize() || count < getFdCount()) { + return NO_MEMORY; + } + + if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY || + mIndex > std::numeric_limits<uint16_t>::max()) { + return BAD_VALUE; + } + + FlattenableUtils::write(buffer, size, mFrameNumber); + + // These are static_cast to uint16_t/uint8_t for alignment. + FlattenableUtils::write(buffer, size, static_cast<uint16_t>(mIndex)); + FlattenableUtils::write( + buffer, size, static_cast<uint8_t>(mAddPostCompositeCalled)); + FlattenableUtils::write( + buffer, size, static_cast<uint8_t>(mAddReleaseCalled)); + + FlattenableUtils::write(buffer, size, mPostedTime); + FlattenableUtils::write(buffer, size, mRequestedPresentTime); + FlattenableUtils::write(buffer, size, mLatchTime); + FlattenableUtils::write(buffer, size, mFirstRefreshStartTime); + FlattenableUtils::write(buffer, size, mLastRefreshStartTime); + FlattenableUtils::write(buffer, size, mDequeueReadyTime); + + // Fences + for (auto fence : allFences(this)) { + status_t status = fence->flatten(buffer, size, fds, count); + if (status != NO_ERROR) { + return status; + } + } + return NO_ERROR; +} + +status_t FrameEventsDelta::unflatten(void const*& buffer, size_t& size, + int const*& fds, size_t& count) { + if (size < minFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, mFrameNumber); + + // These were written as uint16_t/uint8_t for alignment. + uint16_t temp16 = 0; + FlattenableUtils::read(buffer, size, temp16); + mIndex = temp16; + if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY) { + return BAD_VALUE; + } + uint8_t temp8 = 0; + FlattenableUtils::read(buffer, size, temp8); + mAddPostCompositeCalled = static_cast<bool>(temp8); + FlattenableUtils::read(buffer, size, temp8); + mAddReleaseCalled = static_cast<bool>(temp8); + + FlattenableUtils::read(buffer, size, mPostedTime); + FlattenableUtils::read(buffer, size, mRequestedPresentTime); + FlattenableUtils::read(buffer, size, mLatchTime); + FlattenableUtils::read(buffer, size, mFirstRefreshStartTime); + FlattenableUtils::read(buffer, size, mLastRefreshStartTime); + FlattenableUtils::read(buffer, size, mDequeueReadyTime); + + // Fences + for (auto fence : allFences(this)) { + status_t status = fence->unflatten(buffer, size, fds, count); + if (status != NO_ERROR) { + return status; + } + } + return NO_ERROR; +} + + +// ============================================================================ +// FrameEventHistoryDelta +// ============================================================================ + +FrameEventHistoryDelta& FrameEventHistoryDelta::operator=( + FrameEventHistoryDelta&& src) { + mCompositorTiming = src.mCompositorTiming; + + if (CC_UNLIKELY(!mDeltas.empty())) { + ALOGE("FrameEventHistoryDelta assign clobbering history."); + } + mDeltas = std::move(src.mDeltas); + ALOGE_IF(src.mDeltas.empty(), "Source mDeltas not empty."); + return *this; +} + +constexpr size_t FrameEventHistoryDelta::minFlattenedSize() { + return sizeof(uint32_t) + // mDeltas.size() + sizeof(mCompositorTiming); +} + +size_t FrameEventHistoryDelta::getFlattenedSize() const { + return minFlattenedSize() + + std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0), + [](size_t a, const FrameEventsDelta& delta) { + return a + delta.getFlattenedSize(); + }); +} + +size_t FrameEventHistoryDelta::getFdCount() const { + return std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0), + [](size_t a, const FrameEventsDelta& delta) { + return a + delta.getFdCount(); + }); +} + +status_t FrameEventHistoryDelta::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const { + if (mDeltas.size() > FrameEventHistory::MAX_FRAME_HISTORY) { + return BAD_VALUE; + } + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, mCompositorTiming); + + FlattenableUtils::write( + buffer, size, static_cast<uint32_t>(mDeltas.size())); + for (auto& d : mDeltas) { + status_t status = d.flatten(buffer, size, fds, count); + if (status != NO_ERROR) { + return status; + } + } + return NO_ERROR; +} + +status_t FrameEventHistoryDelta::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { + if (size < minFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, mCompositorTiming); + + uint32_t deltaCount = 0; + FlattenableUtils::read(buffer, size, deltaCount); + if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) { + return BAD_VALUE; + } + mDeltas.resize(deltaCount); + for (auto& d : mDeltas) { + status_t status = d.unflatten(buffer, size, fds, count); + if (status != NO_ERROR) { + return status; + } + } + return NO_ERROR; +} + + +} // namespace android diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 10e999c228..c654f083b3 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -31,7 +31,6 @@ #include <gui/BufferItem.h> #include <gui/GLConsumer.h> -#include <gui/IGraphicBufferAlloc.h> #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> @@ -157,6 +156,7 @@ GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mCurrentFence(Fence::NO_FENCE), mCurrentTimestamp(0), + mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), mCurrentFrameNumber(0), mDefaultWidth(1), mDefaultHeight(1), @@ -185,6 +185,7 @@ GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget, mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mCurrentFence(Fence::NO_FENCE), mCurrentTimestamp(0), + mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), mCurrentFrameNumber(0), mDefaultWidth(1), mDefaultHeight(1), @@ -321,7 +322,9 @@ status_t GLConsumer::releaseTexImage() { mCurrentCrop.makeInvalid(); mCurrentTransform = 0; mCurrentTimestamp = 0; + mCurrentDataSpace = HAL_DATASPACE_UNKNOWN; mCurrentFence = Fence::NO_FENCE; + mCurrentFenceTime = FenceTime::NO_FENCE; if (mAttached) { // This binds a dummy buffer (mReleasedTexImage). @@ -487,7 +490,9 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; mCurrentTimestamp = item.mTimestamp; + mCurrentDataSpace = item.mDataSpace; mCurrentFence = item.mFence; + mCurrentFenceTime = item.mFenceTime; mCurrentFrameNumber = item.mFrameNumber; computeCurrentTransformMatrixLocked(); @@ -856,6 +861,8 @@ void GLConsumer::computeTransformMatrix(float outTransform[16], switch (buf->getPixelFormat()) { case PIXEL_FORMAT_RGBA_8888: case PIXEL_FORMAT_RGBX_8888: + case PIXEL_FORMAT_RGBA_FP16: + case PIXEL_FORMAT_RGBA_1010102: case PIXEL_FORMAT_RGB_888: case PIXEL_FORMAT_RGB_565: case PIXEL_FORMAT_BGRA_8888: @@ -911,15 +918,26 @@ nsecs_t GLConsumer::getTimestamp() { return mCurrentTimestamp; } +android_dataspace GLConsumer::getCurrentDataSpace() { + GLC_LOGV("getCurrentDataSpace"); + Mutex::Autolock lock(mMutex); + return mCurrentDataSpace; +} + uint64_t GLConsumer::getFrameNumber() { GLC_LOGV("getFrameNumber"); Mutex::Autolock lock(mMutex); return mCurrentFrameNumber; } -sp<GraphicBuffer> GLConsumer::getCurrentBuffer() const { +sp<GraphicBuffer> GLConsumer::getCurrentBuffer(int* outSlot) const { Mutex::Autolock lock(mMutex); - return (mCurrentTextureImage == NULL) ? + + if (outSlot != nullptr) { + *outSlot = mCurrentTexture; + } + + return (mCurrentTextureImage == nullptr) ? NULL : mCurrentTextureImage->graphicBuffer(); } @@ -981,6 +999,11 @@ sp<Fence> GLConsumer::getCurrentFence() const { return mCurrentFence; } +std::shared_ptr<FenceTime> GLConsumer::getCurrentFenceTime() const { + Mutex::Autolock lock(mMutex); + return mCurrentFenceTime; +} + status_t GLConsumer::doGLFenceWait() const { Mutex::Autolock lock(mMutex); return doGLFenceWaitLocked(); diff --git a/libs/gui/GraphicBufferAlloc.cpp b/libs/gui/GraphicBufferAlloc.cpp deleted file mode 100644 index a8f40e0e8b..0000000000 --- a/libs/gui/GraphicBufferAlloc.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - ** - ** Copyright 2012 The Android Open Source Project - ** - ** Licensed under the Apache License Version 2.0(the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing software - ** distributed under the License is distributed on an "AS IS" BASIS - ** WITHOUT 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 <log/log.h> - -#include <ui/GraphicBuffer.h> - -#include <gui/GraphicBufferAlloc.h> - -// ---------------------------------------------------------------------------- -namespace android { -// ---------------------------------------------------------------------------- - -GraphicBufferAlloc::GraphicBufferAlloc() { -} - -GraphicBufferAlloc::~GraphicBufferAlloc() { -} - -sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t width, - uint32_t height, PixelFormat format, uint32_t usage, - std::string requestorName, status_t* error) { - sp<GraphicBuffer> graphicBuffer(new GraphicBuffer( - width, height, format, usage, std::move(requestorName))); - status_t err = graphicBuffer->initCheck(); - *error = err; - if (err != 0 || graphicBuffer->handle == 0) { - if (err == NO_MEMORY) { - GraphicBuffer::dumpAllocationsToSystemLog(); - } - ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%d, h=%d) " - "failed (%s), handle=%p", - width, height, strerror(-err), graphicBuffer->handle); - return 0; - } - return graphicBuffer; -} - -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp index ff7b83a7d6..85ac304ab8 100644 --- a/libs/gui/IConsumerListener.cpp +++ b/libs/gui/IConsumerListener.cpp @@ -14,147 +14,86 @@ * limitations under the License. */ -#include <stdint.h> -#include <sys/types.h> - -#include <binder/IInterface.h> -#include <binder/Parcel.h> - #include <gui/IConsumerListener.h> + #include <gui/BufferItem.h> -// --------------------------------------------------------------------------- namespace android { -// --------------------------------------------------------------------------- -enum { - ON_FRAME_AVAILABLE = IBinder::FIRST_CALL_TRANSACTION, - ON_BUFFER_RELEASED, +namespace { // Anonymous + +enum class Tag : uint32_t { + ON_DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, + ON_FRAME_AVAILABLE, + ON_FRAME_REPLACED, + ON_BUFFERS_RELEASED, ON_SIDEBAND_STREAM_CHANGED, - GET_FRAME_TIMESTAMPS + LAST = ON_SIDEBAND_STREAM_CHANGED, }; -class BpConsumerListener : public BpInterface<IConsumerListener> -{ +} // Anonymous namespace + +class BpConsumerListener : public SafeBpInterface<IConsumerListener> { public: explicit BpConsumerListener(const sp<IBinder>& impl) - : BpInterface<IConsumerListener>(impl) { + : SafeBpInterface<IConsumerListener>(impl, "BpConsumerListener") {} + + ~BpConsumerListener() override; + + void onDisconnect() override { + callRemoteAsync<decltype(&IConsumerListener::onDisconnect)>(Tag::ON_DISCONNECT); } - virtual ~BpConsumerListener(); + void onFrameAvailable(const BufferItem& item) override { + callRemoteAsync<decltype(&IConsumerListener::onFrameAvailable)>(Tag::ON_FRAME_AVAILABLE, + item); + } - virtual void onFrameAvailable(const BufferItem& item) { - Parcel data, reply; - data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor()); - data.write(item); - remote()->transact(ON_FRAME_AVAILABLE, data, &reply, IBinder::FLAG_ONEWAY); + void onFrameReplaced(const BufferItem& item) override { + callRemoteAsync<decltype(&IConsumerListener::onFrameReplaced)>(Tag::ON_FRAME_REPLACED, + item); } - virtual void onBuffersReleased() { - Parcel data, reply; - data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor()); - remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY); + void onBuffersReleased() override { + callRemoteAsync<decltype(&IConsumerListener::onBuffersReleased)>(Tag::ON_BUFFERS_RELEASED); } - virtual void onSidebandStreamChanged() { - Parcel data, reply; - data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor()); - remote()->transact(ON_SIDEBAND_STREAM_CHANGED, data, &reply, IBinder::FLAG_ONEWAY); + void onSidebandStreamChanged() override { + callRemoteAsync<decltype(&IConsumerListener::onSidebandStreamChanged)>( + Tag::ON_SIDEBAND_STREAM_CHANGED); } - virtual bool getFrameTimestamps(uint64_t frameNumber, - FrameTimestamps* outTimestamps) const { - Parcel data, reply; - status_t result = data.writeInterfaceToken( - IConsumerListener::getInterfaceDescriptor()); - if (result != NO_ERROR) { - ALOGE("getFrameTimestamps failed to write token: %d", result); - return false; - } - result = data.writeUint64(frameNumber); - if (result != NO_ERROR) { - ALOGE("getFrameTimestamps failed to write: %d", result); - return false; - } - result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply); - if (result != NO_ERROR) { - ALOGE("getFrameTimestamps failed to transact: %d", result); - return false; - } - bool found = false; - result = reply.readBool(&found); - if (result != NO_ERROR) { - ALOGE("getFrameTimestamps failed to read: %d", result); - return false; - } - if (found) { - result = reply.read(*outTimestamps); - if (result != NO_ERROR) { - ALOGE("getFrameTimestamps failed to read timestamps: %d", - result); - return false; - } - } - return found; + void addAndGetFrameTimestamps(const NewFrameEventsEntry* /*newTimestamps*/, + FrameEventHistoryDelta* /*outDelta*/) override { + LOG_ALWAYS_FATAL("IConsumerListener::addAndGetFrameTimestamps cannot be proxied"); } }; -// Out-of-line virtual method definition to trigger vtable emission in this -// translation unit (see clang warning -Wweak-vtables) -BpConsumerListener::~BpConsumerListener() {} +// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see +// clang warning -Wweak-vtables) +BpConsumerListener::~BpConsumerListener() = default; +ConsumerListener::~ConsumerListener() = default; IMPLEMENT_META_INTERFACE(ConsumerListener, "android.gui.IConsumerListener"); -// ---------------------------------------------------------------------- - -status_t BnConsumerListener::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) { - case ON_FRAME_AVAILABLE: { - CHECK_INTERFACE(IConsumerListener, data, reply); - BufferItem item; - data.read(item); - onFrameAvailable(item); - return NO_ERROR; } - case ON_BUFFER_RELEASED: { - CHECK_INTERFACE(IConsumerListener, data, reply); - onBuffersReleased(); - return NO_ERROR; } - case ON_SIDEBAND_STREAM_CHANGED: { - CHECK_INTERFACE(IConsumerListener, data, reply); - onSidebandStreamChanged(); - return NO_ERROR; } - case GET_FRAME_TIMESTAMPS: { - CHECK_INTERFACE(IConsumerListener, data, reply); - uint64_t frameNumber = 0; - status_t result = data.readUint64(&frameNumber); - if (result != NO_ERROR) { - ALOGE("onTransact failed to read: %d", result); - return result; - } - FrameTimestamps timestamps; - bool found = getFrameTimestamps(frameNumber, ×tamps); - result = reply->writeBool(found); - if (result != NO_ERROR) { - ALOGE("onTransact failed to write: %d", result); - return result; - } - if (found) { - result = reply->write(timestamps); - if (result != NO_ERROR) { - ALOGE("onTransact failed to write timestamps: %d", result); - return result; - } - } - return NO_ERROR; - } +status_t BnConsumerListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) { + return BBinder::onTransact(code, data, reply, flags); + } + auto tag = static_cast<Tag>(code); + switch (tag) { + case Tag::ON_DISCONNECT: + return callLocalAsync(data, reply, &IConsumerListener::onDisconnect); + case Tag::ON_FRAME_AVAILABLE: + return callLocalAsync(data, reply, &IConsumerListener::onFrameAvailable); + case Tag::ON_FRAME_REPLACED: + return callLocalAsync(data, reply, &IConsumerListener::onFrameReplaced); + case Tag::ON_BUFFERS_RELEASED: + return callLocalAsync(data, reply, &IConsumerListener::onBuffersReleased); + case Tag::ON_SIDEBAND_STREAM_CHANGED: + return callLocalAsync(data, reply, &IConsumerListener::onSidebandStreamChanged); } - return BBinder::onTransact(code, data, reply, flags); } -ConsumerListener::~ConsumerListener() = default; - -// --------------------------------------------------------------------------- -}; // namespace android -// --------------------------------------------------------------------------- +} // namespace android diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp index b1d3b00dbb..c0e246fa15 100644 --- a/libs/gui/IDisplayEventConnection.cpp +++ b/libs/gui/IDisplayEventConnection.cpp @@ -14,91 +14,67 @@ * limitations under the License. */ -#include <stdint.h> -#include <sys/types.h> - -#include <utils/Errors.h> -#include <utils/RefBase.h> -#include <utils/Timers.h> - -#include <binder/Parcel.h> -#include <binder/IInterface.h> - #include <gui/IDisplayEventConnection.h> -#include <gui/BitTube.h> + +#include <private/gui/BitTube.h> namespace android { -// ---------------------------------------------------------------------------- -enum { - GET_DATA_CHANNEL = IBinder::FIRST_CALL_TRANSACTION, +namespace { // Anonymous + +enum class Tag : uint32_t { + STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION, SET_VSYNC_RATE, - REQUEST_NEXT_VSYNC + REQUEST_NEXT_VSYNC, + LAST = REQUEST_NEXT_VSYNC, }; -class BpDisplayEventConnection : public BpInterface<IDisplayEventConnection> -{ +} // Anonymous namespace + +class BpDisplayEventConnection : public SafeBpInterface<IDisplayEventConnection> { public: explicit BpDisplayEventConnection(const sp<IBinder>& impl) - : BpInterface<IDisplayEventConnection>(impl) - { - } + : SafeBpInterface<IDisplayEventConnection>(impl, "BpDisplayEventConnection") {} - virtual ~BpDisplayEventConnection(); + ~BpDisplayEventConnection() override; - virtual sp<BitTube> getDataChannel() const - { - Parcel data, reply; - data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor()); - remote()->transact(GET_DATA_CHANNEL, data, &reply); - return new BitTube(reply); + status_t stealReceiveChannel(gui::BitTube* outChannel) override { + return callRemote<decltype( + &IDisplayEventConnection::stealReceiveChannel)>(Tag::STEAL_RECEIVE_CHANNEL, + outChannel); } - virtual void setVsyncRate(uint32_t count) { - Parcel data, reply; - data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor()); - data.writeUint32(count); - remote()->transact(SET_VSYNC_RATE, data, &reply); + status_t setVsyncRate(uint32_t count) override { + return callRemote<decltype(&IDisplayEventConnection::setVsyncRate)>(Tag::SET_VSYNC_RATE, + count); } - virtual void requestNextVsync() { - Parcel data, reply; - data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor()); - remote()->transact(REQUEST_NEXT_VSYNC, data, &reply, IBinder::FLAG_ONEWAY); + void requestNextVsync() override { + callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>( + Tag::REQUEST_NEXT_VSYNC); } }; -// Out-of-line virtual method definition to trigger vtable emission in this -// translation unit (see clang warning -Wweak-vtables) -BpDisplayEventConnection::~BpDisplayEventConnection() {} +// Out-of-line virtual method definition to trigger vtable emission in this translation unit (see +// clang warning -Wweak-vtables) +BpDisplayEventConnection::~BpDisplayEventConnection() = default; IMPLEMENT_META_INTERFACE(DisplayEventConnection, "android.gui.DisplayEventConnection"); -// ---------------------------------------------------------------------------- - -status_t BnDisplayEventConnection::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) { - case GET_DATA_CHANNEL: { - CHECK_INTERFACE(IDisplayEventConnection, data, reply); - sp<BitTube> channel(getDataChannel()); - channel->writeToParcel(reply); - return NO_ERROR; - } - case SET_VSYNC_RATE: { - CHECK_INTERFACE(IDisplayEventConnection, data, reply); - setVsyncRate(data.readUint32()); - return NO_ERROR; - } - case REQUEST_NEXT_VSYNC: { - CHECK_INTERFACE(IDisplayEventConnection, data, reply); - requestNextVsync(); - return NO_ERROR; - } +status_t BnDisplayEventConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) { + return BBinder::onTransact(code, data, reply, flags); + } + auto tag = static_cast<Tag>(code); + switch (tag) { + case Tag::STEAL_RECEIVE_CHANNEL: + return callLocal(data, reply, &IDisplayEventConnection::stealReceiveChannel); + case Tag::SET_VSYNC_RATE: + return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate); + case Tag::REQUEST_NEXT_VSYNC: + return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync); } - return BBinder::onTransact(code, data, reply, flags); } -// ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/libs/gui/IGraphicBufferAlloc.cpp b/libs/gui/IGraphicBufferAlloc.cpp deleted file mode 100644 index 2fb380ccd1..0000000000 --- a/libs/gui/IGraphicBufferAlloc.cpp +++ /dev/null @@ -1,133 +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. - */ - -// tag as surfaceflinger -#define LOG_TAG "SurfaceFlinger" - -#include <stdint.h> -#include <sys/types.h> - -#include <binder/Parcel.h> - -#include <ui/GraphicBuffer.h> - -#include <gui/IGraphicBufferAlloc.h> - -// --------------------------------------------------------------------------- - -namespace android { - -enum { - CREATE_GRAPHIC_BUFFER = IBinder::FIRST_CALL_TRANSACTION, -}; - -class BpGraphicBufferAlloc : public BpInterface<IGraphicBufferAlloc> -{ -public: - explicit BpGraphicBufferAlloc(const sp<IBinder>& impl) - : BpInterface<IGraphicBufferAlloc>(impl) - { - } - - virtual ~BpGraphicBufferAlloc(); - - virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width, - uint32_t height, PixelFormat format, uint32_t usage, - std::string requestorName, status_t* error) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferAlloc::getInterfaceDescriptor()); - data.writeUint32(width); - data.writeUint32(height); - data.writeInt32(static_cast<int32_t>(format)); - data.writeUint32(usage); - if (requestorName.empty()) { - requestorName += "[PID "; - requestorName += std::to_string(getpid()); - requestorName += ']'; - } - data.writeUtf8AsUtf16(requestorName); - remote()->transact(CREATE_GRAPHIC_BUFFER, data, &reply); - sp<GraphicBuffer> graphicBuffer; - status_t result = reply.readInt32(); - if (result == NO_ERROR) { - graphicBuffer = new GraphicBuffer(); - result = reply.read(*graphicBuffer); - if (result != NO_ERROR) { - graphicBuffer.clear(); - } - // reply.readStrongBinder(); - // here we don't even have to read the BufferReference from - // the parcel, it'll die with the parcel. - } - *error = result; - return graphicBuffer; - } -}; - -// Out-of-line virtual method definition to trigger vtable emission in this -// translation unit (see clang warning -Wweak-vtables) -BpGraphicBufferAlloc::~BpGraphicBufferAlloc() {} - -IMPLEMENT_META_INTERFACE(GraphicBufferAlloc, "android.ui.IGraphicBufferAlloc"); - -// ---------------------------------------------------------------------- - -status_t BnGraphicBufferAlloc::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - // codes that don't require permission check - - // BufferReference just keeps a strong reference to a GraphicBuffer until it - // is destroyed (that is, until no local or remote process have a reference - // to it). - class BufferReference : public BBinder { - sp<GraphicBuffer> mBuffer; - public: - explicit BufferReference(const sp<GraphicBuffer>& buffer) : mBuffer(buffer) {} - }; - - - switch (code) { - case CREATE_GRAPHIC_BUFFER: { - CHECK_INTERFACE(IGraphicBufferAlloc, data, reply); - uint32_t width = data.readUint32(); - uint32_t height = data.readUint32(); - PixelFormat format = static_cast<PixelFormat>(data.readInt32()); - uint32_t usage = data.readUint32(); - status_t error = NO_ERROR; - std::string requestorName; - data.readUtf8FromUtf16(&requestorName); - sp<GraphicBuffer> result = createGraphicBuffer(width, height, - format, usage, requestorName, &error); - reply->writeInt32(error); - if (result != 0) { - reply->write(*result); - // We add a BufferReference to this parcel to make sure the - // buffer stays alive until the GraphicBuffer object on - // the other side has been created. - // This is needed so that the buffer handle can be - // registered before the buffer is destroyed on implementations - // that do not use file-descriptors to track their buffers. - reply->writeStrongBinder( new BufferReference(result) ); - } - return NO_ERROR; - } - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -}; // namespace android diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp index 240146455e..a573bee651 100644 --- a/libs/gui/IGraphicBufferConsumer.cpp +++ b/libs/gui/IGraphicBufferConsumer.cpp @@ -14,27 +14,24 @@ * limitations under the License. */ -#include <stdint.h> -#include <sys/types.h> - -#include <utils/Errors.h> -#include <utils/NativeHandle.h> - -#include <binder/Parcel.h> -#include <binder/IInterface.h> +#include <gui/IGraphicBufferConsumer.h> #include <gui/BufferItem.h> #include <gui/IConsumerListener.h> -#include <gui/IGraphicBufferConsumer.h> -#include <ui/GraphicBuffer.h> +#include <binder/Parcel.h> + #include <ui/Fence.h> +#include <ui/GraphicBuffer.h> -#include <system/window.h> +#include <utils/NativeHandle.h> +#include <utils/String8.h> namespace android { -enum { +namespace { // Anonymous namespace + +enum class Tag : uint32_t { ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION, DETACH_BUFFER, ATTACH_BUFFER, @@ -49,443 +46,185 @@ enum { SET_DEFAULT_BUFFER_FORMAT, SET_DEFAULT_BUFFER_DATA_SPACE, SET_CONSUMER_USAGE_BITS, + SET_CONSUMER_IS_PROTECTED, SET_TRANSFORM_HINT, GET_SIDEBAND_STREAM, GET_OCCUPANCY_HISTORY, DISCARD_FREE_BUFFERS, - DUMP, + DUMP_STATE, + LAST = DUMP_STATE, }; +} // Anonymous namespace -class BpGraphicBufferConsumer : public BpInterface<IGraphicBufferConsumer> -{ +class BpGraphicBufferConsumer : public SafeBpInterface<IGraphicBufferConsumer> { public: explicit BpGraphicBufferConsumer(const sp<IBinder>& impl) - : BpInterface<IGraphicBufferConsumer>(impl) - { + : SafeBpInterface<IGraphicBufferConsumer>(impl, "BpGraphicBufferConsumer") {} + + ~BpGraphicBufferConsumer() override; + + status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen, + uint64_t maxFrameNumber) override { + using Signature = decltype(&IGraphicBufferConsumer::acquireBuffer); + return callRemote<Signature>(Tag::ACQUIRE_BUFFER, buffer, presentWhen, maxFrameNumber); } - virtual ~BpGraphicBufferConsumer(); - - virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen, - uint64_t maxFrameNumber) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - data.writeInt64(presentWhen); - data.writeUint64(maxFrameNumber); - status_t result = remote()->transact(ACQUIRE_BUFFER, data, &reply); - if (result != NO_ERROR) { - return result; - } - result = reply.read(*buffer); - if (result != NO_ERROR) { - return result; - } - return reply.readInt32(); + status_t detachBuffer(int slot) override { + using Signature = decltype(&IGraphicBufferConsumer::detachBuffer); + return callRemote<Signature>(Tag::DETACH_BUFFER, slot); } - virtual status_t detachBuffer(int slot) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - data.writeInt32(slot); - status_t result = remote()->transact(DETACH_BUFFER, data, &reply); - if (result != NO_ERROR) { - return result; - } - result = reply.readInt32(); - return result; + status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) override { + using Signature = decltype(&IGraphicBufferConsumer::attachBuffer); + return callRemote<Signature>(Tag::ATTACH_BUFFER, slot, buffer); } - virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - data.write(*buffer.get()); - status_t result = remote()->transact(ATTACH_BUFFER, data, &reply); - if (result != NO_ERROR) { - return result; - } - *slot = reply.readInt32(); - result = reply.readInt32(); - return result; + status_t releaseBuffer(int buf, uint64_t frameNumber, + EGLDisplay display __attribute__((unused)), + EGLSyncKHR fence __attribute__((unused)), + const sp<Fence>& releaseFence) override { + return callRemote<ReleaseBuffer>(Tag::RELEASE_BUFFER, buf, frameNumber, releaseFence); } - virtual status_t releaseBuffer(int buf, uint64_t frameNumber, - EGLDisplay display __attribute__((unused)), EGLSyncKHR fence __attribute__((unused)), - const sp<Fence>& releaseFence) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - data.writeInt32(buf); - data.writeInt64(static_cast<int64_t>(frameNumber)); - data.write(*releaseFence); - status_t result = remote()->transact(RELEASE_BUFFER, data, &reply); - if (result != NO_ERROR) { - return result; - } - return reply.readInt32(); + status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) override { + using Signature = decltype(&IGraphicBufferConsumer::consumerConnect); + return callRemote<Signature>(Tag::CONSUMER_CONNECT, consumer, controlledByApp); } - virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - data.writeStrongBinder(IInterface::asBinder(consumer)); - data.writeInt32(controlledByApp); - status_t result = remote()->transact(CONSUMER_CONNECT, data, &reply); - if (result != NO_ERROR) { - return result; - } - return reply.readInt32(); + status_t consumerDisconnect() override { + return callRemote<decltype(&IGraphicBufferConsumer::consumerDisconnect)>( + Tag::CONSUMER_DISCONNECT); } - virtual status_t consumerDisconnect() { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - status_t result = remote()->transact(CONSUMER_DISCONNECT, data, &reply); - if (result != NO_ERROR) { - return result; - } - return reply.readInt32(); + status_t getReleasedBuffers(uint64_t* slotMask) override { + using Signature = decltype(&IGraphicBufferConsumer::getReleasedBuffers); + return callRemote<Signature>(Tag::GET_RELEASED_BUFFERS, slotMask); } - virtual status_t getReleasedBuffers(uint64_t* slotMask) { - Parcel data, reply; - if (slotMask == NULL) { - ALOGE("getReleasedBuffers: slotMask must not be NULL"); - return BAD_VALUE; - } - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - status_t result = remote()->transact(GET_RELEASED_BUFFERS, data, &reply); - if (result != NO_ERROR) { - return result; - } - *slotMask = static_cast<uint64_t>(reply.readInt64()); - return reply.readInt32(); + status_t setDefaultBufferSize(uint32_t width, uint32_t height) override { + using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferSize); + return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_SIZE, width, height); } - virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - data.writeUint32(width); - data.writeUint32(height); - status_t result = remote()->transact(SET_DEFAULT_BUFFER_SIZE, data, &reply); - if (result != NO_ERROR) { - return result; - } - return reply.readInt32(); + status_t setMaxBufferCount(int bufferCount) override { + using Signature = decltype(&IGraphicBufferConsumer::setMaxBufferCount); + return callRemote<Signature>(Tag::SET_MAX_BUFFER_COUNT, bufferCount); } - virtual status_t setMaxBufferCount(int bufferCount) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - data.writeInt32(bufferCount); - status_t result = remote()->transact(SET_MAX_BUFFER_COUNT, data, &reply); - if (result != NO_ERROR) { - return result; - } - return reply.readInt32(); + status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override { + using Signature = decltype(&IGraphicBufferConsumer::setMaxAcquiredBufferCount); + return callRemote<Signature>(Tag::SET_MAX_ACQUIRED_BUFFER_COUNT, maxAcquiredBuffers); } - virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - data.writeInt32(maxAcquiredBuffers); - status_t result = remote()->transact(SET_MAX_ACQUIRED_BUFFER_COUNT, data, &reply); - if (result != NO_ERROR) { - return result; - } - return reply.readInt32(); + status_t setConsumerName(const String8& name) override { + using Signature = decltype(&IGraphicBufferConsumer::setConsumerName); + return callRemote<Signature>(Tag::SET_CONSUMER_NAME, name); } - virtual void setConsumerName(const String8& name) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - data.writeString8(name); - remote()->transact(SET_CONSUMER_NAME, data, &reply); + status_t setDefaultBufferFormat(PixelFormat defaultFormat) override { + using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferFormat); + return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_FORMAT, defaultFormat); } - virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - data.writeInt32(static_cast<int32_t>(defaultFormat)); - status_t result = remote()->transact(SET_DEFAULT_BUFFER_FORMAT, data, &reply); - if (result != NO_ERROR) { - return result; - } - return reply.readInt32(); + status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) override { + using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferDataSpace); + return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_DATA_SPACE, defaultDataSpace); } - virtual status_t setDefaultBufferDataSpace( - android_dataspace defaultDataSpace) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - data.writeInt32(static_cast<int32_t>(defaultDataSpace)); - status_t result = remote()->transact(SET_DEFAULT_BUFFER_DATA_SPACE, - data, &reply); - if (result != NO_ERROR) { - return result; - } - return reply.readInt32(); + status_t setConsumerUsageBits(uint32_t usage) override { + using Signature = decltype(&IGraphicBufferConsumer::setConsumerUsageBits); + return callRemote<Signature>(Tag::SET_CONSUMER_USAGE_BITS, usage); } - virtual status_t setConsumerUsageBits(uint32_t usage) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - data.writeUint32(usage); - status_t result = remote()->transact(SET_CONSUMER_USAGE_BITS, data, &reply); - if (result != NO_ERROR) { - return result; - } - return reply.readInt32(); + status_t setConsumerIsProtected(bool isProtected) override { + using Signature = decltype(&IGraphicBufferConsumer::setConsumerIsProtected); + return callRemote<Signature>(Tag::SET_CONSUMER_IS_PROTECTED, isProtected); } - virtual status_t setTransformHint(uint32_t hint) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - data.writeUint32(hint); - status_t result = remote()->transact(SET_TRANSFORM_HINT, data, &reply); - if (result != NO_ERROR) { - return result; - } - return reply.readInt32(); + status_t setTransformHint(uint32_t hint) override { + using Signature = decltype(&IGraphicBufferConsumer::setTransformHint); + return callRemote<Signature>(Tag::SET_TRANSFORM_HINT, hint); } - virtual sp<NativeHandle> getSidebandStream() const { - Parcel data, reply; - status_t err; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - if ((err = remote()->transact(GET_SIDEBAND_STREAM, data, &reply)) != NO_ERROR) { - return NULL; - } - sp<NativeHandle> stream; - if (reply.readInt32()) { - stream = NativeHandle::create(reply.readNativeHandle(), true); - } - return stream; + status_t getSidebandStream(sp<NativeHandle>* outStream) const override { + using Signature = decltype(&IGraphicBufferConsumer::getSidebandStream); + return callRemote<Signature>(Tag::GET_SIDEBAND_STREAM, outStream); } - virtual status_t getOccupancyHistory(bool forceFlush, - std::vector<OccupancyTracker::Segment>* outHistory) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - status_t error = data.writeBool(forceFlush); - if (error != NO_ERROR) { - return error; - } - error = remote()->transact(GET_OCCUPANCY_HISTORY, data, - &reply); - if (error != NO_ERROR) { - return error; - } - error = reply.readParcelableVector(outHistory); - if (error != NO_ERROR) { - return error; - } - status_t result = NO_ERROR; - error = reply.readInt32(&result); - if (error != NO_ERROR) { - return error; - } - return result; + status_t getOccupancyHistory(bool forceFlush, + std::vector<OccupancyTracker::Segment>* outHistory) override { + using Signature = decltype(&IGraphicBufferConsumer::getOccupancyHistory); + return callRemote<Signature>(Tag::GET_OCCUPANCY_HISTORY, forceFlush, outHistory); } - virtual status_t discardFreeBuffers() { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - status_t error = remote()->transact(DISCARD_FREE_BUFFERS, data, &reply); - if (error != NO_ERROR) { - return error; - } - int32_t result = NO_ERROR; - error = reply.readInt32(&result); - if (error != NO_ERROR) { - return error; - } - return result; + status_t discardFreeBuffers() override { + return callRemote<decltype(&IGraphicBufferConsumer::discardFreeBuffers)>( + Tag::DISCARD_FREE_BUFFERS); } - virtual void dumpState(String8& result, const char* prefix) const { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); - data.writeString8(result); - data.writeString8(String8(prefix ? prefix : "")); - remote()->transact(DUMP, data, &reply); - reply.readString8(); + status_t dumpState(const String8& prefix, String8* outResult) const override { + using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const; + return callRemote<Signature>(Tag::DUMP_STATE, prefix, outResult); } }; -// Out-of-line virtual method definition to trigger vtable emission in this -// translation unit (see clang warning -Wweak-vtables) -BpGraphicBufferConsumer::~BpGraphicBufferConsumer() {} +// Out-of-line virtual method definition to trigger vtable emission in this translation unit +// (see clang warning -Wweak-vtables) +BpGraphicBufferConsumer::~BpGraphicBufferConsumer() = default; IMPLEMENT_META_INTERFACE(GraphicBufferConsumer, "android.gui.IGraphicBufferConsumer"); -// ---------------------------------------------------------------------- - -status_t BnGraphicBufferConsumer::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) { - case ACQUIRE_BUFFER: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - BufferItem item; - int64_t presentWhen = data.readInt64(); - uint64_t maxFrameNumber = data.readUint64(); - status_t result = acquireBuffer(&item, presentWhen, maxFrameNumber); - status_t err = reply->write(item); - if (err) return err; - reply->writeInt32(result); - return NO_ERROR; - } - case DETACH_BUFFER: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - int slot = data.readInt32(); - int result = detachBuffer(slot); - reply->writeInt32(result); - return NO_ERROR; - } - case ATTACH_BUFFER: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - sp<GraphicBuffer> buffer = new GraphicBuffer(); - data.read(*buffer.get()); - int slot = -1; - int result = attachBuffer(&slot, buffer); - reply->writeInt32(slot); - reply->writeInt32(result); - return NO_ERROR; - } - case RELEASE_BUFFER: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - int buf = data.readInt32(); - uint64_t frameNumber = static_cast<uint64_t>(data.readInt64()); - sp<Fence> releaseFence = new Fence(); - status_t err = data.read(*releaseFence); - if (err) return err; - status_t result = releaseBuffer(buf, frameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence); - reply->writeInt32(result); - return NO_ERROR; - } - case CONSUMER_CONNECT: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - sp<IConsumerListener> consumer = IConsumerListener::asInterface( data.readStrongBinder() ); - bool controlledByApp = data.readInt32(); - status_t result = consumerConnect(consumer, controlledByApp); - reply->writeInt32(result); - return NO_ERROR; - } - case CONSUMER_DISCONNECT: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - status_t result = consumerDisconnect(); - reply->writeInt32(result); - return NO_ERROR; - } - case GET_RELEASED_BUFFERS: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - uint64_t slotMask = 0; - status_t result = getReleasedBuffers(&slotMask); - reply->writeInt64(static_cast<int64_t>(slotMask)); - reply->writeInt32(result); - return NO_ERROR; - } - case SET_DEFAULT_BUFFER_SIZE: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - uint32_t width = data.readUint32(); - uint32_t height = data.readUint32(); - status_t result = setDefaultBufferSize(width, height); - reply->writeInt32(result); - return NO_ERROR; - } - case SET_MAX_BUFFER_COUNT: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - int bufferCount = data.readInt32(); - status_t result = setMaxBufferCount(bufferCount); - reply->writeInt32(result); - return NO_ERROR; - } - case SET_MAX_ACQUIRED_BUFFER_COUNT: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - int maxAcquiredBuffers = data.readInt32(); - status_t result = setMaxAcquiredBufferCount(maxAcquiredBuffers); - reply->writeInt32(result); - return NO_ERROR; - } - case SET_CONSUMER_NAME: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - setConsumerName( data.readString8() ); - return NO_ERROR; - } - case SET_DEFAULT_BUFFER_FORMAT: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - PixelFormat defaultFormat = static_cast<PixelFormat>(data.readInt32()); - status_t result = setDefaultBufferFormat(defaultFormat); - reply->writeInt32(result); - return NO_ERROR; - } - case SET_DEFAULT_BUFFER_DATA_SPACE: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - android_dataspace defaultDataSpace = - static_cast<android_dataspace>(data.readInt32()); - status_t result = setDefaultBufferDataSpace(defaultDataSpace); - reply->writeInt32(result); - return NO_ERROR; - } - case SET_CONSUMER_USAGE_BITS: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - uint32_t usage = data.readUint32(); - status_t result = setConsumerUsageBits(usage); - reply->writeInt32(result); - return NO_ERROR; - } - case SET_TRANSFORM_HINT: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - uint32_t hint = data.readUint32(); - status_t result = setTransformHint(hint); - reply->writeInt32(result); - return NO_ERROR; - } - case GET_SIDEBAND_STREAM: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - sp<NativeHandle> stream = getSidebandStream(); - reply->writeInt32(static_cast<int32_t>(stream != NULL)); - if (stream != NULL) { - reply->writeNativeHandle(stream->handle()); - } - return NO_ERROR; - } - case GET_OCCUPANCY_HISTORY: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - bool forceFlush = false; - status_t error = data.readBool(&forceFlush); - if (error != NO_ERROR) { - return error; - } - std::vector<OccupancyTracker::Segment> history; - status_t result = getOccupancyHistory(forceFlush, &history); - error = reply->writeParcelableVector(history); - if (error != NO_ERROR) { - return error; - } - error = reply->writeInt32(result); - if (error != NO_ERROR) { - return error; - } - return NO_ERROR; - } - case DISCARD_FREE_BUFFERS: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - status_t result = discardFreeBuffers(); - status_t error = reply->writeInt32(result); - return error; - } - case DUMP: { - CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); - String8 result = data.readString8(); - String8 prefix = data.readString8(); - static_cast<IGraphicBufferConsumer*>(this)->dumpState(result, prefix); - reply->writeString8(result); - return NO_ERROR; +status_t BnGraphicBufferConsumer::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) { + return BBinder::onTransact(code, data, reply, flags); + } + auto tag = static_cast<Tag>(code); + switch (tag) { + case Tag::ACQUIRE_BUFFER: + return callLocal(data, reply, &IGraphicBufferConsumer::acquireBuffer); + case Tag::DETACH_BUFFER: + return callLocal(data, reply, &IGraphicBufferConsumer::detachBuffer); + case Tag::ATTACH_BUFFER: + return callLocal(data, reply, &IGraphicBufferConsumer::attachBuffer); + case Tag::RELEASE_BUFFER: + return callLocal(data, reply, &IGraphicBufferConsumer::releaseHelper); + case Tag::CONSUMER_CONNECT: + return callLocal(data, reply, &IGraphicBufferConsumer::consumerConnect); + case Tag::CONSUMER_DISCONNECT: + return callLocal(data, reply, &IGraphicBufferConsumer::consumerDisconnect); + case Tag::GET_RELEASED_BUFFERS: + return callLocal(data, reply, &IGraphicBufferConsumer::getReleasedBuffers); + case Tag::SET_DEFAULT_BUFFER_SIZE: + return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferSize); + case Tag::SET_MAX_BUFFER_COUNT: + return callLocal(data, reply, &IGraphicBufferConsumer::setMaxBufferCount); + case Tag::SET_MAX_ACQUIRED_BUFFER_COUNT: + return callLocal(data, reply, &IGraphicBufferConsumer::setMaxAcquiredBufferCount); + case Tag::SET_CONSUMER_NAME: + return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerName); + case Tag::SET_DEFAULT_BUFFER_FORMAT: + return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferFormat); + case Tag::SET_DEFAULT_BUFFER_DATA_SPACE: + return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferDataSpace); + case Tag::SET_CONSUMER_USAGE_BITS: + return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerUsageBits); + case Tag::SET_CONSUMER_IS_PROTECTED: + return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerIsProtected); + case Tag::SET_TRANSFORM_HINT: + return callLocal(data, reply, &IGraphicBufferConsumer::setTransformHint); + case Tag::GET_SIDEBAND_STREAM: + return callLocal(data, reply, &IGraphicBufferConsumer::getSidebandStream); + case Tag::GET_OCCUPANCY_HISTORY: + return callLocal(data, reply, &IGraphicBufferConsumer::getOccupancyHistory); + case Tag::DISCARD_FREE_BUFFERS: + return callLocal(data, reply, &IGraphicBufferConsumer::discardFreeBuffers); + case Tag::DUMP_STATE: { + using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const; + return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::dumpState); } } - return BBinder::onTransact(code, data, reply, flags); } -}; // namespace android +} // namespace android diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 0149a3f403..8481b502a0 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -20,18 +20,25 @@ #include <utils/Errors.h> #include <utils/NativeHandle.h> #include <utils/RefBase.h> +#include <utils/String8.h> #include <utils/Timers.h> #include <utils/Vector.h> #include <binder/Parcel.h> #include <binder/IInterface.h> +#include <gui/BufferQueueDefs.h> #include <gui/IGraphicBufferProducer.h> #include <gui/IProducerListener.h> +#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> + namespace android { // ---------------------------------------------------------------------------- +using ::android::hardware::graphics::bufferqueue::V1_0::utils:: + H2BGraphicBufferProducer; + enum { REQUEST_BUFFER = IBinder::FIRST_CALL_TRANSACTION, DEQUEUE_BUFFER, @@ -118,24 +125,35 @@ public: } virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, uint32_t width, - uint32_t height, PixelFormat format, uint32_t usage) { + uint32_t height, PixelFormat format, uint32_t usage, + FrameEventHistoryDelta* outTimestamps) { Parcel data, reply; + bool getFrameTimestamps = (outTimestamps != nullptr); + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeUint32(width); data.writeUint32(height); data.writeInt32(static_cast<int32_t>(format)); data.writeUint32(usage); + data.writeBool(getFrameTimestamps); + status_t result = remote()->transact(DEQUEUE_BUFFER, data, &reply); if (result != NO_ERROR) { return result; } + *buf = reply.readInt32(); - bool nonNull = reply.readInt32(); - if (nonNull) { - *fence = new Fence(); - result = reply.read(**fence); + *fence = new Fence(); + result = reply.read(**fence); + if (result != NO_ERROR) { + fence->clear(); + return result; + } + if (getFrameTimestamps) { + result = reply.read(*outTimestamps); if (result != NO_ERROR) { - fence->clear(); + ALOGE("IGBP::dequeueBuffer failed to read timestamps: %d", + result); return result; } } @@ -203,22 +221,37 @@ public: if (result != NO_ERROR) { return result; } + *slot = reply.readInt32(); result = reply.readInt32(); + if (result == NO_ERROR && + (*slot < 0 || *slot >= BufferQueueDefs::NUM_BUFFER_SLOTS)) { + ALOGE("attachBuffer returned invalid slot %d", *slot); + android_errorWriteLog(0x534e4554, "37478824"); + return UNKNOWN_ERROR; + } + return result; } virtual status_t queueBuffer(int buf, const QueueBufferInput& input, QueueBufferOutput* output) { Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(buf); data.write(input); + status_t result = remote()->transact(QUEUE_BUFFER, data, &reply); if (result != NO_ERROR) { return result; } - memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output)); + + result = reply.read(*output); + if (result != NO_ERROR) { + return result; + } + result = reply.readInt32(); return result; } @@ -265,7 +298,7 @@ public: if (result != NO_ERROR) { return result; } - memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output)); + reply.read(*output); result = reply.readInt32(); return result; } @@ -422,40 +455,24 @@ public: return result; } - virtual bool getFrameTimestamps(uint64_t frameNumber, - FrameTimestamps* outTimestamps) const { + virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) { Parcel data, reply; status_t result = data.writeInterfaceToken( IGraphicBufferProducer::getInterfaceDescriptor()); if (result != NO_ERROR) { - ALOGE("getFrameTimestamps failed to write token: %d", result); - return false; - } - result = data.writeUint64(frameNumber); - if (result != NO_ERROR) { - ALOGE("getFrameTimestamps failed to write: %d", result); - return false; + ALOGE("IGBP::getFrameTimestamps failed to write token: %d", result); + return; } result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply); if (result != NO_ERROR) { - ALOGE("getFrameTimestamps failed to transact: %d", result); - return false; + ALOGE("IGBP::getFrameTimestamps failed to transact: %d", result); + return; } - bool found = false; - result = reply.readBool(&found); + result = reply.read(*outDelta); if (result != NO_ERROR) { - ALOGE("getFrameTimestamps failed to read: %d", result); - return false; + ALOGE("IGBP::getFrameTimestamps failed to read timestamps: %d", + result); } - if (found) { - result = reply.read(*outTimestamps); - if (result != NO_ERROR) { - ALOGE("getFrameTimestamps failed to read timestamps: %d", - result); - return false; - } - } - return found; } virtual status_t getUniqueId(uint64_t* outId) const { @@ -482,7 +499,123 @@ public: // translation unit (see clang warning -Wweak-vtables) BpGraphicBufferProducer::~BpGraphicBufferProducer() {} -IMPLEMENT_META_INTERFACE(GraphicBufferProducer, "android.gui.IGraphicBufferProducer"); +class HpGraphicBufferProducer : public HpInterface< + BpGraphicBufferProducer, H2BGraphicBufferProducer> { +public: + HpGraphicBufferProducer(const sp<IBinder>& base) : PBase(base) {} + + status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override { + return mBase->requestBuffer(slot, buf); + } + + status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override { + return mBase->setMaxDequeuedBufferCount(maxDequeuedBuffers); + } + + status_t setAsyncMode(bool async) override { + return mBase->setAsyncMode(async); + } + + status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h, + PixelFormat format, uint32_t usage, + FrameEventHistoryDelta* outTimestamps) override { + return mBase->dequeueBuffer( + slot, fence, w, h, format, usage, outTimestamps); + } + + status_t detachBuffer(int slot) override { + return mBase->detachBuffer(slot); + } + + status_t detachNextBuffer( + sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override { + return mBase->detachNextBuffer(outBuffer, outFence); + } + + status_t attachBuffer( + int* outSlot, const sp<GraphicBuffer>& buffer) override { + return mBase->attachBuffer(outSlot, buffer); + } + + status_t queueBuffer( + int slot, + const QueueBufferInput& input, + QueueBufferOutput* output) override { + return mBase->queueBuffer(slot, input, output); + } + + status_t cancelBuffer(int slot, const sp<Fence>& fence) override { + return mBase->cancelBuffer(slot, fence); + } + + int query(int what, int* value) override { + return mBase->query(what, value); + } + + status_t connect( + const sp<IProducerListener>& listener, + int api, bool producerControlledByApp, + QueueBufferOutput* output) override { + return mBase->connect(listener, api, producerControlledByApp, output); + } + + status_t disconnect( + int api, DisconnectMode mode = DisconnectMode::Api) override { + return mBase->disconnect(api, mode); + } + + status_t setSidebandStream(const sp<NativeHandle>& stream) override { + return mBase->setSidebandStream(stream); + } + + void allocateBuffers(uint32_t width, uint32_t height, + PixelFormat format, uint32_t usage) override { + return mBase->allocateBuffers(width, height, format, usage); + } + + status_t allowAllocation(bool allow) override { + return mBase->allowAllocation(allow); + } + + status_t setGenerationNumber(uint32_t generationNumber) override { + return mBase->setGenerationNumber(generationNumber); + } + + String8 getConsumerName() const override { + return mBase->getConsumerName(); + } + + status_t setSharedBufferMode(bool sharedBufferMode) override { + return mBase->setSharedBufferMode(sharedBufferMode); + } + + status_t setAutoRefresh(bool autoRefresh) override { + return mBase->setAutoRefresh(autoRefresh); + } + + status_t setDequeueTimeout(nsecs_t timeout) override { + return mBase->setDequeueTimeout(timeout); + } + + status_t getLastQueuedBuffer( + sp<GraphicBuffer>* outBuffer, + sp<Fence>* outFence, + float outTransformMatrix[16]) override { + return mBase->getLastQueuedBuffer( + outBuffer, outFence, outTransformMatrix); + } + + void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override { + return mBase->getFrameTimestamps(outDelta); + } + + status_t getUniqueId(uint64_t* outId) const override { + return mBase->getUniqueId(outId); + } +}; + +IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer, + "android.gui.IGraphicBufferProducer"); // ---------------------------------------------------------------------- @@ -522,14 +655,18 @@ status_t BnGraphicBufferProducer::onTransact( uint32_t height = data.readUint32(); PixelFormat format = static_cast<PixelFormat>(data.readInt32()); uint32_t usage = data.readUint32(); + bool getTimestamps = data.readBool(); + int buf = 0; - sp<Fence> fence; + sp<Fence> fence = Fence::NO_FENCE; + FrameEventHistoryDelta frameTimestamps; int result = dequeueBuffer(&buf, &fence, width, height, format, - usage); + usage, getTimestamps ? &frameTimestamps : nullptr); + reply->writeInt32(buf); - reply->writeInt32(fence != NULL); - if (fence != NULL) { - reply->write(*fence); + reply->write(*fence); + if (getTimestamps) { + reply->write(frameTimestamps); } reply->writeInt32(result); return NO_ERROR; @@ -573,14 +710,14 @@ status_t BnGraphicBufferProducer::onTransact( } case QUEUE_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + int buf = data.readInt32(); QueueBufferInput input(data); - QueueBufferOutput* const output = - reinterpret_cast<QueueBufferOutput *>( - reply->writeInplace(sizeof(QueueBufferOutput))); - memset(output, 0, sizeof(QueueBufferOutput)); - status_t result = queueBuffer(buf, input, output); + QueueBufferOutput output; + status_t result = queueBuffer(buf, input, &output); + reply->write(output); reply->writeInt32(result); + return NO_ERROR; } case CANCEL_BUFFER: { @@ -611,11 +748,9 @@ status_t BnGraphicBufferProducer::onTransact( } int api = data.readInt32(); bool producerControlledByApp = data.readInt32(); - QueueBufferOutput* const output = - reinterpret_cast<QueueBufferOutput *>( - reply->writeInplace(sizeof(QueueBufferOutput))); - memset(output, 0, sizeof(QueueBufferOutput)); - status_t res = connect(listener, api, producerControlledByApp, output); + QueueBufferOutput output; + status_t res = connect(listener, api, producerControlledByApp, &output); + reply->write(output); reply->writeInt32(res); return NO_ERROR; } @@ -718,26 +853,14 @@ status_t BnGraphicBufferProducer::onTransact( } case GET_FRAME_TIMESTAMPS: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); - uint64_t frameNumber = 0; - status_t result = data.readUint64(&frameNumber); + FrameEventHistoryDelta frameTimestamps; + getFrameTimestamps(&frameTimestamps); + status_t result = reply->write(frameTimestamps); if (result != NO_ERROR) { - ALOGE("onTransact failed to read: %d", result); - return result; - } - FrameTimestamps timestamps; - bool found = getFrameTimestamps(frameNumber, ×tamps); - result = reply->writeBool(found); - if (result != NO_ERROR) { - ALOGE("onTransact failed to write: %d", result); + ALOGE("BnGBP::GET_FRAME_TIMESTAMPS failed to write buffer: %d", + result); return result; } - if (found) { - result = reply->write(timestamps); - if (result != NO_ERROR) { - ALOGE("onTransact failed to write timestamps: %d", result); - return result; - } - } return NO_ERROR; } case GET_UNIQUE_ID: { @@ -764,16 +887,21 @@ IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) parcel.read(*this); } +constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() { + return sizeof(timestamp) + + sizeof(isAutoTimestamp) + + sizeof(dataSpace) + + sizeof(crop) + + sizeof(scalingMode) + + sizeof(transform) + + sizeof(stickyTransform) + + sizeof(getFrameTimestamps); +} + size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const { - return sizeof(timestamp) - + sizeof(isAutoTimestamp) - + sizeof(dataSpace) - + sizeof(crop) - + sizeof(scalingMode) - + sizeof(transform) - + sizeof(stickyTransform) - + fence->getFlattenedSize() - + surfaceDamage.getFlattenedSize(); + return minFlattenedSize() + + fence->getFlattenedSize() + + surfaceDamage.getFlattenedSize(); } size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const { @@ -786,6 +914,7 @@ status_t IGraphicBufferProducer::QueueBufferInput::flatten( if (size < getFlattenedSize()) { return NO_MEMORY; } + FlattenableUtils::write(buffer, size, timestamp); FlattenableUtils::write(buffer, size, isAutoTimestamp); FlattenableUtils::write(buffer, size, dataSpace); @@ -793,6 +922,8 @@ status_t IGraphicBufferProducer::QueueBufferInput::flatten( 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; @@ -803,16 +934,7 @@ status_t IGraphicBufferProducer::QueueBufferInput::flatten( status_t IGraphicBufferProducer::QueueBufferInput::unflatten( void const*& buffer, size_t& size, int const*& fds, size_t& count) { - size_t minNeeded = - sizeof(timestamp) - + sizeof(isAutoTimestamp) - + sizeof(dataSpace) - + sizeof(crop) - + sizeof(scalingMode) - + sizeof(transform) - + sizeof(stickyTransform); - - if (size < minNeeded) { + if (size < minFlattenedSize()) { return NO_MEMORY; } @@ -823,6 +945,7 @@ status_t IGraphicBufferProducer::QueueBufferInput::unflatten( 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); @@ -832,4 +955,56 @@ status_t IGraphicBufferProducer::QueueBufferInput::unflatten( return surfaceDamage.unflatten(buffer, size); } +// ---------------------------------------------------------------------------- +constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() { + return sizeof(width) + + sizeof(height) + + sizeof(transformHint) + + sizeof(numPendingBuffers) + + sizeof(nextFrameNumber) + + sizeof(bufferReplaced); +} + +size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const { + return minFlattenedSize() + frameTimestamps.getFlattenedSize(); +} + +size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const { + return frameTimestamps.getFdCount(); +} + +status_t IGraphicBufferProducer::QueueBufferOutput::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const +{ + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, width); + FlattenableUtils::write(buffer, size, height); + FlattenableUtils::write(buffer, size, transformHint); + FlattenableUtils::write(buffer, size, numPendingBuffers); + FlattenableUtils::write(buffer, size, nextFrameNumber); + FlattenableUtils::write(buffer, size, bufferReplaced); + + return frameTimestamps.flatten(buffer, size, fds, count); +} + +status_t IGraphicBufferProducer::QueueBufferOutput::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) +{ + if (size < minFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, width); + FlattenableUtils::read(buffer, size, height); + FlattenableUtils::read(buffer, size, transformHint); + FlattenableUtils::read(buffer, size, numPendingBuffers); + FlattenableUtils::read(buffer, size, nextFrameNumber); + FlattenableUtils::read(buffer, size, bufferReplaced); + + return frameTimestamps.unflatten(buffer, size, fds, count); +} + }; // namespace android diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 0a8e6a555e..0a0d112af6 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -21,14 +21,13 @@ #include <sys/types.h> #include <binder/Parcel.h> -#include <binder/IMemory.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> -#include <gui/BitTube.h> #include <gui/IDisplayEventConnection.h> -#include <gui/ISurfaceComposer.h> #include <gui/IGraphicBufferProducer.h> +#include <gui/ISurfaceComposer.h> +#include <gui/ISurfaceComposerClient.h> #include <private/gui/LayerState.h> @@ -44,8 +43,6 @@ namespace android { -class IDisplayEventConnection; - class BpSurfaceComposer : public BpInterface<ISurfaceComposer> { public: @@ -64,12 +61,14 @@ public: return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); } - virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc() + virtual sp<ISurfaceComposerClient> createScopedConnection( + const sp<IGraphicBufferProducer>& parent) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::CREATE_GRAPHIC_BUFFER_ALLOC, data, &reply); - return interface_cast<IGraphicBufferAlloc>(reply.readStrongBinder()); + data.writeStrongBinder(IInterface::asBinder(parent)); + remote()->transact(BnSurfaceComposer::CREATE_SCOPED_CONNECTION, data, &reply); + return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); } virtual void setTransactionState( @@ -104,7 +103,7 @@ public: virtual status_t captureScreen(const sp<IBinder>& display, const sp<IGraphicBufferProducer>& producer, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - uint32_t minLayerZ, uint32_t maxLayerZ, + int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, ISurfaceComposer::Rotation rotation) { @@ -115,8 +114,8 @@ public: data.write(sourceCrop); data.writeUint32(reqWidth); data.writeUint32(reqHeight); - data.writeUint32(minLayerZ); - data.writeUint32(maxLayerZ); + data.writeInt32(minLayerZ); + data.writeInt32(maxLayerZ); data.writeInt32(static_cast<int32_t>(useIdentityTransform)); data.writeInt32(static_cast<int32_t>(rotation)); remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); @@ -158,7 +157,51 @@ public: return result != 0; } - virtual sp<IDisplayEventConnection> createDisplayEventConnection() + virtual status_t getSupportedFrameTimestamps( + std::vector<FrameEvent>* outSupported) const { + if (!outSupported) { + return UNEXPECTED_NULL; + } + outSupported->clear(); + + Parcel data, reply; + + status_t err = data.writeInterfaceToken( + ISurfaceComposer::getInterfaceDescriptor()); + if (err != NO_ERROR) { + return err; + } + + err = remote()->transact( + BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS, + data, &reply); + if (err != NO_ERROR) { + return err; + } + + int32_t result = 0; + err = reply.readInt32(&result); + if (err != NO_ERROR) { + return err; + } + if (result != NO_ERROR) { + return result; + } + + std::vector<int32_t> supported; + err = reply.readInt32Vector(&supported); + if (err != NO_ERROR) { + return err; + } + + outSupported->reserve(supported.size()); + for (int32_t s : supported) { + outSupported->push_back(static_cast<FrameEvent>(s)); + } + return NO_ERROR; + } + + virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource) { Parcel data, reply; sp<IDisplayEventConnection> result; @@ -167,6 +210,7 @@ public: if (err != NO_ERROR) { return result; } + data.writeInt32(static_cast<int32_t>(vsyncSource)); err = remote()->transact( BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION, data, &reply); @@ -379,10 +423,52 @@ public: } result = reply.readInt32(); if (result == NO_ERROR) { - result = reply.readParcelable(outCapabilities); + result = reply.read(*outCapabilities); } return result; } + + virtual status_t enableVSyncInjections(bool enable) { + Parcel data, reply; + status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("enableVSyncInjections failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeBool(enable); + if (result != NO_ERROR) { + ALOGE("enableVSyncInjections failed to writeBool: %d", result); + return result; + } + result = remote()->transact(BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS, + data, &reply, TF_ONE_WAY); + if (result != NO_ERROR) { + ALOGE("enableVSyncInjections failed to transact: %d", result); + return result; + } + return result; + } + + virtual status_t injectVSync(nsecs_t when) { + Parcel data, reply; + status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("injectVSync failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeInt64(when); + if (result != NO_ERROR) { + ALOGE("injectVSync failed to writeInt64: %d", result); + return result; + } + result = remote()->transact(BnSurfaceComposer::INJECT_VSYNC, data, &reply, TF_ONE_WAY); + if (result != NO_ERROR) { + ALOGE("injectVSync failed to transact: %d", result); + return result; + } + return result; + } + }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -403,9 +489,11 @@ status_t BnSurfaceComposer::onTransact( reply->writeStrongBinder(b); return NO_ERROR; } - case CREATE_GRAPHIC_BUFFER_ALLOC: { + case CREATE_SCOPED_CONNECTION: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> b = IInterface::asBinder(createGraphicBufferAlloc()); + sp<IGraphicBufferProducer> bufferProducer = + interface_cast<IGraphicBufferProducer>(data.readStrongBinder()); + sp<IBinder> b = IInterface::asBinder(createScopedConnection(bufferProducer)); reply->writeStrongBinder(b); return NO_ERROR; } @@ -458,8 +546,8 @@ status_t BnSurfaceComposer::onTransact( data.read(sourceCrop); uint32_t reqWidth = data.readUint32(); uint32_t reqHeight = data.readUint32(); - uint32_t minLayerZ = data.readUint32(); - uint32_t maxLayerZ = data.readUint32(); + int32_t minLayerZ = data.readInt32(); + int32_t maxLayerZ = data.readInt32(); bool useIdentityTransform = static_cast<bool>(data.readInt32()); int32_t rotation = data.readInt32(); @@ -478,9 +566,29 @@ status_t BnSurfaceComposer::onTransact( reply->writeInt32(result); return NO_ERROR; } + case GET_SUPPORTED_FRAME_TIMESTAMPS: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + std::vector<FrameEvent> supportedTimestamps; + status_t result = getSupportedFrameTimestamps(&supportedTimestamps); + status_t err = reply->writeInt32(result); + if (err != NO_ERROR) { + return err; + } + if (result != NO_ERROR) { + return result; + } + + std::vector<int32_t> supported; + supported.reserve(supportedTimestamps.size()); + for (FrameEvent s : supportedTimestamps) { + supported.push_back(static_cast<int32_t>(s)); + } + return reply->writeInt32Vector(supported); + } case CREATE_DISPLAY_EVENT_CONNECTION: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IDisplayEventConnection> connection(createDisplayEventConnection()); + sp<IDisplayEventConnection> connection(createDisplayEventConnection( + static_cast<ISurfaceComposer::VsyncSource>(data.readInt32()))); reply->writeStrongBinder(IInterface::asBinder(connection)); return NO_ERROR; } @@ -631,10 +739,30 @@ status_t BnSurfaceComposer::onTransact( result = getHdrCapabilities(display, &capabilities); reply->writeInt32(result); if (result == NO_ERROR) { - reply->writeParcelable(capabilities); + reply->write(capabilities); } return NO_ERROR; } + case ENABLE_VSYNC_INJECTIONS: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + bool enable = false; + status_t result = data.readBool(&enable); + if (result != NO_ERROR) { + ALOGE("enableVSyncInjections failed to readBool: %d", result); + return result; + } + return enableVSyncInjections(enable); + } + case INJECT_VSYNC: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + int64_t when = 0; + status_t result = data.readInt64(&when); + if (result != NO_ERROR) { + ALOGE("enableVSyncInjections failed to readInt64: %d", result); + return result; + } + return injectVSync(when); + } default: { return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp index 47cb0473e8..679f44b57b 100644 --- a/libs/gui/ISurfaceComposerClient.cpp +++ b/libs/gui/ISurfaceComposerClient.cpp @@ -17,112 +17,61 @@ // tag as surfaceflinger #define LOG_TAG "SurfaceFlinger" -#include <stdio.h> -#include <stdint.h> -#include <sys/types.h> - -#include <binder/Parcel.h> -#include <binder/IMemory.h> -#include <binder/IPCThreadState.h> -#include <binder/IServiceManager.h> - -#include <ui/Point.h> -#include <ui/Rect.h> +#include <gui/ISurfaceComposerClient.h> #include <gui/IGraphicBufferProducer.h> -#include <gui/ISurfaceComposerClient.h> -#include <private/gui/LayerState.h> -// --------------------------------------------------------------------------- +#include <binder/SafeInterface.h> + +#include <ui/FrameStats.h> namespace android { -enum { +namespace { // Anonymous + +enum class Tag : uint32_t { CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION, DESTROY_SURFACE, CLEAR_LAYER_FRAME_STATS, GET_LAYER_FRAME_STATS, - GET_TRANSFORM_TO_DISPLAY_INVERSE + LAST = GET_LAYER_FRAME_STATS, }; -class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient> -{ +} // Anonymous namespace + +class BpSurfaceComposerClient : public SafeBpInterface<ISurfaceComposerClient> { public: explicit BpSurfaceComposerClient(const sp<IBinder>& impl) - : BpInterface<ISurfaceComposerClient>(impl) { - } - - virtual ~BpSurfaceComposerClient(); - - virtual status_t createSurface(const String8& name, uint32_t width, - uint32_t height, PixelFormat format, uint32_t flags, - sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp) { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); - data.writeString8(name); - data.writeUint32(width); - data.writeUint32(height); - data.writeInt32(static_cast<int32_t>(format)); - data.writeUint32(flags); - remote()->transact(CREATE_SURFACE, data, &reply); - *handle = reply.readStrongBinder(); - *gbp = interface_cast<IGraphicBufferProducer>(reply.readStrongBinder()); - return reply.readInt32(); + : SafeBpInterface<ISurfaceComposerClient>(impl, "BpSurfaceComposerClient") {} + + ~BpSurfaceComposerClient() override; + + status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format, + uint32_t flags, const sp<IBinder>& parent, uint32_t windowType, + uint32_t ownerUid, sp<IBinder>* handle, + sp<IGraphicBufferProducer>* gbp) override { + return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE, + name, width, height, + format, flags, parent, + windowType, ownerUid, + handle, gbp); } - virtual status_t destroySurface(const sp<IBinder>& handle) { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); - data.writeStrongBinder(handle); - remote()->transact(DESTROY_SURFACE, data, &reply); - return reply.readInt32(); + status_t destroySurface(const sp<IBinder>& handle) override { + return callRemote<decltype(&ISurfaceComposerClient::destroySurface)>(Tag::DESTROY_SURFACE, + handle); } - virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); - data.writeStrongBinder(handle); - remote()->transact(CLEAR_LAYER_FRAME_STATS, data, &reply); - return reply.readInt32(); + status_t clearLayerFrameStats(const sp<IBinder>& handle) const override { + return callRemote<decltype( + &ISurfaceComposerClient::clearLayerFrameStats)>(Tag::CLEAR_LAYER_FRAME_STATS, + handle); } - virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); - data.writeStrongBinder(handle); - remote()->transact(GET_LAYER_FRAME_STATS, data, &reply); - reply.read(*outStats); - return reply.readInt32(); - } - - virtual status_t getTransformToDisplayInverse(const sp<IBinder>& handle, - bool* outTransformToDisplayInverse) const { - Parcel data, reply; - status_t result = - data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); - if (result != NO_ERROR) { - return result; - } - result = data.writeStrongBinder(handle); - if (result != NO_ERROR) { - return result; - } - result = remote()->transact(GET_TRANSFORM_TO_DISPLAY_INVERSE, data, &reply); - if (result != NO_ERROR) { - return result; - } - int transformInverse; - result = reply.readInt32(&transformInverse); - if (result != NO_ERROR) { - return result; - } - *outTransformToDisplayInverse = transformInverse != 0 ? true : false; - status_t result2 = reply.readInt32(&result); - if (result2 != NO_ERROR) { - return result2; - } - return result; + status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const override { + return callRemote<decltype( + &ISurfaceComposerClient::getLayerFrameStats)>(Tag::GET_LAYER_FRAME_STATS, handle, + outStats); } }; @@ -134,69 +83,22 @@ IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClie // ---------------------------------------------------------------------- -status_t BnSurfaceComposerClient::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) { - case CREATE_SURFACE: { - CHECK_INTERFACE(ISurfaceComposerClient, data, reply); - String8 name = data.readString8(); - uint32_t width = data.readUint32(); - uint32_t height = data.readUint32(); - PixelFormat format = static_cast<PixelFormat>(data.readInt32()); - uint32_t createFlags = data.readUint32(); - sp<IBinder> handle; - sp<IGraphicBufferProducer> gbp; - status_t result = createSurface(name, width, height, format, - createFlags, &handle, &gbp); - reply->writeStrongBinder(handle); - reply->writeStrongBinder(IInterface::asBinder(gbp)); - reply->writeInt32(result); - return NO_ERROR; - } - case DESTROY_SURFACE: { - CHECK_INTERFACE(ISurfaceComposerClient, data, reply); - reply->writeInt32(destroySurface( data.readStrongBinder() ) ); - return NO_ERROR; - } - case CLEAR_LAYER_FRAME_STATS: { - CHECK_INTERFACE(ISurfaceComposerClient, data, reply); - sp<IBinder> handle = data.readStrongBinder(); - status_t result = clearLayerFrameStats(handle); - reply->writeInt32(result); - return NO_ERROR; - } - case GET_LAYER_FRAME_STATS: { - CHECK_INTERFACE(ISurfaceComposerClient, data, reply); - sp<IBinder> handle = data.readStrongBinder(); - FrameStats stats; - status_t result = getLayerFrameStats(handle, &stats); - reply->write(stats); - reply->writeInt32(result); - return NO_ERROR; - } - case GET_TRANSFORM_TO_DISPLAY_INVERSE: { - CHECK_INTERFACE(ISurfaceComposerClient, data, reply); - sp<IBinder> handle; - status_t result = data.readStrongBinder(&handle); - if (result != NO_ERROR) { - return result; - } - bool transformInverse = false; - result = getTransformToDisplayInverse(handle, &transformInverse); - if (result != NO_ERROR) { - return result; - } - result = reply->writeInt32(transformInverse ? 1 : 0); - if (result != NO_ERROR) { - return result; - } - result = reply->writeInt32(NO_ERROR); - return result; - } - default: - return BBinder::onTransact(code, data, reply, flags); +status_t BnSurfaceComposerClient::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) { + return BBinder::onTransact(code, data, reply, flags); + } + auto tag = static_cast<Tag>(code); + switch (tag) { + case Tag::CREATE_SURFACE: + return callLocal(data, reply, &ISurfaceComposerClient::createSurface); + case Tag::DESTROY_SURFACE: + return callLocal(data, reply, &ISurfaceComposerClient::destroySurface); + case Tag::CLEAR_LAYER_FRAME_STATS: + return callLocal(data, reply, &ISurfaceComposerClient::clearLayerFrameStats); + case Tag::GET_LAYER_FRAME_STATS: + return callLocal(data, reply, &ISurfaceComposerClient::getLayerFrameStats); } } -}; // namespace android +} // namespace android diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index d1c576e4c5..9b06e63610 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -28,7 +28,7 @@ status_t layer_state_t::write(Parcel& output) const output.writeUint32(what); output.writeFloat(x); output.writeFloat(y); - output.writeUint32(z); + output.writeInt32(z); output.writeUint32(w); output.writeUint32(h); output.writeUint32(layerStack); @@ -39,9 +39,12 @@ status_t layer_state_t::write(Parcel& output) const output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix; output.write(crop); output.write(finalCrop); - output.writeStrongBinder(handle); + output.writeStrongBinder(barrierHandle); + output.writeStrongBinder(reparentHandle); output.writeUint64(frameNumber); output.writeInt32(overrideScalingMode); + output.writeStrongBinder(IInterface::asBinder(barrierGbp)); + output.writeStrongBinder(relativeLayerHandle); output.write(transparentRegion); return NO_ERROR; } @@ -52,7 +55,7 @@ status_t layer_state_t::read(const Parcel& input) what = input.readUint32(); x = input.readFloat(); y = input.readFloat(); - z = input.readUint32(); + z = input.readInt32(); w = input.readUint32(); h = input.readUint32(); layerStack = input.readUint32(); @@ -67,9 +70,13 @@ status_t layer_state_t::read(const Parcel& input) } input.read(crop); input.read(finalCrop); - handle = input.readStrongBinder(); + barrierHandle = input.readStrongBinder(); + reparentHandle = input.readStrongBinder(); frameNumber = input.readUint64(); overrideScalingMode = input.readInt32(); + barrierGbp = + interface_cast<IGraphicBufferProducer>(input.readStrongBinder()); + relativeLayerHandle = input.readStrongBinder(); input.read(transparentRegion); return NO_ERROR; } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index d1a9cbbbda..7b2b5c37f1 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -18,25 +18,28 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 -#include <android/native_window.h> +#include <gui/Surface.h> -#include <binder/Parcel.h> +#include <android/native_window.h> #include <utils/Log.h> #include <utils/Trace.h> #include <utils/NativeHandle.h> +#include <ui/DisplayStatInfo.h> #include <ui/Fence.h> +#include <ui/HdrCapabilities.h> #include <ui/Region.h> +#include <gui/BufferItem.h> #include <gui/IProducerListener.h> -#include <gui/ISurfaceComposer.h> -#include <gui/SurfaceComposerClient.h> -#include <gui/GLConsumer.h> -#include <gui/Surface.h> +#include <gui/ISurfaceComposer.h> #include <private/gui/ComposerService.h> +#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> +#include <configstore/Utils.h> + namespace android { Surface::Surface( @@ -49,7 +52,10 @@ Surface::Surface( mAutoRefresh(false), mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT), mSharedBufferHasBeenQueued(false), - mNextFrameNumber(1) + mQueriedSupportedTimestamps(false), + mFrameTimestampsSupportsPresent(false), + mEnableFrameTimestamps(false), + mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>()) { // Initialize the ANativeWindow function pointers. ANativeWindow::setSwapInterval = hook_setSwapInterval; @@ -93,6 +99,14 @@ Surface::~Surface() { } } +sp<ISurfaceComposer> Surface::composerService() const { + return ComposerService::getComposerService(); +} + +nsecs_t Surface::now() const { + return systemTime(); +} + sp<IGraphicBufferProducer> Surface::getIGraphicBufferProducer() const { return mGraphicBufferProducer; } @@ -135,37 +149,224 @@ status_t Surface::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, outTransformMatrix); } -bool Surface::getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime, - nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime, - nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime, - nsecs_t* outReleaseTime) { +status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) { ATRACE_CALL(); - FrameTimestamps timestamps; - bool found = mGraphicBufferProducer->getFrameTimestamps(frameNumber, - ×tamps); - if (found) { - if (outPostedTime) { - *outPostedTime = timestamps.postedTime; - } - if (outAcquireTime) { - *outAcquireTime = timestamps.acquireTime; - } - if (outRefreshStartTime) { - *outRefreshStartTime = timestamps.refreshStartTime; - } - if (outGlCompositionDoneTime) { - *outGlCompositionDoneTime = timestamps.glCompositionDoneTime; - } - if (outDisplayRetireTime) { - *outDisplayRetireTime = timestamps.displayRetireTime; + DisplayStatInfo stats; + status_t err = composerService()->getDisplayStats(NULL, &stats); + + *outRefreshDuration = stats.vsyncPeriod; + + return NO_ERROR; +} + +void Surface::enableFrameTimestamps(bool enable) { + Mutex::Autolock lock(mMutex); + // If going from disabled to enabled, get the initial values for + // compositor and display timing. + if (!mEnableFrameTimestamps && enable) { + FrameEventHistoryDelta delta; + mGraphicBufferProducer->getFrameTimestamps(&delta); + mFrameEventHistory->applyDelta(delta); + } + mEnableFrameTimestamps = enable; +} + +status_t Surface::getCompositorTiming( + nsecs_t* compositeDeadline, nsecs_t* compositeInterval, + nsecs_t* compositeToPresentLatency) { + Mutex::Autolock lock(mMutex); + if (!mEnableFrameTimestamps) { + return INVALID_OPERATION; + } + + if (compositeDeadline != nullptr) { + *compositeDeadline = + mFrameEventHistory->getNextCompositeDeadline(now()); + } + if (compositeInterval != nullptr) { + *compositeInterval = mFrameEventHistory->getCompositeInterval(); + } + if (compositeToPresentLatency != nullptr) { + *compositeToPresentLatency = + mFrameEventHistory->getCompositeToPresentLatency(); + } + return NO_ERROR; +} + +static bool checkConsumerForUpdates( + const FrameEvents* e, const uint64_t lastFrameNumber, + const nsecs_t* outLatchTime, + const nsecs_t* outFirstRefreshStartTime, + const nsecs_t* outLastRefreshStartTime, + const nsecs_t* outGpuCompositionDoneTime, + const nsecs_t* outDisplayPresentTime, + const nsecs_t* outDequeueReadyTime, + const nsecs_t* outReleaseTime) { + bool checkForLatch = (outLatchTime != nullptr) && !e->hasLatchInfo(); + bool checkForFirstRefreshStart = (outFirstRefreshStartTime != nullptr) && + !e->hasFirstRefreshStartInfo(); + bool checkForGpuCompositionDone = (outGpuCompositionDoneTime != nullptr) && + !e->hasGpuCompositionDoneInfo(); + bool checkForDisplayPresent = (outDisplayPresentTime != nullptr) && + !e->hasDisplayPresentInfo(); + + // LastRefreshStart, DequeueReady, and Release are never available for the + // last frame. + bool checkForLastRefreshStart = (outLastRefreshStartTime != nullptr) && + !e->hasLastRefreshStartInfo() && + (e->frameNumber != lastFrameNumber); + bool checkForDequeueReady = (outDequeueReadyTime != nullptr) && + !e->hasDequeueReadyInfo() && (e->frameNumber != lastFrameNumber); + bool checkForRelease = (outReleaseTime != nullptr) && + !e->hasReleaseInfo() && (e->frameNumber != lastFrameNumber); + + // RequestedPresent and Acquire info are always available producer-side. + return checkForLatch || checkForFirstRefreshStart || + checkForLastRefreshStart || checkForGpuCompositionDone || + checkForDisplayPresent || checkForDequeueReady || checkForRelease; +} + +static void getFrameTimestamp(nsecs_t *dst, const nsecs_t& src) { + if (dst != nullptr) { + // We always get valid timestamps for these eventually. + *dst = (src == FrameEvents::TIMESTAMP_PENDING) ? + NATIVE_WINDOW_TIMESTAMP_PENDING : src; + } +} + +static void getFrameTimestampFence(nsecs_t *dst, + const std::shared_ptr<FenceTime>& src, bool fenceShouldBeKnown) { + if (dst != nullptr) { + if (!fenceShouldBeKnown) { + *dst = NATIVE_WINDOW_TIMESTAMP_PENDING; + return; } - if (outReleaseTime) { - *outReleaseTime = timestamps.releaseTime; + + nsecs_t signalTime = src->getSignalTime(); + *dst = (signalTime == Fence::SIGNAL_TIME_PENDING) ? + NATIVE_WINDOW_TIMESTAMP_PENDING : + (signalTime == Fence::SIGNAL_TIME_INVALID) ? + NATIVE_WINDOW_TIMESTAMP_INVALID : + signalTime; + } +} + +status_t Surface::getFrameTimestamps(uint64_t frameNumber, + nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime, + nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime, + nsecs_t* outLastRefreshStartTime, nsecs_t* outGpuCompositionDoneTime, + nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime, + nsecs_t* outReleaseTime) { + ATRACE_CALL(); + + Mutex::Autolock lock(mMutex); + + if (!mEnableFrameTimestamps) { + return INVALID_OPERATION; + } + + // Verify the requested timestamps are supported. + querySupportedTimestampsLocked(); + if (outDisplayPresentTime != nullptr && !mFrameTimestampsSupportsPresent) { + return BAD_VALUE; + } + + FrameEvents* events = mFrameEventHistory->getFrame(frameNumber); + if (events == nullptr) { + // If the entry isn't available in the producer, it's definitely not + // available in the consumer. + return NAME_NOT_FOUND; + } + + // Update our cache of events if the requested events are not available. + if (checkConsumerForUpdates(events, mLastFrameNumber, + outLatchTime, outFirstRefreshStartTime, outLastRefreshStartTime, + outGpuCompositionDoneTime, outDisplayPresentTime, + outDequeueReadyTime, outReleaseTime)) { + FrameEventHistoryDelta delta; + mGraphicBufferProducer->getFrameTimestamps(&delta); + mFrameEventHistory->applyDelta(delta); + events = mFrameEventHistory->getFrame(frameNumber); + } + + if (events == nullptr) { + // The entry was available before the update, but was overwritten + // after the update. Make sure not to send the wrong frame's data. + return NAME_NOT_FOUND; + } + + getFrameTimestamp(outRequestedPresentTime, events->requestedPresentTime); + getFrameTimestamp(outLatchTime, events->latchTime); + getFrameTimestamp(outFirstRefreshStartTime, events->firstRefreshStartTime); + getFrameTimestamp(outLastRefreshStartTime, events->lastRefreshStartTime); + getFrameTimestamp(outDequeueReadyTime, events->dequeueReadyTime); + + getFrameTimestampFence(outAcquireTime, events->acquireFence, + events->hasAcquireInfo()); + getFrameTimestampFence(outGpuCompositionDoneTime, + events->gpuCompositionDoneFence, + events->hasGpuCompositionDoneInfo()); + getFrameTimestampFence(outDisplayPresentTime, events->displayPresentFence, + events->hasDisplayPresentInfo()); + getFrameTimestampFence(outReleaseTime, events->releaseFence, + events->hasReleaseInfo()); + + return NO_ERROR; +} + +using namespace android::hardware::configstore; +using namespace android::hardware::configstore::V1_0; + +status_t Surface::getWideColorSupport(bool* supported) { + ATRACE_CALL(); + + sp<IBinder> display( + composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + Vector<android_color_mode_t> colorModes; + status_t err = + composerService()->getDisplayColorModes(display, &colorModes); + + if (err) + return err; + + bool wideColorBoardConfig = + getBool<ISurfaceFlingerConfigs, + &ISurfaceFlingerConfigs::hasWideColorDisplay>(false); + + *supported = false; + for (android_color_mode_t colorMode : colorModes) { + switch (colorMode) { + case HAL_COLOR_MODE_DISPLAY_P3: + case HAL_COLOR_MODE_ADOBE_RGB: + case HAL_COLOR_MODE_DCI_P3: + if (wideColorBoardConfig) { + *supported = true; + } + break; + default: + break; } - return true; } - return false; + + return NO_ERROR; +} + +status_t Surface::getHdrSupport(bool* supported) { + ATRACE_CALL(); + + sp<IBinder> display( + composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + HdrCapabilities hdrCapabilities; + status_t err = + composerService()->getHdrCapabilities(display, &hdrCapabilities); + + if (err) + return err; + + *supported = !hdrCapabilities.getSupportedHdrTypes().empty(); + + return NO_ERROR; } int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) { @@ -271,9 +472,13 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { uint32_t reqHeight; PixelFormat reqFormat; uint32_t reqUsage; + bool enableFrameTimestamps; { Mutex::Autolock lock(mMutex); + if (mReportRemovedBuffers) { + mRemovedBuffers.clear(); + } reqWidth = mReqWidth ? mReqWidth : mUserWidth; reqHeight = mReqHeight ? mReqHeight : mUserHeight; @@ -281,6 +486,8 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { reqFormat = mReqFormat; reqUsage = mReqUsage; + enableFrameTimestamps = mEnableFrameTimestamps; + if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot != BufferItem::INVALID_BUFFER_SLOT) { sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer); @@ -294,10 +501,13 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { int buf = -1; sp<Fence> fence; - nsecs_t now = systemTime(); + nsecs_t startTime = systemTime(); + + FrameEventHistoryDelta frameTimestamps; status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, - reqWidth, reqHeight, reqFormat, reqUsage); - mLastDequeueDuration = systemTime() - now; + reqWidth, reqHeight, reqFormat, reqUsage, + enableFrameTimestamps ? &frameTimestamps : nullptr); + mLastDequeueDuration = systemTime() - startTime; if (result < 0) { ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer" @@ -306,8 +516,17 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { return result; } + if (buf < 0 || buf >= NUM_BUFFER_SLOTS) { + ALOGE("dequeueBuffer: IGraphicBufferProducer returned invalid slot number %d", buf); + android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging + return FAILED_TRANSACTION; + } + Mutex::Autolock lock(mMutex); + // Write this while holding the mutex + mLastDequeueStartTime = startTime; + sp<GraphicBuffer>& gbuf(mSlots[buf].buffer); // this should never happen @@ -317,7 +536,14 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { freeAllBuffers(); } - if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) { + if (enableFrameTimestamps) { + mFrameEventHistory->applyDelta(frameTimestamps); + } + + if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) { + if (mReportRemovedBuffers && (gbuf != nullptr)) { + mRemovedBuffers.push_back(gbuf); + } result = mGraphicBufferProducer->requestBuffer(buf, &gbuf); if (result != NO_ERROR) { ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result); @@ -435,7 +661,7 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { IGraphicBufferProducer::QueueBufferOutput output; IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp, mDataSpace, crop, mScalingMode, mTransform ^ mStickyTransform, - fence, mStickyTransform); + fence, mStickyTransform, mEnableFrameTimestamps); if (mConnectedToCpu || mDirtyRegion.bounds() == Rect::INVALID_RECT) { input.setSurfaceDamage(Region::INVALID_REGION); @@ -507,17 +733,31 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err); } - uint32_t numPendingBuffers = 0; - uint32_t hint = 0; - output.deflate(&mDefaultWidth, &mDefaultHeight, &hint, - &numPendingBuffers, &mNextFrameNumber); + if (mEnableFrameTimestamps) { + mFrameEventHistory->applyDelta(output.frameTimestamps); + // Update timestamps with the local acquire fence. + // The consumer doesn't send it back to prevent us from having two + // file descriptors of the same fence. + mFrameEventHistory->updateAcquireFence(mNextFrameNumber, + std::make_shared<FenceTime>(std::move(fence))); + + // Cache timestamps of signaled fences so we can close their file + // descriptors. + mFrameEventHistory->updateSignalTimes(); + } + + mLastFrameNumber = mNextFrameNumber; + + mDefaultWidth = output.width; + mDefaultHeight = output.height; + mNextFrameNumber = output.nextFrameNumber; // Disable transform hint if sticky transform is set. if (mStickyTransform == 0) { - mTransformHint = hint; + mTransformHint = output.transformHint; } - mConsumerRunningBehind = (numPendingBuffers >= 2); + mConsumerRunningBehind = (output.numPendingBuffers >= 2); if (!mConnectedToCpu) { // Clear surface damage back to full-buffer @@ -533,6 +773,29 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { return err; } +void Surface::querySupportedTimestampsLocked() const { + // mMutex must be locked when calling this method. + + if (mQueriedSupportedTimestamps) { + return; + } + mQueriedSupportedTimestamps = true; + + std::vector<FrameEvent> supportedFrameTimestamps; + status_t err = composerService()->getSupportedFrameTimestamps( + &supportedFrameTimestamps); + + if (err != NO_ERROR) { + return; + } + + for (auto sft : supportedFrameTimestamps) { + if (sft == FrameEvent::DISPLAY_PRESENT) { + mFrameTimestampsSupportsPresent = true; + } + } +} + int Surface::query(int what, int* value) const { ATRACE_CALL(); ALOGV("Surface::query"); @@ -546,9 +809,8 @@ int Surface::query(int what, int* value) const { } break; case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: { - sp<ISurfaceComposer> composer( - ComposerService::getComposerService()); - if (composer->authenticateSurfaceTexture(mGraphicBufferProducer)) { + if (composerService()->authenticateSurfaceTexture( + mGraphicBufferProducer)) { *value = 1; } else { *value = 0; @@ -595,6 +857,15 @@ int Surface::query(int what, int* value) const { static_cast<int>(durationUs); return NO_ERROR; } + case NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT: { + querySupportedTimestampsLocked(); + *value = mFrameTimestampsSupportsPresent ? 1 : 0; + return NO_ERROR; + } + case NATIVE_WINDOW_IS_VALID: { + *value = mGraphicBufferProducer != nullptr ? 1 : 0; + return NO_ERROR; + } } } return mGraphicBufferProducer->query(what, value); @@ -670,9 +941,27 @@ int Surface::perform(int operation, va_list args) case NATIVE_WINDOW_SET_AUTO_REFRESH: res = dispatchSetAutoRefresh(args); break; + case NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION: + res = dispatchGetDisplayRefreshCycleDuration(args); + break; + case NATIVE_WINDOW_GET_NEXT_FRAME_ID: + res = dispatchGetNextFrameId(args); + break; + case NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS: + res = dispatchEnableFrameTimestamps(args); + break; + case NATIVE_WINDOW_GET_COMPOSITOR_TIMING: + res = dispatchGetCompositorTiming(args); + break; case NATIVE_WINDOW_GET_FRAME_TIMESTAMPS: res = dispatchGetFrameTimestamps(args); break; + case NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT: + res = dispatchGetWideColorSupport(args); + break; + case NATIVE_WINDOW_GET_HDR_SUPPORT: + res = dispatchGetHdrSupport(args); + break; default: res = NAME_NOT_FOUND; break; @@ -793,18 +1082,57 @@ int Surface::dispatchSetAutoRefresh(va_list args) { return setAutoRefresh(autoRefresh); } +int Surface::dispatchGetDisplayRefreshCycleDuration(va_list args) { + nsecs_t* outRefreshDuration = va_arg(args, int64_t*); + return getDisplayRefreshCycleDuration(outRefreshDuration); +} + +int Surface::dispatchGetNextFrameId(va_list args) { + uint64_t* nextFrameId = va_arg(args, uint64_t*); + *nextFrameId = getNextFrameNumber(); + return NO_ERROR; +} + +int Surface::dispatchEnableFrameTimestamps(va_list args) { + bool enable = va_arg(args, int); + enableFrameTimestamps(enable); + return NO_ERROR; +} + +int Surface::dispatchGetCompositorTiming(va_list args) { + nsecs_t* compositeDeadline = va_arg(args, int64_t*); + nsecs_t* compositeInterval = va_arg(args, int64_t*); + nsecs_t* compositeToPresentLatency = va_arg(args, int64_t*); + return getCompositorTiming(compositeDeadline, compositeInterval, + compositeToPresentLatency); +} + int Surface::dispatchGetFrameTimestamps(va_list args) { - uint32_t framesAgo = va_arg(args, uint32_t); - nsecs_t* outPostedTime = va_arg(args, int64_t*); + uint64_t frameId = va_arg(args, uint64_t); + nsecs_t* outRequestedPresentTime = va_arg(args, int64_t*); nsecs_t* outAcquireTime = va_arg(args, int64_t*); - nsecs_t* outRefreshStartTime = va_arg(args, int64_t*); - nsecs_t* outGlCompositionDoneTime = va_arg(args, int64_t*); - nsecs_t* outDisplayRetireTime = va_arg(args, int64_t*); + nsecs_t* outLatchTime = va_arg(args, int64_t*); + nsecs_t* outFirstRefreshStartTime = va_arg(args, int64_t*); + nsecs_t* outLastRefreshStartTime = va_arg(args, int64_t*); + nsecs_t* outGpuCompositionDoneTime = va_arg(args, int64_t*); + nsecs_t* outDisplayPresentTime = va_arg(args, int64_t*); + nsecs_t* outDequeueReadyTime = va_arg(args, int64_t*); nsecs_t* outReleaseTime = va_arg(args, int64_t*); - bool ret = getFrameTimestamps(getNextFrameNumber() - 1 - framesAgo, - outPostedTime, outAcquireTime, outRefreshStartTime, - outGlCompositionDoneTime, outDisplayRetireTime, outReleaseTime); - return ret ? NO_ERROR : BAD_VALUE; + return getFrameTimestamps(frameId, + outRequestedPresentTime, outAcquireTime, outLatchTime, + outFirstRefreshStartTime, outLastRefreshStartTime, + outGpuCompositionDoneTime, outDisplayPresentTime, + outDequeueReadyTime, outReleaseTime); +} + +int Surface::dispatchGetWideColorSupport(va_list args) { + bool* outSupport = va_arg(args, bool*); + return getWideColorSupport(outSupport); +} + +int Surface::dispatchGetHdrSupport(va_list args) { + bool* outSupport = va_arg(args, bool*); + return getHdrSupport(outSupport); } int Surface::connect(int api) { @@ -813,23 +1141,28 @@ int Surface::connect(int api) { } int Surface::connect(int api, const sp<IProducerListener>& listener) { + return connect(api, listener, false); +} + +int Surface::connect( + int api, const sp<IProducerListener>& listener, bool reportBufferRemoval) { ATRACE_CALL(); ALOGV("Surface::connect"); Mutex::Autolock lock(mMutex); IGraphicBufferProducer::QueueBufferOutput output; + mReportRemovedBuffers = reportBufferRemoval; int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output); if (err == NO_ERROR) { - uint32_t numPendingBuffers = 0; - uint32_t hint = 0; - output.deflate(&mDefaultWidth, &mDefaultHeight, &hint, - &numPendingBuffers, &mNextFrameNumber); + mDefaultWidth = output.width; + mDefaultHeight = output.height; + mNextFrameNumber = output.nextFrameNumber; // Disable transform hint if sticky transform is set. if (mStickyTransform == 0) { - mTransformHint = hint; + mTransformHint = output.transformHint; } - mConsumerRunningBehind = (numPendingBuffers >= 2); + mConsumerRunningBehind = (output.numPendingBuffers >= 2); } if (!err && api == NATIVE_WINDOW_API_CPU) { mConnectedToCpu = true; @@ -848,6 +1181,7 @@ int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) { ATRACE_CALL(); ALOGV("Surface::disconnect"); Mutex::Autolock lock(mMutex); + mRemovedBuffers.clear(); mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT; mSharedBufferHasBeenQueued = false; freeAllBuffers(); @@ -879,6 +1213,9 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer, } Mutex::Autolock lock(mMutex); + if (mReportRemovedBuffers) { + mRemovedBuffers.clear(); + } sp<GraphicBuffer> buffer(NULL); sp<Fence> fence(NULL); @@ -897,7 +1234,10 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer, for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { if (mSlots[i].buffer != NULL && - mSlots[i].buffer->handle == buffer->handle) { + mSlots[i].buffer->getId() == buffer->getId()) { + if (mReportRemovedBuffers) { + mRemovedBuffers.push_back(mSlots[i].buffer); + } mSlots[i].buffer = NULL; } } @@ -911,6 +1251,9 @@ int Surface::attachBuffer(ANativeWindowBuffer* buffer) ALOGV("Surface::attachBuffer"); Mutex::Autolock lock(mMutex); + if (mReportRemovedBuffers) { + mRemovedBuffers.clear(); + } sp<GraphicBuffer> graphicBuffer(static_cast<GraphicBuffer*>(buffer)); uint32_t priorGeneration = graphicBuffer->mGenerationNumber; @@ -923,6 +1266,9 @@ int Surface::attachBuffer(ANativeWindowBuffer* buffer) graphicBuffer->mGenerationNumber = priorGeneration; return result; } + if (mReportRemovedBuffers && (mSlots[attachedSlot].buffer != nullptr)) { + mRemovedBuffers.push_back(mSlots[attachedSlot].buffer); + } mSlots[attachedSlot].buffer = graphicBuffer; return NO_ERROR; @@ -1356,70 +1702,21 @@ status_t Surface::getUniqueId(uint64_t* outId) const { return mGraphicBufferProducer->getUniqueId(outId); } -namespace view { - -status_t Surface::writeToParcel(Parcel* parcel) const { - return writeToParcel(parcel, false); -} - -status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const { - if (parcel == nullptr) return BAD_VALUE; - - status_t res = OK; - - if (!nameAlreadyWritten) { - res = parcel->writeString16(name); - if (res != OK) return res; - - /* isSingleBuffered defaults to no */ - res = parcel->writeInt32(0); - if (res != OK) return res; - } - - res = parcel->writeStrongBinder( - IGraphicBufferProducer::asBinder(graphicBufferProducer)); - - return res; -} - -status_t Surface::readFromParcel(const Parcel* parcel) { - return readFromParcel(parcel, false); +nsecs_t Surface::getLastDequeueStartTime() const { + Mutex::Autolock lock(mMutex); + return mLastDequeueStartTime; } -status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) { - if (parcel == nullptr) return BAD_VALUE; - - status_t res = OK; - if (!nameAlreadyRead) { - name = readMaybeEmptyString16(parcel); - // Discard this for now - int isSingleBuffered; - res = parcel->readInt32(&isSingleBuffered); - if (res != OK) { - return res; - } +status_t Surface::getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out) { + if (out == nullptr) { + ALOGE("%s: out must not be null!", __FUNCTION__); + return BAD_VALUE; } - sp<IBinder> binder; - - res = parcel->readStrongBinder(&binder); - if (res != OK) return res; - - graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder); - + Mutex::Autolock lock(mMutex); + *out = mRemovedBuffers; + mRemovedBuffers.clear(); return OK; } -String16 Surface::readMaybeEmptyString16(const Parcel* parcel) { - size_t len; - const char16_t* str = parcel->readString16Inplace(&len); - if (str != nullptr) { - return String16(str, len); - } else { - return String16(); - } -} - -} // namespace view - }; // namespace android diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 43506e9191..8c8384399c 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -26,17 +26,18 @@ #include <utils/String8.h> #include <utils/threads.h> -#include <binder/IMemory.h> #include <binder/IServiceManager.h> #include <system/graphics.h> #include <ui/DisplayInfo.h> +#include <gui/BufferItemConsumer.h> #include <gui/CpuConsumer.h> #include <gui/IGraphicBufferProducer.h> #include <gui/ISurfaceComposer.h> #include <gui/ISurfaceComposerClient.h> +#include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <private/gui/ComposerService.h> @@ -129,6 +130,8 @@ class Composer : public Singleton<Composer> void openGlobalTransactionImpl(); void closeGlobalTransactionImpl(bool synchronous); void setAnimationTransactionImpl(); + status_t enableVSyncInjectionsImpl(bool enable); + status_t injectVSyncImpl(nsecs_t when); layer_state_t* getLayerStateLocked( const sp<SurfaceComposerClient>& client, const sp<IBinder>& id); @@ -145,7 +148,9 @@ public: status_t setSize(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, uint32_t w, uint32_t h); status_t setLayer(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, - uint32_t z); + int32_t z); + status_t setRelativeLayer(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, + const sp<IBinder>& relativeTo, int32_t z); status_t setFlags(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, uint32_t flags, uint32_t mask); status_t setTransparentRegionHint( @@ -154,7 +159,7 @@ public: status_t setAlpha(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, float alpha); status_t setMatrix(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, - float dsdx, float dtdx, float dsdy, float dtdy); + float dsdx, float dtdx, float dtdy, float dsdy); status_t setOrientation(int orientation); status_t setCrop(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, const Rect& crop); @@ -165,6 +170,14 @@ public: status_t deferTransactionUntil(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, const sp<IBinder>& handle, uint64_t frameNumber); + status_t deferTransactionUntil(const sp<SurfaceComposerClient>& client, + const sp<IBinder>& id, const sp<Surface>& barrierSurface, + uint64_t frameNumber); + status_t reparentChildren(const sp<SurfaceComposerClient>& client, + const sp<IBinder>& id, + const sp<IBinder>& newParentHandle); + status_t detachChildren(const sp<SurfaceComposerClient>& client, + const sp<IBinder>& id); status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, int32_t overrideScalingMode); status_t setGeometryAppliesWithResize(const sp<SurfaceComposerClient>& client, @@ -190,6 +203,14 @@ public: static void closeGlobalTransaction(bool synchronous) { Composer::getInstance().closeGlobalTransactionImpl(synchronous); } + + static status_t enableVSyncInjections(bool enable) { + return Composer::getInstance().enableVSyncInjectionsImpl(enable); + } + + static status_t injectVSync(nsecs_t when) { + return Composer::getInstance().injectVSyncImpl(when); + } }; ANDROID_SINGLETON_STATIC_INSTANCE(Composer); @@ -253,6 +274,16 @@ void Composer::closeGlobalTransactionImpl(bool synchronous) { sm->setTransactionState(transaction, displayTransaction, flags); } +status_t Composer::enableVSyncInjectionsImpl(bool enable) { + sp<ISurfaceComposer> sm(ComposerService::getComposerService()); + return sm->enableVSyncInjections(enable); +} + +status_t Composer::injectVSyncImpl(nsecs_t when) { + sp<ISurfaceComposer> sm(ComposerService::getComposerService()); + return sm->injectVSync(when); +} + void Composer::setAnimationTransactionImpl() { Mutex::Autolock _l(mLock); mAnimation = true; @@ -304,7 +335,7 @@ status_t Composer::setSize(const sp<SurfaceComposerClient>& client, } status_t Composer::setLayer(const sp<SurfaceComposerClient>& client, - const sp<IBinder>& id, uint32_t z) { + const sp<IBinder>& id, int32_t z) { Mutex::Autolock _l(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) @@ -314,6 +345,20 @@ status_t Composer::setLayer(const sp<SurfaceComposerClient>& client, return NO_ERROR; } +status_t Composer::setRelativeLayer(const sp<SurfaceComposerClient>& client, + const sp<IBinder>& id, const sp<IBinder>& relativeTo, + int32_t z) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) { + return BAD_INDEX; + } + s->what |= layer_state_t::eRelativeLayerChanged; + s->relativeLayerHandle = relativeTo; + s->z = z; + return NO_ERROR; +} + status_t Composer::setFlags(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, uint32_t flags, uint32_t mask) { @@ -368,7 +413,7 @@ status_t Composer::setLayerStack(const sp<SurfaceComposerClient>& client, status_t Composer::setMatrix(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, float dsdx, float dtdx, - float dsdy, float dtdy) { + float dtdy, float dsdy) { Mutex::Autolock _l(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) @@ -415,11 +460,51 @@ status_t Composer::deferTransactionUntil( return BAD_INDEX; } s->what |= layer_state_t::eDeferTransaction; - s->handle = handle; + s->barrierHandle = handle; s->frameNumber = frameNumber; return NO_ERROR; } +status_t Composer::deferTransactionUntil( + const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, + const sp<Surface>& barrierSurface, uint64_t frameNumber) { + Mutex::Autolock lock(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) { + return BAD_INDEX; + } + s->what |= layer_state_t::eDeferTransaction; + s->barrierGbp = barrierSurface->getIGraphicBufferProducer(); + s->frameNumber = frameNumber; + return NO_ERROR; +} + +status_t Composer::reparentChildren( + const sp<SurfaceComposerClient>& client, + const sp<IBinder>& id, + const sp<IBinder>& newParentHandle) { + Mutex::Autolock lock(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) { + return BAD_INDEX; + } + s->what |= layer_state_t::eReparentChildren; + s->reparentHandle = newParentHandle; + return NO_ERROR; +} + +status_t Composer::detachChildren( + const sp<SurfaceComposerClient>& client, + const sp<IBinder>& id) { + Mutex::Autolock lock(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) { + return BAD_INDEX; + } + s->what |= layer_state_t::eDetachChildren; + return NO_ERROR; +} + status_t Composer::setOverrideScalingMode( const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, int32_t overrideScalingMode) { @@ -529,10 +614,18 @@ SurfaceComposerClient::SurfaceComposerClient() { } +SurfaceComposerClient::SurfaceComposerClient(const sp<IGraphicBufferProducer>& root) + : mStatus(NO_INIT), mComposer(Composer::getInstance()), mParent(root) +{ +} + void SurfaceComposerClient::onFirstRef() { sp<ISurfaceComposer> sm(ComposerService::getComposerService()); if (sm != 0) { - sp<ISurfaceComposerClient> conn = sm->createConnection(); + auto rootProducer = mParent.promote(); + sp<ISurfaceComposerClient> conn; + conn = (rootProducer != nullptr) ? sm->createScopedConnection(rootProducer) : + sm->createConnection(); if (conn != 0) { mClient = conn; mStatus = NO_ERROR; @@ -575,14 +668,22 @@ sp<SurfaceControl> SurfaceComposerClient::createSurface( uint32_t w, uint32_t h, PixelFormat format, - uint32_t flags) + uint32_t flags, + SurfaceControl* parent, + uint32_t windowType, + uint32_t ownerUid) { sp<SurfaceControl> sur; if (mStatus == NO_ERROR) { sp<IBinder> handle; + sp<IBinder> parentHandle; sp<IGraphicBufferProducer> gbp; - status_t err = mClient->createSurface(name, w, h, format, flags, - &handle, &gbp); + + if (parent != nullptr) { + parentHandle = parent->getHandle(); + } + status_t err = mClient->createSurface(name, w, h, format, flags, parentHandle, + windowType, ownerUid, &handle, &gbp); ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err)); if (err == NO_ERROR) { sur = new SurfaceControl(this, handle, gbp); @@ -626,14 +727,6 @@ status_t SurfaceComposerClient::getLayerFrameStats(const sp<IBinder>& token, return mClient->getLayerFrameStats(token, outStats); } -status_t SurfaceComposerClient::getTransformToDisplayInverse(const sp<IBinder>& token, - bool* outTransformToDisplayInverse) const { - if (mStatus != NO_ERROR) { - return mStatus; - } - return mClient->getTransformToDisplayInverse(token, outTransformToDisplayInverse); -} - inline Composer& SurfaceComposerClient::getComposer() { return mComposer; } @@ -652,6 +745,14 @@ void SurfaceComposerClient::setAnimationTransaction() { Composer::setAnimationTransaction(); } +status_t SurfaceComposerClient::enableVSyncInjections(bool enable) { + return Composer::enableVSyncInjections(enable); +} + +status_t SurfaceComposerClient::injectVSync(nsecs_t when) { + return Composer::injectVSync(when); +} + // ---------------------------------------------------------------------------- status_t SurfaceComposerClient::setCrop(const sp<IBinder>& id, const Rect& crop) { @@ -671,10 +772,15 @@ status_t SurfaceComposerClient::setSize(const sp<IBinder>& id, uint32_t w, uint3 return getComposer().setSize(this, id, w, h); } -status_t SurfaceComposerClient::setLayer(const sp<IBinder>& id, uint32_t z) { +status_t SurfaceComposerClient::setLayer(const sp<IBinder>& id, int32_t z) { return getComposer().setLayer(this, id, z); } +status_t SurfaceComposerClient::setRelativeLayer(const sp<IBinder>& id, + const sp<IBinder>& relativeTo, int32_t z) { + return getComposer().setRelativeLayer(this, id, relativeTo, z); +} + status_t SurfaceComposerClient::hide(const sp<IBinder>& id) { return getComposer().setFlags(this, id, layer_state_t::eLayerHidden, @@ -706,8 +812,8 @@ status_t SurfaceComposerClient::setLayerStack(const sp<IBinder>& id, uint32_t la } status_t SurfaceComposerClient::setMatrix(const sp<IBinder>& id, float dsdx, float dtdx, - float dsdy, float dtdy) { - return getComposer().setMatrix(this, id, dsdx, dtdx, dsdy, dtdy); + float dtdy, float dsdy) { + return getComposer().setMatrix(this, id, dsdx, dtdx, dtdy, dsdy); } status_t SurfaceComposerClient::deferTransactionUntil(const sp<IBinder>& id, @@ -715,6 +821,20 @@ status_t SurfaceComposerClient::deferTransactionUntil(const sp<IBinder>& id, return getComposer().deferTransactionUntil(this, id, handle, frameNumber); } +status_t SurfaceComposerClient::deferTransactionUntil(const sp<IBinder>& id, + const sp<Surface>& barrierSurface, uint64_t frameNumber) { + return getComposer().deferTransactionUntil(this, id, barrierSurface, frameNumber); +} + +status_t SurfaceComposerClient::reparentChildren(const sp<IBinder>& id, + const sp<IBinder>& newParentHandle) { + return getComposer().reparentChildren(this, id, newParentHandle); +} + +status_t SurfaceComposerClient::detachChildren(const sp<IBinder>& id) { + return getComposer().detachChildren(this, id); +} + status_t SurfaceComposerClient::setOverrideScalingMode( const sp<IBinder>& id, int32_t overrideScalingMode) { return getComposer().setOverrideScalingMode( @@ -824,13 +944,40 @@ status_t ScreenshotClient::capture( const sp<IBinder>& display, const sp<IGraphicBufferProducer>& producer, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - uint32_t minLayerZ, uint32_t maxLayerZ, bool useIdentityTransform) { + int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (s == NULL) return NO_INIT; return s->captureScreen(display, producer, sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform); } +status_t ScreenshotClient::captureToBuffer(const sp<IBinder>& display, + Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, + int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, + uint32_t rotation, + sp<GraphicBuffer>* outBuffer) { + sp<ISurfaceComposer> s(ComposerService::getComposerService()); + if (s == NULL) return NO_INIT; + + sp<IGraphicBufferConsumer> gbpConsumer; + sp<IGraphicBufferProducer> producer; + BufferQueue::createBufferQueue(&producer, &gbpConsumer); + sp<BufferItemConsumer> consumer(new BufferItemConsumer(gbpConsumer, + GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_NEVER, + 1, true)); + + status_t ret = s->captureScreen(display, producer, sourceCrop, reqWidth, reqHeight, + minLayerZ, maxLayerZ, useIdentityTransform, + static_cast<ISurfaceComposer::Rotation>(rotation)); + if (ret != NO_ERROR) { + return ret; + } + BufferItem b; + consumer->acquireBuffer(&b, 0, true); + *outBuffer = b.mGraphicBuffer; + return ret; +} + ScreenshotClient::ScreenshotClient() : mHaveBuffer(false) { memset(&mBuffer, 0, sizeof(mBuffer)); @@ -852,7 +999,7 @@ sp<CpuConsumer> ScreenshotClient::getCpuConsumer() const { status_t ScreenshotClient::update(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - uint32_t minLayerZ, uint32_t maxLayerZ, + int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, uint32_t rotation) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (s == NULL) return NO_INIT; @@ -879,7 +1026,7 @@ status_t ScreenshotClient::update(const sp<IBinder>& display, status_t ScreenshotClient::update(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - uint32_t minLayerZ, uint32_t maxLayerZ, + int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform) { return ScreenshotClient::update(display, sourceCrop, reqWidth, reqHeight, @@ -888,14 +1035,16 @@ status_t ScreenshotClient::update(const sp<IBinder>& display, status_t ScreenshotClient::update(const sp<IBinder>& display, Rect sourceCrop, bool useIdentityTransform) { - return ScreenshotClient::update(display, sourceCrop, 0, 0, 0, -1U, + return ScreenshotClient::update(display, sourceCrop, 0, 0, + INT32_MIN, INT32_MAX, useIdentityTransform, ISurfaceComposer::eRotateNone); } status_t ScreenshotClient::update(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform) { return ScreenshotClient::update(display, sourceCrop, reqWidth, reqHeight, - 0, -1U, useIdentityTransform, ISurfaceComposer::eRotateNone); + INT32_MIN, INT32_MAX, + useIdentityTransform, ISurfaceComposer::eRotateNone); } void ScreenshotClient::release() { diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index 33c1d906e6..58bd273de6 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -102,11 +102,19 @@ status_t SurfaceControl::setLayerStack(uint32_t layerStack) { if (err < 0) return err; return mClient->setLayerStack(mHandle, layerStack); } -status_t SurfaceControl::setLayer(uint32_t layer) { + +status_t SurfaceControl::setLayer(int32_t layer) { status_t err = validate(); if (err < 0) return err; return mClient->setLayer(mHandle, layer); } + +status_t SurfaceControl::setRelativeLayer(const sp<IBinder>& relativeTo, int32_t layer) { + status_t err = validate(); + if (err < 0) return err; + return mClient->setRelativeLayer(mHandle, relativeTo, layer); +} + status_t SurfaceControl::setPosition(float x, float y) { status_t err = validate(); if (err < 0) return err; @@ -147,10 +155,10 @@ status_t SurfaceControl::setAlpha(float alpha) { if (err < 0) return err; return mClient->setAlpha(mHandle, alpha); } -status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { +status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) { status_t err = validate(); if (err < 0) return err; - return mClient->setMatrix(mHandle, dsdx, dtdx, dsdy, dtdy); + return mClient->setMatrix(mHandle, dsdx, dtdx, dtdy, dsdy); } status_t SurfaceControl::setCrop(const Rect& crop) { status_t err = validate(); @@ -163,13 +171,32 @@ status_t SurfaceControl::setFinalCrop(const Rect& crop) { return mClient->setFinalCrop(mHandle, crop); } -status_t SurfaceControl::deferTransactionUntil(sp<IBinder> handle, +status_t SurfaceControl::deferTransactionUntil(const sp<IBinder>& handle, uint64_t frameNumber) { status_t err = validate(); if (err < 0) return err; return mClient->deferTransactionUntil(mHandle, handle, frameNumber); } +status_t SurfaceControl::deferTransactionUntil(const sp<Surface>& handle, + uint64_t frameNumber) { + status_t err = validate(); + if (err < 0) return err; + return mClient->deferTransactionUntil(mHandle, handle, frameNumber); +} + +status_t SurfaceControl::reparentChildren(const sp<IBinder>& newParentHandle) { + status_t err = validate(); + if (err < 0) return err; + return mClient->reparentChildren(mHandle, newParentHandle); +} + +status_t SurfaceControl::detachChildren() { + status_t err = validate(); + if (err < 0) return err; + return mClient->detachChildren(mHandle); +} + status_t SurfaceControl::setOverrideScalingMode(int32_t overrideScalingMode) { status_t err = validate(); if (err < 0) return err; @@ -190,13 +217,6 @@ status_t SurfaceControl::getLayerFrameStats(FrameStats* outStats) const { return client->getLayerFrameStats(mHandle, outStats); } -status_t SurfaceControl::getTransformToDisplayInverse(bool* outTransformToDisplayInverse) const { - status_t err = validate(); - if (err < 0) return err; - const sp<SurfaceComposerClient>& client(mClient); - return client->getTransformToDisplayInverse(mHandle, outTransformToDisplayInverse); -} - status_t SurfaceControl::validate() const { if (mHandle==0 || mClient==0) { @@ -217,17 +237,30 @@ status_t SurfaceControl::writeSurfaceToParcel( return parcel->writeStrongBinder(IInterface::asBinder(bp)); } +sp<Surface> SurfaceControl::generateSurfaceLocked() const +{ + // This surface is always consumed by SurfaceFlinger, so the + // producerControlledByApp value doesn't matter; using false. + mSurfaceData = new Surface(mGraphicBufferProducer, false); + + return mSurfaceData; +} + sp<Surface> SurfaceControl::getSurface() const { Mutex::Autolock _l(mLock); if (mSurfaceData == 0) { - // This surface is always consumed by SurfaceFlinger, so the - // producerControlledByApp value doesn't matter; using false. - mSurfaceData = new Surface(mGraphicBufferProducer, false); + return generateSurfaceLocked(); } return mSurfaceData; } +sp<Surface> SurfaceControl::createSurface() const +{ + Mutex::Autolock _l(mLock); + return generateSurfaceLocked(); +} + sp<IBinder> SurfaceControl::getHandle() const { Mutex::Autolock lock(mLock); diff --git a/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp b/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp new file mode 100644 index 0000000000..a5f28cdbd7 --- /dev/null +++ b/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gui/bufferqueue/1.0/B2HProducerListener.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V1_0 { +namespace utils { + +// B2HProducerListener +B2HProducerListener::B2HProducerListener( + sp<BProducerListener> const& base): + mBase(base) { +} + +Return<void> B2HProducerListener::onBufferReleased() { + mBase->onBufferReleased(); + return Void(); +} + +Return<bool> B2HProducerListener::needsReleaseNotify() { + return mBase->needsReleaseNotify(); +} + +} // namespace utils +} // namespace V1_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp new file mode 100644 index 0000000000..fda5b945da --- /dev/null +++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp @@ -0,0 +1,1234 @@ +/* + * Copyright 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "H2BGraphicBufferProducer" + +#include <android-base/logging.h> + +#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> +#include <gui/bufferqueue/1.0/B2HProducerListener.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V1_0 { +namespace utils { + +using Status = HGraphicBufferProducer::Status; +using ::android::hardware::graphics::common::V1_0::Dataspace; +typedef ::android::hardware::media::V1_0::Rect HRect; +typedef ::android::hardware::media::V1_0::Region HRegion; + +// Conversion functions + +// native_handle_t helper functions. + +/** + * \brief Take an fd and create a native handle containing only the given fd. + * The created handle will need to be deleted manually with + * `native_handle_delete()`. + * + * \param[in] fd The source file descriptor (of type `int`). + * \return The create `native_handle_t*` that contains the given \p fd. If the + * supplied \p fd is negative, the created native handle will contain no file + * descriptors. + * + * If the native handle cannot be created, the return value will be + * `nullptr`. + * + * This function does not duplicate the file descriptor. + */ +inline native_handle_t* native_handle_create_from_fd(int fd) { + if (fd < 0) { + return native_handle_create(0, 0); + } + native_handle_t* nh = native_handle_create(1, 0); + if (nh == nullptr) { + return nullptr; + } + nh->data[0] = fd; + return nh; +} + +/** + * \brief Extract a file descriptor from a native handle. + * + * \param[in] nh The source `native_handle_t*`. + * \param[in] index The index of the file descriptor in \p nh to read from. This + * input has the default value of `0`. + * \return The `index`-th file descriptor in \p nh. If \p nh does not have + * enough file descriptors, the returned value will be `-1`. + * + * This function does not duplicate the file descriptor. + */ +inline int native_handle_read_fd(native_handle_t const* nh, int index = 0) { + return ((nh == nullptr) || (nh->numFds == 0) || + (nh->numFds <= index) || (index < 0)) ? + -1 : nh->data[index]; +} + +/** + * \brief Convert `Return<Status>` to `status_t`. This is for legacy binder + * calls. + * + * \param[in] t The source `Return<Status>`. + * \return The corresponding `status_t`. + * + * This function first check if \p t has a transport error. If it does, then the + * return value is the transport error code. Otherwise, the return value is + * converted from `Status` contained inside \p t. + * + * Note: + * - This `Status` is omx-specific. It is defined in `types.hal`. + * - The name of this function is not `convert`. + */ +// convert: Return<Status> -> status_t +inline status_t toStatusT(Return<Status> const& t) { + return t.isOk() ? static_cast<status_t>(static_cast<Status>(t)) : UNKNOWN_ERROR; +} + +/** + * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls. + * + * \param[in] t The source `Return<void>`. + * \return The corresponding `status_t`. + */ +// convert: Return<void> -> status_t +inline status_t toStatusT(Return<void> const& t) { + return t.isOk() ? OK : UNKNOWN_ERROR; +} + +/** + * \brief Wrap `GraphicBuffer` in `AnwBuffer`. + * + * \param[out] t The wrapper of type `AnwBuffer`. + * \param[in] l The source `GraphicBuffer`. + */ +// wrap: GraphicBuffer -> AnwBuffer +inline void wrapAs(AnwBuffer* t, GraphicBuffer const& l) { + t->attr.width = l.getWidth(); + t->attr.height = l.getHeight(); + t->attr.stride = l.getStride(); + t->attr.format = static_cast<PixelFormat>(l.getPixelFormat()); + t->attr.layerCount = l.getLayerCount(); + t->attr.usage = l.getUsage(); + t->attr.id = l.getId(); + t->attr.generationNumber = l.getGenerationNumber(); + t->nativeHandle = hidl_handle(l.handle); +} + +/** + * \brief Convert `AnwBuffer` to `GraphicBuffer`. + * + * \param[out] l The destination `GraphicBuffer`. + * \param[in] t The source `AnwBuffer`. + * + * This function will duplicate all file descriptors in \p t. + */ +// convert: AnwBuffer -> GraphicBuffer +// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten +inline bool convertTo(GraphicBuffer* l, AnwBuffer const& t) { + native_handle_t* handle = t.nativeHandle == nullptr ? + nullptr : native_handle_clone(t.nativeHandle); + + size_t const numInts = 12 + + static_cast<size_t>(handle ? handle->numInts : 0); + int32_t* ints = new int32_t[numInts]; + + size_t numFds = static_cast<size_t>(handle ? handle->numFds : 0); + int* fds = new int[numFds]; + + ints[0] = 'GBFR'; + ints[1] = static_cast<int32_t>(t.attr.width); + ints[2] = static_cast<int32_t>(t.attr.height); + ints[3] = static_cast<int32_t>(t.attr.stride); + ints[4] = static_cast<int32_t>(t.attr.format); + ints[5] = static_cast<int32_t>(t.attr.layerCount); + ints[6] = static_cast<int32_t>(t.attr.usage); + ints[7] = static_cast<int32_t>(t.attr.id >> 32); + ints[8] = static_cast<int32_t>(t.attr.id & 0xFFFFFFFF); + ints[9] = static_cast<int32_t>(t.attr.generationNumber); + ints[10] = 0; + ints[11] = 0; + if (handle) { + ints[10] = static_cast<int32_t>(handle->numFds); + ints[11] = static_cast<int32_t>(handle->numInts); + int* intsStart = handle->data + handle->numFds; + std::copy(handle->data, intsStart, fds); + std::copy(intsStart, intsStart + handle->numInts, &ints[12]); + } + + void const* constBuffer = static_cast<void const*>(ints); + size_t size = numInts * sizeof(int32_t); + int const* constFds = static_cast<int const*>(fds); + status_t status = l->unflatten(constBuffer, size, constFds, numFds); + + delete [] fds; + delete [] ints; + native_handle_delete(handle); + return status == NO_ERROR; +} + +// Ref: frameworks/native/libs/ui/Fence.cpp + +/** + * \brief Return the size of the non-fd buffer required to flatten a fence. + * + * \param[in] fence The input fence of type `hidl_handle`. + * \return The required size of the flat buffer. + * + * The current version of this function always returns 4, which is the number of + * bytes required to store the number of file descriptors contained in the fd + * part of the flat buffer. + */ +inline size_t getFenceFlattenedSize(hidl_handle const& /* fence */) { + return 4; +}; + +/** + * \brief Return the number of file descriptors contained in a fence. + * + * \param[in] fence The input fence of type `hidl_handle`. + * \return `0` if \p fence does not contain a valid file descriptor, or `1` + * otherwise. + */ +inline size_t getFenceFdCount(hidl_handle const& fence) { + return native_handle_read_fd(fence) == -1 ? 0 : 1; +} + +/** + * \brief Unflatten `Fence` to `hidl_handle`. + * + * \param[out] fence The destination `hidl_handle`. + * \param[out] nh The underlying native handle. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * If the return value is `NO_ERROR`, \p nh will point to a newly created + * native handle, which needs to be deleted with `native_handle_delete()` + * afterwards. + */ +inline status_t unflattenFence(hidl_handle* fence, native_handle_t** nh, + void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { + if (size < 4) { + return NO_MEMORY; + } + + uint32_t numFdsInHandle; + FlattenableUtils::read(buffer, size, numFdsInHandle); + + if (numFdsInHandle > 1) { + return BAD_VALUE; + } + + if (numFds < numFdsInHandle) { + return NO_MEMORY; + } + + if (numFdsInHandle) { + *nh = native_handle_create_from_fd(*fds); + if (*nh == nullptr) { + return NO_MEMORY; + } + *fence = *nh; + ++fds; + --numFds; + } else { + *nh = nullptr; + *fence = hidl_handle(); + } + + return NO_ERROR; +} + +/** + * \brief Flatten `hidl_handle` as `Fence`. + * + * \param[in] fence The source `hidl_handle`. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + */ +inline status_t flattenFence(hidl_handle const& fence, + void*& buffer, size_t& size, int*& fds, size_t& numFds) { + if (size < getFenceFlattenedSize(fence) || + numFds < getFenceFdCount(fence)) { + return NO_MEMORY; + } + // Cast to uint32_t since the size of a size_t can vary between 32- and + // 64-bit processes + FlattenableUtils::write(buffer, size, + static_cast<uint32_t>(getFenceFdCount(fence))); + int fd = native_handle_read_fd(fence); + if (fd != -1) { + *fds = fd; + ++fds; + --numFds; + } + return NO_ERROR; +} + +/** + * \brief Wrap `Fence` in `hidl_handle`. + * + * \param[out] t The wrapper of type `hidl_handle`. + * \param[out] nh The native handle pointed to by \p t. + * \param[in] l The source `Fence`. + * + * On success, \p nh will hold a newly created native handle, which must be + * deleted manually with `native_handle_delete()` afterwards. + */ +// wrap: Fence -> hidl_handle +inline bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l) { + size_t const baseSize = l.getFlattenedSize(); + std::unique_ptr<uint8_t[]> baseBuffer( + new (std::nothrow) uint8_t[baseSize]); + if (!baseBuffer) { + return false; + } + + size_t const baseNumFds = l.getFdCount(); + std::unique_ptr<int[]> baseFds( + new (std::nothrow) int[baseNumFds]); + if (!baseFds) { + return false; + } + + void* buffer = static_cast<void*>(baseBuffer.get()); + size_t size = baseSize; + int* fds = static_cast<int*>(baseFds.get()); + size_t numFds = baseNumFds; + if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) { + return false; + } + + void const* constBuffer = static_cast<void const*>(baseBuffer.get()); + size = baseSize; + int const* constFds = static_cast<int const*>(baseFds.get()); + numFds = baseNumFds; + if (unflattenFence(t, nh, constBuffer, size, constFds, numFds) + != NO_ERROR) { + return false; + } + + return true; +} + +/** + * \brief Convert `hidl_handle` to `Fence`. + * + * \param[out] l The destination `Fence`. `l` must not have been used + * (`l->isValid()` must return `false`) before this function is called. + * \param[in] t The source `hidl_handle`. + * + * If \p t contains a valid file descriptor, it will be duplicated. + */ +// convert: hidl_handle -> Fence +inline bool convertTo(Fence* l, hidl_handle const& t) { + int fd = native_handle_read_fd(t); + if (fd != -1) { + fd = dup(fd); + if (fd == -1) { + return false; + } + } + native_handle_t* nh = native_handle_create_from_fd(fd); + if (nh == nullptr) { + if (fd != -1) { + close(fd); + } + return false; + } + + size_t const baseSize = getFenceFlattenedSize(t); + std::unique_ptr<uint8_t[]> baseBuffer( + new (std::nothrow) uint8_t[baseSize]); + if (!baseBuffer) { + native_handle_delete(nh); + return false; + } + + size_t const baseNumFds = getFenceFdCount(t); + std::unique_ptr<int[]> baseFds( + new (std::nothrow) int[baseNumFds]); + if (!baseFds) { + native_handle_delete(nh); + return false; + } + + void* buffer = static_cast<void*>(baseBuffer.get()); + size_t size = baseSize; + int* fds = static_cast<int*>(baseFds.get()); + size_t numFds = baseNumFds; + if (flattenFence(hidl_handle(nh), buffer, size, fds, numFds) != NO_ERROR) { + native_handle_delete(nh); + return false; + } + native_handle_delete(nh); + + void const* constBuffer = static_cast<void const*>(baseBuffer.get()); + size = baseSize; + int const* constFds = static_cast<int const*>(baseFds.get()); + numFds = baseNumFds; + if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) { + return false; + } + + return true; +} + +// Ref: frameworks/native/libs/ui/Region.cpp + +/** + * \brief Unflatten `HRegion`. + * + * \param[out] t The destination `HRegion`. + * \param[in,out] buffer The pointer to the flat buffer. + * \param[in,out] size The size of the flat buffer. + * \return `NO_ERROR` on success; other value on failure. + */ +inline status_t unflatten(HRegion* t, void const*& buffer, size_t& size) { + if (size < sizeof(uint32_t)) { + return NO_MEMORY; + } + + uint32_t numRects = 0; + FlattenableUtils::read(buffer, size, numRects); + if (size < numRects * sizeof(HRect)) { + return NO_MEMORY; + } + if (numRects > (UINT32_MAX / sizeof(HRect))) { + return NO_MEMORY; + } + + t->resize(numRects); + for (size_t r = 0; r < numRects; ++r) { + ::android::Rect rect(::android::Rect::EMPTY_RECT); + status_t status = rect.unflatten(buffer, size); + if (status != NO_ERROR) { + return status; + } + FlattenableUtils::advance(buffer, size, sizeof(rect)); + (*t)[r] = HRect{ + static_cast<int32_t>(rect.left), + static_cast<int32_t>(rect.top), + static_cast<int32_t>(rect.right), + static_cast<int32_t>(rect.bottom)}; + } + return NO_ERROR; +} + +// Ref: frameworks/native/libs/gui/IGraphicBufferProducer.cpp: +// IGraphicBufferProducer::QueueBufferInput + +/** + * \brief Return a lower bound on the size of the buffer required to flatten + * `HGraphicBufferProducer::QueueBufferInput`. + * + * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`. + * \return A lower bound on the size of the flat buffer. + */ +constexpr size_t minFlattenedSize( + HGraphicBufferProducer::QueueBufferInput const& /* t */) { + return sizeof(int64_t) + // timestamp + sizeof(int) + // isAutoTimestamp + sizeof(android_dataspace) + // dataSpace + sizeof(::android::Rect) + // crop + sizeof(int) + // scalingMode + sizeof(uint32_t) + // transform + sizeof(uint32_t) + // stickyTransform + sizeof(bool); // getFrameTimestamps +} + +/** + * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`. + * + * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`. + * \param[out] nh The underlying native handle for `t->fence`. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * If the return value is `NO_ERROR` and `t->fence` contains a valid file + * descriptor, \p nh will be a newly created native handle holding that file + * descriptor. \p nh needs to be deleted with `native_handle_delete()` + * afterwards. + */ +inline status_t unflatten( + HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh, + void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { + if (size < minFlattenedSize(*t)) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, t->timestamp); + int lIsAutoTimestamp; + FlattenableUtils::read(buffer, size, lIsAutoTimestamp); + t->isAutoTimestamp = static_cast<int32_t>(lIsAutoTimestamp); + android_dataspace_t lDataSpace; + FlattenableUtils::read(buffer, size, lDataSpace); + t->dataSpace = static_cast<Dataspace>(lDataSpace); + ::android::Rect lCrop; + FlattenableUtils::read(buffer, size, lCrop); + t->crop = HRect{ + static_cast<int32_t>(lCrop.left), + static_cast<int32_t>(lCrop.top), + static_cast<int32_t>(lCrop.right), + static_cast<int32_t>(lCrop.bottom)}; + int lScalingMode; + FlattenableUtils::read(buffer, size, lScalingMode); + t->scalingMode = static_cast<int32_t>(lScalingMode); + FlattenableUtils::read(buffer, size, t->transform); + FlattenableUtils::read(buffer, size, t->stickyTransform); + FlattenableUtils::read(buffer, size, t->getFrameTimestamps); + + status_t status = unflattenFence(&(t->fence), nh, + buffer, size, fds, numFds); + if (status != NO_ERROR) { + return status; + } + return unflatten(&(t->surfaceDamage), buffer, size); +} + +/** + * \brief Wrap `IGraphicBufferProducer::QueueBufferInput` in + * `HGraphicBufferProducer::QueueBufferInput`. + * + * \param[out] t The wrapper of type + * `HGraphicBufferProducer::QueueBufferInput`. + * \param[out] nh The underlying native handle for `t->fence`. + * \param[in] l The source `IGraphicBufferProducer::QueueBufferInput`. + * + * If the return value is `true` and `t->fence` contains a valid file + * descriptor, \p nh will be a newly created native handle holding that file + * descriptor. \p nh needs to be deleted with `native_handle_delete()` + * afterwards. + */ +inline bool wrapAs( + HGraphicBufferProducer::QueueBufferInput* t, + native_handle_t** nh, + BGraphicBufferProducer::QueueBufferInput const& l) { + + size_t const baseSize = l.getFlattenedSize(); + std::unique_ptr<uint8_t[]> baseBuffer( + new (std::nothrow) uint8_t[baseSize]); + if (!baseBuffer) { + return false; + } + + size_t const baseNumFds = l.getFdCount(); + std::unique_ptr<int[]> baseFds( + new (std::nothrow) int[baseNumFds]); + if (!baseFds) { + return false; + } + + void* buffer = static_cast<void*>(baseBuffer.get()); + size_t size = baseSize; + int* fds = baseFds.get(); + size_t numFds = baseNumFds; + if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) { + return false; + } + + void const* constBuffer = static_cast<void const*>(baseBuffer.get()); + size = baseSize; + int const* constFds = static_cast<int const*>(baseFds.get()); + numFds = baseNumFds; + if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) { + return false; + } + + return true; +} + +// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot + +/** + * \brief Return the size of the non-fd buffer required to flatten + * `FenceTimeSnapshot`. + * + * \param[in] t The input `FenceTimeSnapshot`. + * \return The required size of the flat buffer. + */ +inline size_t getFlattenedSize( + HGraphicBufferProducer::FenceTimeSnapshot const& t) { + constexpr size_t min = sizeof(t.state); + switch (t.state) { + case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY: + return min; + case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE: + return min + getFenceFlattenedSize(t.fence); + case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME: + return min + sizeof( + ::android::FenceTime::Snapshot::signalTime); + } + return 0; +} + +/** + * \brief Return the number of file descriptors contained in + * `FenceTimeSnapshot`. + * + * \param[in] t The input `FenceTimeSnapshot`. + * \return The number of file descriptors contained in \p snapshot. + */ +inline size_t getFdCount( + HGraphicBufferProducer::FenceTimeSnapshot const& t) { + return t.state == + HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE ? + getFenceFdCount(t.fence) : 0; +} + +/** + * \brief Flatten `FenceTimeSnapshot`. + * + * \param[in] t The source `FenceTimeSnapshot`. + * \param[out] nh The cloned native handle, if necessary. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * This function will duplicate the file descriptor in `t.fence` if `t.state == + * FENCE`, in which case \p nh will be returned. + */ +inline status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t, + native_handle_t** nh, + void*& buffer, size_t& size, int*& fds, size_t& numFds) { + if (size < getFlattenedSize(t)) { + return NO_MEMORY; + } + + *nh = nullptr; + switch (t.state) { + case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY: + FlattenableUtils::write(buffer, size, + ::android::FenceTime::Snapshot::State::EMPTY); + return NO_ERROR; + case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE: + FlattenableUtils::write(buffer, size, + ::android::FenceTime::Snapshot::State::FENCE); + *nh = t.fence.getNativeHandle() == nullptr ? + nullptr : native_handle_clone(t.fence); + return flattenFence(hidl_handle(*nh), buffer, size, fds, numFds); + case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME: + FlattenableUtils::write(buffer, size, + ::android::FenceTime::Snapshot::State::SIGNAL_TIME); + FlattenableUtils::write(buffer, size, t.signalTimeNs); + return NO_ERROR; + } + return NO_ERROR; +} + +// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta + +/** + * \brief Return a lower bound on the size of the non-fd buffer required to + * flatten `FrameEventsDelta`. + * + * \param[in] t The input `FrameEventsDelta`. + * \return A lower bound on the size of the flat buffer. + */ +constexpr size_t minFlattenedSize( + HGraphicBufferProducer::FrameEventsDelta const& /* t */) { + return sizeof(uint64_t) + // mFrameNumber + sizeof(uint8_t) + // mIndex + sizeof(uint8_t) + // mAddPostCompositeCalled + sizeof(uint8_t) + // mAddRetireCalled + sizeof(uint8_t) + // mAddReleaseCalled + sizeof(nsecs_t) + // mPostedTime + sizeof(nsecs_t) + // mRequestedPresentTime + sizeof(nsecs_t) + // mLatchTime + sizeof(nsecs_t) + // mFirstRefreshStartTime + sizeof(nsecs_t); // mLastRefreshStartTime +} + +/** + * \brief Return the size of the non-fd buffer required to flatten + * `FrameEventsDelta`. + * + * \param[in] t The input `FrameEventsDelta`. + * \return The required size of the flat buffer. + */ +inline size_t getFlattenedSize( + HGraphicBufferProducer::FrameEventsDelta const& t) { + return minFlattenedSize(t) + + getFlattenedSize(t.gpuCompositionDoneFence) + + getFlattenedSize(t.displayPresentFence) + + getFlattenedSize(t.displayRetireFence) + + getFlattenedSize(t.releaseFence); +}; + +/** + * \brief Return the number of file descriptors contained in + * `FrameEventsDelta`. + * + * \param[in] t The input `FrameEventsDelta`. + * \return The number of file descriptors contained in \p t. + */ +inline size_t getFdCount( + HGraphicBufferProducer::FrameEventsDelta const& t) { + return getFdCount(t.gpuCompositionDoneFence) + + getFdCount(t.displayPresentFence) + + getFdCount(t.displayRetireFence) + + getFdCount(t.releaseFence); +}; + +/** + * \brief Flatten `FrameEventsDelta`. + * + * \param[in] t The source `FrameEventsDelta`. + * \param[out] nh The array of native handles that are cloned. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * On success, this function will duplicate file descriptors contained in \p t. + * The cloned native handles will be stored in \p nh. These native handles will + * need to be closed by the caller. + */ +// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp: +// FrameEventsDelta::flatten +inline status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t, + std::vector<native_handle_t*>* nh, + void*& buffer, size_t& size, int*& fds, size_t numFds) { + // Check that t.index is within a valid range. + if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY) + || t.index > std::numeric_limits<uint8_t>::max()) { + return BAD_VALUE; + } + + FlattenableUtils::write(buffer, size, t.frameNumber); + + // These are static_cast to uint8_t for alignment. + FlattenableUtils::write(buffer, size, static_cast<uint8_t>(t.index)); + FlattenableUtils::write( + buffer, size, static_cast<uint8_t>(t.addPostCompositeCalled)); + FlattenableUtils::write( + buffer, size, static_cast<uint8_t>(t.addRetireCalled)); + FlattenableUtils::write( + buffer, size, static_cast<uint8_t>(t.addReleaseCalled)); + + FlattenableUtils::write(buffer, size, t.postedTimeNs); + FlattenableUtils::write(buffer, size, t.requestedPresentTimeNs); + FlattenableUtils::write(buffer, size, t.latchTimeNs); + FlattenableUtils::write(buffer, size, t.firstRefreshStartTimeNs); + FlattenableUtils::write(buffer, size, t.lastRefreshStartTimeNs); + FlattenableUtils::write(buffer, size, t.dequeueReadyTime); + + // Fences + HGraphicBufferProducer::FenceTimeSnapshot const* tSnapshot[4]; + tSnapshot[0] = &t.gpuCompositionDoneFence; + tSnapshot[1] = &t.displayPresentFence; + tSnapshot[2] = &t.displayRetireFence; + tSnapshot[3] = &t.releaseFence; + nh->resize(4); + for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) { + status_t status = flatten( + *(tSnapshot[snapshotIndex]), + &((*nh)[snapshotIndex]), + buffer, size, fds, numFds); + if (status != NO_ERROR) { + while (snapshotIndex > 0) { + --snapshotIndex; + native_handle_close((*nh)[snapshotIndex]); + native_handle_delete((*nh)[snapshotIndex]); + (*nh)[snapshotIndex] = nullptr; + } + return status; + } + } + return NO_ERROR; +} + +// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta + +/** + * \brief Return the size of the non-fd buffer required to flatten + * `HGraphicBufferProducer::FrameEventHistoryDelta`. + * + * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`. + * \return The required size of the flat buffer. + */ +inline size_t getFlattenedSize( + HGraphicBufferProducer::FrameEventHistoryDelta const& t) { + size_t size = 4 + // mDeltas.size() + sizeof(t.compositorTiming); + for (size_t i = 0; i < t.deltas.size(); ++i) { + size += getFlattenedSize(t.deltas[i]); + } + return size; +} + +/** + * \brief Return the number of file descriptors contained in + * `HGraphicBufferProducer::FrameEventHistoryDelta`. + * + * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`. + * \return The number of file descriptors contained in \p t. + */ +inline size_t getFdCount( + HGraphicBufferProducer::FrameEventHistoryDelta const& t) { + size_t numFds = 0; + for (size_t i = 0; i < t.deltas.size(); ++i) { + numFds += getFdCount(t.deltas[i]); + } + return numFds; +} + +/** + * \brief Flatten `FrameEventHistoryDelta`. + * + * \param[in] t The source `FrameEventHistoryDelta`. + * \param[out] nh The array of arrays of cloned native handles. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * On success, this function will duplicate file descriptors contained in \p t. + * The cloned native handles will be stored in \p nh. Before making the call, \p + * nh should have enough space to store `n` pointers to arrays of native + * handles, where `n` is the length of `t.deltas`, and each `nh[i]` should have + * enough space to store `4` native handles. + */ +inline status_t flatten( + HGraphicBufferProducer::FrameEventHistoryDelta const& t, + std::vector<std::vector<native_handle_t*> >* nh, + void*& buffer, size_t& size, int*& fds, size_t& numFds) { + if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) { + return BAD_VALUE; + } + if (size < getFlattenedSize(t)) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, t.compositorTiming); + + FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.deltas.size())); + nh->resize(t.deltas.size()); + for (size_t deltaIndex = 0; deltaIndex < t.deltas.size(); ++deltaIndex) { + status_t status = flatten( + t.deltas[deltaIndex], &((*nh)[deltaIndex]), + buffer, size, fds, numFds); + if (status != NO_ERROR) { + while (deltaIndex > 0) { + --deltaIndex; + for (size_t snapshotIndex = 0; + snapshotIndex < 4; ++snapshotIndex) { + native_handle_close((*nh)[deltaIndex][snapshotIndex]); + native_handle_delete((*nh)[deltaIndex][snapshotIndex]); + (*nh)[deltaIndex][snapshotIndex] = nullptr; + } + } + return status; + } + } + return NO_ERROR; +} + +/** + * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to + * `::android::FrameEventHistoryDelta`. + * + * \param[out] l The destination `::android::FrameEventHistoryDelta`. + * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`. + * + * This function will duplicate all file descriptors contained in \p t. + */ +inline bool convertTo( + ::android::FrameEventHistoryDelta* l, + HGraphicBufferProducer::FrameEventHistoryDelta const& t) { + + size_t const baseSize = getFlattenedSize(t); + std::unique_ptr<uint8_t[]> baseBuffer( + new (std::nothrow) uint8_t[baseSize]); + if (!baseBuffer) { + return false; + } + + size_t const baseNumFds = getFdCount(t); + std::unique_ptr<int[]> baseFds( + new (std::nothrow) int[baseNumFds]); + if (!baseFds) { + return false; + } + + void* buffer = static_cast<void*>(baseBuffer.get()); + size_t size = baseSize; + int* fds = static_cast<int*>(baseFds.get()); + size_t numFds = baseNumFds; + std::vector<std::vector<native_handle_t*> > nhAA; + if (flatten(t, &nhAA, buffer, size, fds, numFds) != NO_ERROR) { + return false; + } + + void const* constBuffer = static_cast<void const*>(baseBuffer.get()); + size = baseSize; + int const* constFds = static_cast<int const*>(baseFds.get()); + numFds = baseNumFds; + if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) { + for (auto nhA : nhAA) { + for (auto nh : nhA) { + if (nh != nullptr) { + native_handle_close(nh); + native_handle_delete(nh); + } + } + } + return false; + } + + for (auto nhA : nhAA) { + for (auto nh : nhA) { + if (nh != nullptr) { + native_handle_delete(nh); + } + } + } + return true; +} + +// Ref: frameworks/native/libs/gui/IGraphicBufferProducer.cpp: +// IGraphicBufferProducer::QueueBufferOutput + +/** + * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to + * `IGraphicBufferProducer::QueueBufferOutput`. + * + * \param[out] l The destination `IGraphicBufferProducer::QueueBufferOutput`. + * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`. + * + * This function will duplicate all file descriptors contained in \p t. + */ +// convert: HGraphicBufferProducer::QueueBufferOutput -> +// IGraphicBufferProducer::QueueBufferOutput +inline bool convertTo( + BGraphicBufferProducer::QueueBufferOutput* l, + HGraphicBufferProducer::QueueBufferOutput const& t) { + if (!convertTo(&(l->frameTimestamps), t.frameTimestamps)) { + return false; + } + l->width = t.width; + l->height = t.height; + l->transformHint = t.transformHint; + l->numPendingBuffers = t.numPendingBuffers; + l->nextFrameNumber = t.nextFrameNumber; + l->bufferReplaced = t.bufferReplaced; + return true; +} + +/** + * \brief Convert `IGraphicBufferProducer::DisconnectMode` to + * `HGraphicBufferProducer::DisconnectMode`. + * + * \param[in] l The source `IGraphicBufferProducer::DisconnectMode`. + * \return The corresponding `HGraphicBufferProducer::DisconnectMode`. + */ +inline HGraphicBufferProducer::DisconnectMode toHDisconnectMode( + BGraphicBufferProducer::DisconnectMode l) { + switch (l) { + case BGraphicBufferProducer::DisconnectMode::Api: + return HGraphicBufferProducer::DisconnectMode::API; + case BGraphicBufferProducer::DisconnectMode::AllLocal: + return HGraphicBufferProducer::DisconnectMode::ALL_LOCAL; + } + return HGraphicBufferProducer::DisconnectMode::API; +} + +// H2BGraphicBufferProducer + +status_t H2BGraphicBufferProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { + *buf = new GraphicBuffer(); + status_t fnStatus; + status_t transStatus = toStatusT(mBase->requestBuffer( + static_cast<int32_t>(slot), + [&fnStatus, &buf] (Status status, AnwBuffer const& buffer) { + fnStatus = toStatusT(status); + if (!convertTo(buf->get(), buffer)) { + fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; + } + })); + return transStatus == NO_ERROR ? fnStatus : transStatus; +} + +status_t H2BGraphicBufferProducer::setMaxDequeuedBufferCount( + int maxDequeuedBuffers) { + return toStatusT(mBase->setMaxDequeuedBufferCount( + static_cast<int32_t>(maxDequeuedBuffers))); +} + +status_t H2BGraphicBufferProducer::setAsyncMode(bool async) { + return toStatusT(mBase->setAsyncMode(async)); +} + +status_t H2BGraphicBufferProducer::dequeueBuffer( + int* slot, sp<Fence>* fence, + uint32_t w, uint32_t h, ::android::PixelFormat format, + uint32_t usage, FrameEventHistoryDelta* outTimestamps) { + *fence = new Fence(); + status_t fnStatus; + status_t transStatus = toStatusT(mBase->dequeueBuffer( + w, h, static_cast<PixelFormat>(format), usage, + outTimestamps != nullptr, + [&fnStatus, slot, fence, outTimestamps] ( + Status status, + int32_t tSlot, + hidl_handle const& tFence, + HGraphicBufferProducer::FrameEventHistoryDelta const& tTs) { + fnStatus = toStatusT(status); + *slot = tSlot; + if (!convertTo(fence->get(), tFence)) { + ALOGE("H2BGraphicBufferProducer::dequeueBuffer - " + "Invalid output fence"); + fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; + } + if (outTimestamps && !convertTo(outTimestamps, tTs)) { + ALOGE("H2BGraphicBufferProducer::dequeueBuffer - " + "Invalid output timestamps"); + fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; + } + })); + return transStatus == NO_ERROR ? fnStatus : transStatus; +} + +status_t H2BGraphicBufferProducer::detachBuffer(int slot) { + return toStatusT(mBase->detachBuffer(static_cast<int>(slot))); +} + +status_t H2BGraphicBufferProducer::detachNextBuffer( + sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { + *outBuffer = new GraphicBuffer(); + *outFence = new Fence(); + status_t fnStatus; + status_t transStatus = toStatusT(mBase->detachNextBuffer( + [&fnStatus, outBuffer, outFence] ( + Status status, + AnwBuffer const& tBuffer, + hidl_handle const& tFence) { + fnStatus = toStatusT(status); + if (!convertTo(outFence->get(), tFence)) { + ALOGE("H2BGraphicBufferProducer::detachNextBuffer - " + "Invalid output fence"); + fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; + } + if (!convertTo(outBuffer->get(), tBuffer)) { + ALOGE("H2BGraphicBufferProducer::detachNextBuffer - " + "Invalid output buffer"); + fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; + } + })); + return transStatus == NO_ERROR ? fnStatus : transStatus; +} + +status_t H2BGraphicBufferProducer::attachBuffer( + int* outSlot, const sp<GraphicBuffer>& buffer) { + AnwBuffer tBuffer; + wrapAs(&tBuffer, *buffer); + status_t fnStatus; + status_t transStatus = toStatusT(mBase->attachBuffer(tBuffer, + [&fnStatus, outSlot] (Status status, int32_t slot) { + fnStatus = toStatusT(status); + *outSlot = slot; + })); + return transStatus == NO_ERROR ? fnStatus : transStatus; +} + +status_t H2BGraphicBufferProducer::queueBuffer( + int slot, + const QueueBufferInput& input, + QueueBufferOutput* output) { + HGraphicBufferProducer::QueueBufferInput tInput; + native_handle_t* nh; + if (!wrapAs(&tInput, &nh, input)) { + ALOGE("H2BGraphicBufferProducer::queueBuffer - " + "Invalid input"); + return BAD_VALUE; + } + status_t fnStatus; + status_t transStatus = toStatusT(mBase->queueBuffer(slot, tInput, + [&fnStatus, output] ( + Status status, + HGraphicBufferProducer::QueueBufferOutput const& tOutput) { + fnStatus = toStatusT(status); + if (!convertTo(output, tOutput)) { + ALOGE("H2BGraphicBufferProducer::queueBuffer - " + "Invalid output"); + fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; + } + })); + native_handle_delete(nh); + return transStatus == NO_ERROR ? fnStatus : transStatus; +} + +status_t H2BGraphicBufferProducer::cancelBuffer(int slot, const sp<Fence>& fence) { + hidl_handle tFence; + native_handle_t* nh = nullptr; + if ((fence == nullptr) || !wrapAs(&tFence, &nh, *fence)) { + ALOGE("H2BGraphicBufferProducer::cancelBuffer - " + "Invalid input fence"); + return BAD_VALUE; + } + + status_t status = toStatusT(mBase->cancelBuffer( + static_cast<int32_t>(slot), tFence)); + native_handle_delete(nh); + return status; +} + +int H2BGraphicBufferProducer::query(int what, int* value) { + int result; + status_t transStatus = toStatusT(mBase->query( + static_cast<int32_t>(what), + [&result, value] (int32_t tResult, int32_t tValue) { + result = static_cast<int>(tResult); + *value = static_cast<int>(tValue); + })); + return transStatus == NO_ERROR ? result : static_cast<int>(transStatus); +} + +status_t H2BGraphicBufferProducer::connect( + const sp<IProducerListener>& listener, int api, + bool producerControlledByApp, QueueBufferOutput* output) { + sp<HProducerListener> tListener = listener == nullptr ? + nullptr : new B2HProducerListener(listener); + status_t fnStatus; + status_t transStatus = toStatusT(mBase->connect( + tListener, static_cast<int32_t>(api), producerControlledByApp, + [&fnStatus, output] ( + Status status, + HGraphicBufferProducer::QueueBufferOutput const& tOutput) { + fnStatus = toStatusT(status); + if (!convertTo(output, tOutput)) { + ALOGE("H2BGraphicBufferProducer::connect - " + "Invalid output"); + fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; + } + })); + return transStatus == NO_ERROR ? fnStatus : transStatus; +} + +status_t H2BGraphicBufferProducer::disconnect(int api, DisconnectMode mode) { + return toStatusT(mBase->disconnect( + static_cast<int32_t>(api), toHDisconnectMode(mode))); +} + +status_t H2BGraphicBufferProducer::setSidebandStream( + const sp<NativeHandle>& stream) { + return toStatusT(mBase->setSidebandStream(stream == nullptr ? nullptr : stream->handle())); +} + +void H2BGraphicBufferProducer::allocateBuffers(uint32_t width, uint32_t height, + ::android::PixelFormat format, uint32_t usage) { + mBase->allocateBuffers( + width, height, static_cast<PixelFormat>(format), usage); +} + +status_t H2BGraphicBufferProducer::allowAllocation(bool allow) { + return toStatusT(mBase->allowAllocation(allow)); +} + +status_t H2BGraphicBufferProducer::setGenerationNumber(uint32_t generationNumber) { + return toStatusT(mBase->setGenerationNumber(generationNumber)); +} + +String8 H2BGraphicBufferProducer::getConsumerName() const { + String8 lName; + mBase->getConsumerName([&lName] (hidl_string const& name) { + lName = name.c_str(); + }); + return lName; +} + +status_t H2BGraphicBufferProducer::setSharedBufferMode(bool sharedBufferMode) { + return toStatusT(mBase->setSharedBufferMode(sharedBufferMode)); +} + +status_t H2BGraphicBufferProducer::setAutoRefresh(bool autoRefresh) { + return toStatusT(mBase->setAutoRefresh(autoRefresh)); +} + +status_t H2BGraphicBufferProducer::setDequeueTimeout(nsecs_t timeout) { + return toStatusT(mBase->setDequeueTimeout(static_cast<int64_t>(timeout))); +} + +status_t H2BGraphicBufferProducer::getLastQueuedBuffer( + sp<GraphicBuffer>* outBuffer, + sp<Fence>* outFence, + float outTransformMatrix[16]) { + status_t fnStatus; + status_t transStatus = toStatusT(mBase->getLastQueuedBuffer( + [&fnStatus, outBuffer, outFence, &outTransformMatrix] ( + Status status, + AnwBuffer const& buffer, + hidl_handle const& fence, + hidl_array<float, 16> const& transformMatrix) { + fnStatus = toStatusT(status); + *outBuffer = new GraphicBuffer(); + if (!convertTo(outBuffer->get(), buffer)) { + ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - " + "Invalid output buffer"); + fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; + } + *outFence = new Fence(); + if (!convertTo(outFence->get(), fence)) { + ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - " + "Invalid output fence"); + fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; + } + std::copy(transformMatrix.data(), + transformMatrix.data() + 16, + outTransformMatrix); + })); + return transStatus == NO_ERROR ? fnStatus : transStatus; +} + +void H2BGraphicBufferProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) { + mBase->getFrameTimestamps([outDelta] ( + HGraphicBufferProducer::FrameEventHistoryDelta const& tDelta) { + convertTo(outDelta, tDelta); + }); +} + +status_t H2BGraphicBufferProducer::getUniqueId(uint64_t* outId) const { + status_t fnStatus; + status_t transStatus = toStatusT(mBase->getUniqueId( + [&fnStatus, outId] (Status status, uint64_t id) { + fnStatus = toStatusT(status); + *outId = id; + })); + return transStatus == NO_ERROR ? fnStatus : transStatus; +} + +} // namespace utils +} // namespace V1_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/libs/gui/include/private/gui/BitTube.h b/libs/gui/include/private/gui/BitTube.h new file mode 100644 index 0000000000..13c01623b7 --- /dev/null +++ b/libs/gui/include/private/gui/BitTube.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 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. + */ + +#pragma once + +#include <android-base/unique_fd.h> +#include <binder/Parcelable.h> +#include <utils/Errors.h> + +namespace android { + +class Parcel; + +namespace gui { + +class BitTube : public Parcelable { +public: + // creates an uninitialized BitTube (to unparcel into) + BitTube() = default; + + // creates a BitTube with a a specified send and receive buffer size + explicit BitTube(size_t bufsize); + + // creates a BitTube with a default (4KB) send buffer + struct DefaultSizeType {}; + static constexpr DefaultSizeType DefaultSize{}; + explicit BitTube(DefaultSizeType); + + explicit BitTube(const Parcel& data); + + virtual ~BitTube() = default; + + // check state after construction + status_t initCheck() const; + + // get receive file-descriptor + int getFd() const; + + // get the send file-descriptor. + int getSendFd() const; + + // moves the receive file descriptor out of this BitTube + base::unique_fd moveReceiveFd(); + + // resets this BitTube's receive file descriptor to receiveFd + void setReceiveFd(base::unique_fd&& receiveFd); + + // send objects (sized blobs). All objects are guaranteed to be written or the call fails. + template <typename T> + static ssize_t sendObjects(BitTube* tube, T const* events, size_t count) { + return sendObjects(tube, events, count, sizeof(T)); + } + + // receive objects (sized blobs). If the receiving buffer isn't large enough, excess messages + // are silently discarded. + template <typename T> + static ssize_t recvObjects(BitTube* tube, T* events, size_t count) { + return recvObjects(tube, events, count, sizeof(T)); + } + + // implement the Parcelable protocol. Only parcels the receive file descriptor + status_t writeToParcel(Parcel* reply) const; + status_t readFromParcel(const Parcel* parcel); + +private: + void init(size_t rcvbuf, size_t sndbuf); + + // send a message. The write is guaranteed to send the whole message or fail. + ssize_t write(void const* vaddr, size_t size); + + // receive a message. the passed buffer must be at least as large as the write call used to send + // the message, excess data is silently discarded. + ssize_t read(void* vaddr, size_t size); + + base::unique_fd mSendFd; + mutable base::unique_fd mReceiveFd; + + static ssize_t sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize); + + static ssize_t recvObjects(BitTube* tube, void* events, size_t count, size_t objSize); +}; + +} // namespace gui +} // namespace android diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 477900377a..fa87f29aa1 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -9,14 +9,14 @@ cc_test { clang: true, srcs: [ + "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", "CpuConsumer_test.cpp", "FillBuffer.cpp", "GLTest.cpp", "IGraphicBufferProducer_test.cpp", + "Malicious.cpp", "MultiTextureConsumer_test.cpp", - "Sensor_test.cpp", - "SRGB_test.cpp", "StreamSplitter_test.cpp", "SurfaceTextureClient_test.cpp", "SurfaceTextureFBO_test.cpp", @@ -29,6 +29,8 @@ cc_test { ], shared_libs: [ + "android.hardware.configstore@1.0", + "android.hardware.configstore-utils", "liblog", "libEGL", "libGLESv1_CM", @@ -36,8 +38,10 @@ cc_test { "libbinder", "libcutils", "libgui", - "libsync", + "libhidlbase", + "libhidltransport", "libui", "libutils", + "libnativewindow" ], } diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp new file mode 100644 index 0000000000..d64e530488 --- /dev/null +++ b/libs/gui/tests/BufferItemConsumer_test.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "BufferItemConsumer_test" +//#define LOG_NDEBUG 0 + +#include <gtest/gtest.h> +#include <gui/BufferItemConsumer.h> +#include <gui/IProducerListener.h> +#include <gui/Surface.h> + +namespace android { + +static constexpr int kWidth = 100; +static constexpr int kHeight = 100; +static constexpr int kMaxLockedBuffers = 3; +static constexpr int kFormat = HAL_PIXEL_FORMAT_RGBA_8888; +static constexpr int kFrameSleepUs = 30 * 1000; + +class BufferItemConsumerTest : public ::testing::Test { + protected: + struct BufferFreedListener + : public BufferItemConsumer::BufferFreedListener { + explicit BufferFreedListener(BufferItemConsumerTest* test) + : mTest(test) {} + void onBufferFreed(const wp<GraphicBuffer>& /* gBuffer */) override { + mTest->HandleBufferFreed(); + } + BufferItemConsumerTest* mTest; + }; + + void SetUp() override { + BufferQueue::createBufferQueue(&mProducer, &mConsumer); + mBIC = + new BufferItemConsumer(mConsumer, kFormat, kMaxLockedBuffers, true); + String8 name("BufferItemConsumer_Under_Test"); + mBIC->setName(name); + mBFL = new BufferFreedListener(this); + mBIC->setBufferFreedListener(mBFL); + + sp<IProducerListener> producerListener = new DummyProducerListener(); + IGraphicBufferProducer::QueueBufferOutput bufferOutput; + ASSERT_EQ(NO_ERROR, + mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU, + true, &bufferOutput)); + ASSERT_EQ(NO_ERROR, + mProducer->setMaxDequeuedBufferCount(kMaxLockedBuffers)); + } + + int GetFreedBufferCount() { + std::lock_guard<std::mutex> lock(mMutex); + return mFreedBufferCount; + } + + void HandleBufferFreed() { + std::lock_guard<std::mutex> lock(mMutex); + mFreedBufferCount++; + ALOGV("HandleBufferFreed, mFreedBufferCount=%d", mFreedBufferCount); + } + + void DequeueBuffer(int* outSlot) { + ASSERT_NE(outSlot, nullptr); + + int slot; + sp<Fence> outFence; + status_t ret = mProducer->dequeueBuffer(&slot, &outFence, kWidth, + kHeight, 0, 0, nullptr); + ASSERT_GE(ret, 0); + + ALOGV("dequeueBuffer: slot=%d", slot); + if (ret & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { + ret = mProducer->requestBuffer(slot, &mBuffers[slot]); + ASSERT_EQ(NO_ERROR, ret); + } + *outSlot = slot; + } + + void QueueBuffer(int slot) { + ALOGV("enqueueBuffer: slot=%d", slot); + IGraphicBufferProducer::QueueBufferInput bufferInput( + 0ULL, true, HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT, + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); + IGraphicBufferProducer::QueueBufferOutput bufferOutput; + status_t ret = mProducer->queueBuffer(slot, bufferInput, &bufferOutput); + ASSERT_EQ(NO_ERROR, ret); + } + + void AcquireBuffer(int* outSlot) { + ASSERT_NE(outSlot, nullptr); + BufferItem buffer; + status_t ret = mBIC->acquireBuffer(&buffer, 0, false); + ASSERT_EQ(NO_ERROR, ret); + + ALOGV("acquireBuffer: slot=%d", buffer.mSlot); + *outSlot = buffer.mSlot; + } + + void ReleaseBuffer(int slot) { + ALOGV("releaseBuffer: slot=%d", slot); + BufferItem buffer; + buffer.mSlot = slot; + buffer.mGraphicBuffer = mBuffers[slot]; + status_t ret = mBIC->releaseBuffer(buffer, Fence::NO_FENCE); + ASSERT_EQ(NO_ERROR, ret); + } + + + std::mutex mMutex; + int mFreedBufferCount{0}; + + sp<BufferItemConsumer> mBIC; + sp<BufferFreedListener> mBFL; + sp<IGraphicBufferProducer> mProducer; + sp<IGraphicBufferConsumer> mConsumer; + sp<GraphicBuffer> mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS]; +}; + +// Test that detaching buffer from consumer side triggers onBufferFreed. +TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DetachBufferFromConsumer) { + int slot; + // Producer: generate a dummy buffer. + DequeueBuffer(&slot); + QueueBuffer(slot); + + ASSERT_EQ(0, GetFreedBufferCount()); + // Consumer: acquire the buffer and then detach it. + AcquireBuffer(&slot); + status_t ret = mBIC->detachBuffer(slot); + ASSERT_EQ(NO_ERROR, ret); + + // Sleep to give some time for callbacks to happen. + usleep(kFrameSleepUs); + ASSERT_EQ(1, GetFreedBufferCount()); +} + +// Test that detaching buffer from producer side triggers onBufferFreed. +TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DetachBufferFromProducer) { + int slot; + // Let buffer go through the cycle at least once. + DequeueBuffer(&slot); + QueueBuffer(slot); + AcquireBuffer(&slot); + ReleaseBuffer(slot); + + ASSERT_EQ(0, GetFreedBufferCount()); + + // Producer: generate the buffer again. + DequeueBuffer(&slot); + + // Producer: detach the buffer. + status_t ret = mProducer->detachBuffer(slot); + ASSERT_EQ(NO_ERROR, ret); + + // Sleep to give some time for callbacks to happen. + usleep(kFrameSleepUs); + ASSERT_EQ(1, GetFreedBufferCount()); +} + +// Test that abandoning BufferItemConsumer triggers onBufferFreed. +TEST_F(BufferItemConsumerTest, TriggerBufferFreed_AbandonBufferItemConsumer) { + int slot; + // Let buffer go through the cycle at least once. + DequeueBuffer(&slot); + QueueBuffer(slot); + AcquireBuffer(&slot); + ReleaseBuffer(slot); + + // Abandon the BufferItemConsumer. + mBIC->abandon(); + + // Sleep to give some time for callbacks to happen. + usleep(kFrameSleepUs); + ASSERT_EQ(1, GetFreedBufferCount()); +} + +// Test that delete BufferItemConsumer triggers onBufferFreed. +TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DeleteBufferItemConsumer) { + int slot; + // Let buffer go through the cycle at least once. + DequeueBuffer(&slot); + QueueBuffer(slot); + AcquireBuffer(&slot); + ReleaseBuffer(slot); + + // Delete the BufferItemConsumer. + mBIC.clear(); + + // Sleep to give some time for callbacks to happen. + usleep(kFrameSleepUs); + ASSERT_EQ(1, GetFreedBufferCount()); +} + +} // namespace android diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 65df7dc991..60c1277db0 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -98,7 +98,11 @@ static const uint32_t TEST_DATA = 0x12345678u; // XXX: Tests that fork a process to hold the BufferQueue must run before tests // that use a local BufferQueue, or else Binder will get unhappy -TEST_F(BufferQueueTest, BufferQueueInAnotherProcess) { +// +// In one instance this was a crash in the createBufferQueue where the +// binder call to create a buffer allocator apparently got garbage back. +// See b/36592665. +TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) { const String16 PRODUCER_NAME = String16("BQTestProducer"); const String16 CONSUMER_NAME = String16("BQTestConsumer"); @@ -139,7 +143,7 @@ TEST_F(BufferQueueTest, BufferQueueInAnotherProcess) { sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN)); + GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); uint32_t* dataIn; @@ -183,7 +187,7 @@ TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) { for (int i = 0; i < 2; i++) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, - GRALLOC_USAGE_SW_READ_OFTEN)); + GRALLOC_USAGE_SW_READ_OFTEN, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); @@ -191,7 +195,7 @@ TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, - GRALLOC_USAGE_SW_READ_OFTEN)); + GRALLOC_USAGE_SW_READ_OFTEN, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); @@ -234,7 +238,7 @@ TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) for (int i = 0; i < 3; i++) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, - GRALLOC_USAGE_SW_READ_OFTEN)); + GRALLOC_USAGE_SW_READ_OFTEN, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); @@ -270,7 +274,7 @@ TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, - GRALLOC_USAGE_SW_READ_OFTEN)); + GRALLOC_USAGE_SW_READ_OFTEN, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); @@ -280,7 +284,7 @@ TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) { for (int i = 0; i < 2; i++) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, - GRALLOC_USAGE_SW_READ_OFTEN)); + GRALLOC_USAGE_SW_READ_OFTEN, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); @@ -330,7 +334,7 @@ TEST_F(BufferQueueTest, DetachAndReattachOnProducerSide) { sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN)); + GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(slot)); // Not requested ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); ASSERT_EQ(OK, mProducer->detachBuffer(slot)); @@ -379,7 +383,7 @@ TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) { sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN)); + GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); IGraphicBufferProducer::QueueBufferInput input(0, false, HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), @@ -415,7 +419,7 @@ TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN)); + GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); uint32_t* dataOut; @@ -438,7 +442,7 @@ TEST_F(BufferQueueTest, MoveFromConsumerToProducer) { sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN)); + GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); uint32_t* dataIn; @@ -487,13 +491,13 @@ TEST_F(BufferQueueTest, TestDisallowingAllocation) { // This should return an error since it would require an allocation ASSERT_EQ(OK, mProducer->allowAllocation(false)); ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, - 0, GRALLOC_USAGE_SW_WRITE_OFTEN)); + 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); // This should succeed, now that we've lifted the prohibition ASSERT_EQ(OK, mProducer->allowAllocation(true)); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN)); + GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); // Release the previous buffer back to the BufferQueue mProducer->cancelBuffer(slot, fence); @@ -501,7 +505,7 @@ TEST_F(BufferQueueTest, TestDisallowingAllocation) { // This should fail since we're requesting a different size ASSERT_EQ(OK, mProducer->allowAllocation(false)); ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence, - WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN)); + WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); } TEST_F(BufferQueueTest, TestGenerationNumbers) { @@ -518,7 +522,7 @@ TEST_F(BufferQueueTest, TestGenerationNumbers) { int slot; sp<Fence> fence; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); sp<GraphicBuffer> buffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); @@ -561,7 +565,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithoutAutoRefresh) { sp<Fence> fence; sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0)); + mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer)); // Queue the buffer @@ -575,7 +579,8 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithoutAutoRefresh) { // always the same one and because async mode gets enabled. int slot; for (int i = 0; i < 5; i++) { - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->dequeueBuffer( + &slot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(sharedSlot, slot); ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output)); } @@ -612,7 +617,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) { sp<Fence> fence; sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0)); + mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer)); // Queue the buffer @@ -639,7 +644,8 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) { // always return the same one. int slot; for (int i = 0; i < 5; i++) { - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->dequeueBuffer( + &slot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(sharedSlot, slot); ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output)); } @@ -678,7 +684,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeUsingAlreadyDequeuedBuffer) { sp<Fence> fence; sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0)); + mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer)); // Enable shared buffer mode @@ -695,7 +701,8 @@ TEST_F(BufferQueueTest, TestSharedBufferModeUsingAlreadyDequeuedBuffer) { // always the same one and because async mode gets enabled. int slot; for (int i = 0; i < 5; i++) { - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->dequeueBuffer( + &slot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(sharedSlot, slot); ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output)); } @@ -730,7 +737,8 @@ TEST_F(BufferQueueTest, TestTimeouts) { for (int i = 0; i < 5; ++i) { int slot = BufferQueue::INVALID_BUFFER_SLOT; sp<Fence> fence = Fence::NO_FENCE; - auto result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0); + auto result = mProducer->dequeueBuffer( + &slot, &fence, 0, 0, 0, 0, nullptr); if (i < 2) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); @@ -757,7 +765,8 @@ TEST_F(BufferQueueTest, TestTimeouts) { for (int i = 0; i < 2; ++i) { int slot = BufferQueue::INVALID_BUFFER_SLOT; sp<Fence> fence = Fence::NO_FENCE; - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->dequeueBuffer( + &slot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); IGraphicBufferProducer::QueueBufferInput input(0ull, true, HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT, @@ -768,7 +777,8 @@ TEST_F(BufferQueueTest, TestTimeouts) { int slot = BufferQueue::INVALID_BUFFER_SLOT; sp<Fence> fence = Fence::NO_FENCE; auto startTime = systemTime(); - ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer( + &slot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_GE(systemTime() - startTime, TIMEOUT); // We're technically attaching the same buffer multiple times (since we @@ -789,7 +799,7 @@ TEST_F(BufferQueueTest, CanAttachWhileDisallowingAllocation) { int slot = BufferQueue::INVALID_BUFFER_SLOT; sp<Fence> sourceFence; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0)); + mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0, nullptr)); sp<GraphicBuffer> buffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); ASSERT_EQ(OK, mProducer->detachBuffer(slot)); @@ -812,7 +822,7 @@ TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) { int slot = BufferQueue::INVALID_BUFFER_SLOT; sp<Fence> fence; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); sp<GraphicBuffer> firstBuffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &firstBuffer)); @@ -824,7 +834,7 @@ TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) { // Dequeue a second buffer slot = BufferQueue::INVALID_BUFFER_SLOT; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); sp<GraphicBuffer> secondBuffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &secondBuffer)); @@ -876,7 +886,7 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { mProducer->setMaxDequeuedBufferCount(3); for (size_t i = 0; i < 3; ++i) { status_t result = mProducer->dequeueBuffer(&slots[i], &fence, - 0, 0, 0, 0); + 0, 0, 0, 0, nullptr); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer)); } @@ -889,7 +899,8 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { // The first segment is a two-buffer segment, so we only put one buffer into // the queue at a time for (size_t i = 0; i < 5; ++i) { - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->dequeueBuffer( + &slot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, @@ -904,16 +915,17 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { // two-buffer segment, but then at the end, we put two buffers in the queue // at the same time before draining it. for (size_t i = 0; i < 5; ++i) { - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->dequeueBuffer( + &slot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); std::this_thread::sleep_for(16ms); } - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, @@ -928,10 +940,11 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { // The third segment is a triple-buffer segment, so the queue is switching // between one buffer and two buffers deep. - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); for (size_t i = 0; i < 5; ++i) { - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->dequeueBuffer( + &slot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, @@ -1012,7 +1025,7 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { mProducer->setMaxDequeuedBufferCount(4); for (size_t i = 0; i < 4; ++i) { status_t result = mProducer->dequeueBuffer(&slots[i], &fence, - 0, 0, 0, 0); + 0, 0, 0, 0, nullptr); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer)); } @@ -1023,14 +1036,14 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { // Get buffers in all states: dequeued, filled, acquired, free // Fill 3 buffers - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); // Dequeue 1 buffer - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); // Acquire and free 1 buffer ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); @@ -1044,7 +1057,7 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { // Check no free buffers in dump String8 dumpString; - mConsumer->dumpState(dumpString, nullptr); + mConsumer->dumpState(String8{}, &dumpString); // Parse the dump to ensure that all buffer slots that are FREE also // have a null GraphicBuffer @@ -1067,4 +1080,122 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { } } +TEST_F(BufferQueueTest, TestBufferReplacedInQueueBuffer) { + createBufferQueue(); + sp<DummyConsumer> dc(new DummyConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); + IGraphicBufferProducer::QueueBufferOutput output; + ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, + NATIVE_WINDOW_API_CPU, true, &output)); + ASSERT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1)); + + int slot = BufferQueue::INVALID_BUFFER_SLOT; + sp<Fence> fence = Fence::NO_FENCE; + sp<GraphicBuffer> buffer = nullptr; + IGraphicBufferProducer::QueueBufferInput input(0ull, true, + HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT, + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); + BufferItem item{}; + + // Preallocate, dequeue, request, and cancel 2 buffers so we don't get + // BUFFER_NEEDS_REALLOCATION below + int slots[2] = {}; + ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2)); + for (size_t i = 0; i < 2; ++i) { + status_t result = mProducer->dequeueBuffer(&slots[i], &fence, + 0, 0, 0, 0, nullptr); + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); + ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer)); + } + for (size_t i = 0; i < 2; ++i) { + ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE)); + } + + // Fill 2 buffers without consumer consuming them. Verify that all + // queued buffer returns proper bufferReplaced flag + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + ASSERT_EQ(false, output.bufferReplaced); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + ASSERT_EQ(true, output.bufferReplaced); +} + +TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) { + createBufferQueue(); + sp<DummyConsumer> dc(new DummyConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); + IGraphicBufferProducer::QueueBufferOutput output; + sp<IProducerListener> dummyListener(new DummyProducerListener); + ASSERT_EQ(OK, mProducer->connect(dummyListener, NATIVE_WINDOW_API_CPU, + true, &output)); + + int slot = BufferQueue::INVALID_BUFFER_SLOT; + sp<Fence> fence = Fence::NO_FENCE; + sp<GraphicBuffer> buffer = nullptr; + IGraphicBufferProducer::QueueBufferInput input(0ull, true, + HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT, + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); + + // Dequeue, request, and queue one buffer + status_t result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, + nullptr); + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); + ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); + ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + + // Acquire and release the buffer. Upon acquiring, the buffer handle should + // be non-null since this is the first time we've acquired this slot. + BufferItem item; + ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); + ASSERT_EQ(slot, item.mSlot); + ASSERT_NE(nullptr, item.mGraphicBuffer.get()); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + + // Dequeue and queue the buffer again + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + + // Acquire and release the buffer again. Upon acquiring, the buffer handle + // should be null since this is not the first time we've acquired this slot. + ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); + ASSERT_EQ(slot, item.mSlot); + ASSERT_EQ(nullptr, item.mGraphicBuffer.get()); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + + // Dequeue and queue the buffer again + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + + // Disconnect the producer end. This should clear all of the slots and mark + // the buffer in the queue as stale. + ASSERT_EQ(OK, mProducer->disconnect(NATIVE_WINDOW_API_CPU)); + + // Acquire the buffer again. Upon acquiring, the buffer handle should not be + // null since the queued buffer should have been marked as stale, which + // should trigger the BufferQueue to resend the buffer handle. + ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); + ASSERT_EQ(slot, item.mSlot); + ASSERT_NE(nullptr, item.mGraphicBuffer.get()); +} + +TEST_F(BufferQueueTest, TestProducerConnectDisconnect) { + createBufferQueue(); + sp<DummyConsumer> dc(new DummyConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); + IGraphicBufferProducer::QueueBufferOutput output; + sp<IProducerListener> dummyListener(new DummyProducerListener); + ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU)); + ASSERT_EQ(OK, mProducer->connect( + dummyListener, NATIVE_WINDOW_API_CPU, true, &output)); + ASSERT_EQ(BAD_VALUE, mProducer->connect( + dummyListener, NATIVE_WINDOW_API_MEDIA, true, &output)); + + ASSERT_EQ(BAD_VALUE, mProducer->disconnect(NATIVE_WINDOW_API_MEDIA)); + ASSERT_EQ(OK, mProducer->disconnect(NATIVE_WINDOW_API_CPU)); + ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU)); +} + } // namespace android diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp index 9c2e838b09..5848c74b8c 100644 --- a/libs/gui/tests/CpuConsumer_test.cpp +++ b/libs/gui/tests/CpuConsumer_test.cpp @@ -490,7 +490,7 @@ void produceOneFrame(const sp<ANativeWindow>& anw, ASSERT_TRUE(anb != NULL); - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); *stride = buf->getStride(); uint8_t* img = NULL; diff --git a/libs/gui/tests/DummyConsumer.h b/libs/gui/tests/DummyConsumer.h index 0511e165c2..502bdf981b 100644 --- a/libs/gui/tests/DummyConsumer.h +++ b/libs/gui/tests/DummyConsumer.h @@ -19,9 +19,9 @@ namespace android { struct DummyConsumer : public BnConsumerListener { - virtual void onFrameAvailable(const BufferItem& /* item */) {} - virtual void onBuffersReleased() {} - virtual void onSidebandStreamChanged() {} + void onFrameAvailable(const BufferItem& /* item */) override {} + void onBuffersReleased() override {} + void onSidebandStreamChanged() override {} }; } // namespace android diff --git a/libs/gui/tests/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp index 079962c8b9..ccd674fcb8 100644 --- a/libs/gui/tests/FillBuffer.cpp +++ b/libs/gui/tests/FillBuffer.cpp @@ -95,7 +95,7 @@ void produceOneRGBA8Frame(const sp<ANativeWindow>& anw) { &anb)); ASSERT_TRUE(anb != NULL); - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); uint8_t* img = NULL; ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp index 9f3304731e..aa071f68b0 100644 --- a/libs/gui/tests/IGraphicBufferProducer_test.cpp +++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "IGraphicBufferProducer_test" //#define LOG_NDEBUG 0 +#include "DummyConsumer.h" + #include <gtest/gtest.h> #include <utils/String8.h> @@ -64,12 +66,6 @@ namespace { const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE; }; // namespace anonymous -struct DummyConsumer : public BnConsumerListener { - virtual void onFrameAvailable(const BufferItem& /* item */) {} - virtual void onBuffersReleased() {} - virtual void onSidebandStreamChanged() {} -}; - class IGraphicBufferProducerTest : public ::testing::Test { protected: @@ -196,7 +192,7 @@ protected: }; 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); + return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage, nullptr); } void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence, @@ -210,7 +206,7 @@ protected: ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(slot, fence, DEFAULT_WIDTH, - DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS))); + DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr))); EXPECT_LE(0, *slot); EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, *slot); @@ -349,7 +345,7 @@ TEST_F(IGraphicBufferProducerTest, Queue_Succeeds) { ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS))); + TEST_PRODUCER_USAGE_BITS, nullptr))); EXPECT_LE(0, dequeuedSlot); EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, dequeuedSlot); @@ -366,20 +362,12 @@ TEST_F(IGraphicBufferProducerTest, Queue_Succeeds) { ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output)); { - uint32_t width; - uint32_t height; - uint32_t transformHint; - uint32_t numPendingBuffers; - uint64_t nextFrameNumber; - - output.deflate(&width, &height, &transformHint, &numPendingBuffers, - &nextFrameNumber); - - EXPECT_EQ(DEFAULT_WIDTH, width); - EXPECT_EQ(DEFAULT_HEIGHT, height); - EXPECT_EQ(DEFAULT_TRANSFORM_HINT, transformHint); - EXPECT_EQ(1u, numPendingBuffers); // since queueBuffer was called exactly once - EXPECT_EQ(2u, nextFrameNumber); + EXPECT_EQ(DEFAULT_WIDTH, output.width); + EXPECT_EQ(DEFAULT_HEIGHT, output.height); + EXPECT_EQ(DEFAULT_TRANSFORM_HINT, output.transformHint); + // Since queueBuffer was called exactly once + EXPECT_EQ(1u, output.numPendingBuffers); + EXPECT_EQ(2u, output.nextFrameNumber); } // Buffer was not in the dequeued state @@ -416,7 +404,7 @@ TEST_F(IGraphicBufferProducerTest, Queue_ReturnsError) { ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS))); + TEST_PRODUCER_USAGE_BITS, nullptr))); // Slot was enqueued without requesting a buffer { @@ -485,7 +473,7 @@ TEST_F(IGraphicBufferProducerTest, CancelBuffer_DoesntCrash) { ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS))); + TEST_PRODUCER_USAGE_BITS, nullptr))); // No return code, but at least test that it doesn't blow up... // TODO: add a return code @@ -534,7 +522,7 @@ TEST_F(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) { (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS))) + TEST_PRODUCER_USAGE_BITS, nullptr))) << "iteration: " << i << ", slot: " << dequeuedSlot; } @@ -571,7 +559,7 @@ TEST_F(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Fails) { (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS))) + TEST_PRODUCER_USAGE_BITS, nullptr))) << "slot: " << dequeuedSlot; } @@ -606,7 +594,8 @@ TEST_F(IGraphicBufferProducerTest, SetAsyncMode_Succeeds) { ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS))) << "slot : " << dequeuedSlot; + TEST_PRODUCER_USAGE_BITS, nullptr))) + << "slot : " << dequeuedSlot; ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer)); ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output)); } @@ -622,7 +611,8 @@ TEST_F(IGraphicBufferProducerTest, SetAsyncMode_Fails) { ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS))) << "slot: " << dequeuedSlot; + TEST_PRODUCER_USAGE_BITS, nullptr))) + << "slot: " << dequeuedSlot; } // Abandon buffer queue @@ -639,7 +629,7 @@ TEST_F(IGraphicBufferProducerTest, sp<Fence> fence; ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH, - DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)); + DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr)); } TEST_F(IGraphicBufferProducerTest, @@ -659,7 +649,8 @@ TEST_F(IGraphicBufferProducerTest, ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH, - DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS))); + DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, + nullptr))); EXPECT_LE(0, slot); EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, slot); diff --git a/libs/gui/tests/Malicious.cpp b/libs/gui/tests/Malicious.cpp new file mode 100644 index 0000000000..7ecf08cdb0 --- /dev/null +++ b/libs/gui/tests/Malicious.cpp @@ -0,0 +1,202 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gui/BufferQueue.h> +#include <gui/IProducerListener.h> +#include <gui/Surface.h> + +#include <android/native_window.h> + +#include <gtest/gtest.h> + +namespace android { +namespace test { + +class ProxyBQP : public BnGraphicBufferProducer { +public: + ProxyBQP(const sp<IGraphicBufferProducer>& producer) : mProducer(producer) {} + + // Pass through calls to mProducer + status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override { + return mProducer->requestBuffer(slot, buf); + } + status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override { + return mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers); + } + status_t setAsyncMode(bool async) override { return mProducer->setAsyncMode(async); } + status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h, PixelFormat format, + uint32_t usage, FrameEventHistoryDelta* outTimestamps) override { + return mProducer->dequeueBuffer(slot, fence, w, h, format, usage, outTimestamps); + } + status_t detachBuffer(int slot) override { return mProducer->detachBuffer(slot); } + status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override { + return mProducer->detachNextBuffer(outBuffer, outFence); + } + status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer) override { + return mProducer->attachBuffer(outSlot, buffer); + } + status_t queueBuffer(int slot, const QueueBufferInput& input, + QueueBufferOutput* output) override { + return mProducer->queueBuffer(slot, input, output); + } + status_t cancelBuffer(int slot, const sp<Fence>& fence) override { + return mProducer->cancelBuffer(slot, fence); + } + int query(int what, int* value) override { return mProducer->query(what, value); } + status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp, + QueueBufferOutput* output) override { + return mProducer->connect(listener, api, producerControlledByApp, output); + } + status_t disconnect(int api, DisconnectMode mode) override { + return mProducer->disconnect(api, mode); + } + status_t setSidebandStream(const sp<NativeHandle>& stream) override { + return mProducer->setSidebandStream(stream); + } + void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format, + uint32_t usage) override { + mProducer->allocateBuffers(width, height, format, usage); + } + status_t allowAllocation(bool allow) override { return mProducer->allowAllocation(allow); } + status_t setGenerationNumber(uint32_t generationNumber) override { + return mProducer->setGenerationNumber(generationNumber); + } + String8 getConsumerName() const override { return mProducer->getConsumerName(); } + status_t setSharedBufferMode(bool sharedBufferMode) override { + return mProducer->setSharedBufferMode(sharedBufferMode); + } + status_t setAutoRefresh(bool autoRefresh) override { + return mProducer->setAutoRefresh(autoRefresh); + } + status_t setDequeueTimeout(nsecs_t timeout) override { + return mProducer->setDequeueTimeout(timeout); + } + status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, + float outTransformMatrix[16]) override { + return mProducer->getLastQueuedBuffer(outBuffer, outFence, outTransformMatrix); + } + void getFrameTimestamps(FrameEventHistoryDelta*) override {} + status_t getUniqueId(uint64_t* outId) const override { return mProducer->getUniqueId(outId); } + +protected: + sp<IGraphicBufferProducer> mProducer; +}; + +class MaliciousBQP : public ProxyBQP { +public: + MaliciousBQP(const sp<IGraphicBufferProducer>& producer) : ProxyBQP(producer) {} + + void beMalicious(int32_t value) { mMaliciousValue = value; } + + void setExpectedSlot(int32_t slot) { mExpectedSlot = slot; } + + // Override dequeueBuffer, optionally corrupting the returned slot number + status_t dequeueBuffer(int* buf, sp<Fence>* fence, uint32_t width, uint32_t height, + PixelFormat format, uint32_t usage, + FrameEventHistoryDelta* outTimestamps) override { + EXPECT_EQ(BUFFER_NEEDS_REALLOCATION, + mProducer->dequeueBuffer(buf, fence, width, height, format, usage, + outTimestamps)); + EXPECT_EQ(mExpectedSlot, *buf); + if (mMaliciousValue != 0) { + *buf = mMaliciousValue; + return NO_ERROR; + } else { + return BUFFER_NEEDS_REALLOCATION; + } + } + +private: + int32_t mMaliciousValue = 0; + int32_t mExpectedSlot = 0; +}; + +class DummyListener : public BnConsumerListener { +public: + void onFrameAvailable(const BufferItem&) override {} + void onBuffersReleased() override {} + void onSidebandStreamChanged() override {} +}; + +sp<MaliciousBQP> getMaliciousBQP() { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + sp<IConsumerListener> listener = new DummyListener; + consumer->consumerConnect(listener, false); + + sp<MaliciousBQP> malicious = new MaliciousBQP(producer); + return malicious; +} + +TEST(Malicious, Bug36991414Max) { + sp<MaliciousBQP> malicious = getMaliciousBQP(); + sp<Surface> surface = new Surface(malicious); + + ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false)); + ANativeWindow_Buffer buffer; + ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr)); + ASSERT_EQ(NO_ERROR, surface->unlockAndPost()); + + malicious->setExpectedSlot(1); + malicious->beMalicious(std::numeric_limits<int32_t>::max()); + ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr)); +} + +TEST(Malicious, Bug36991414Min) { + sp<MaliciousBQP> malicious = getMaliciousBQP(); + sp<Surface> surface = new Surface(malicious); + + ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false)); + ANativeWindow_Buffer buffer; + ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr)); + ASSERT_EQ(NO_ERROR, surface->unlockAndPost()); + + malicious->setExpectedSlot(1); + malicious->beMalicious(std::numeric_limits<int32_t>::min()); + ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr)); +} + +TEST(Malicious, Bug36991414NegativeOne) { + sp<MaliciousBQP> malicious = getMaliciousBQP(); + sp<Surface> surface = new Surface(malicious); + + ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false)); + ANativeWindow_Buffer buffer; + ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr)); + ASSERT_EQ(NO_ERROR, surface->unlockAndPost()); + + malicious->setExpectedSlot(1); + malicious->beMalicious(-1); + ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr)); +} + +TEST(Malicious, Bug36991414NumSlots) { + sp<MaliciousBQP> malicious = getMaliciousBQP(); + sp<Surface> surface = new Surface(malicious); + + ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false)); + ANativeWindow_Buffer buffer; + ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr)); + ASSERT_EQ(NO_ERROR, surface->unlockAndPost()); + + malicious->setExpectedSlot(1); + malicious->beMalicious(BufferQueueDefs::NUM_BUFFER_SLOTS); + ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr)); +} + +} // namespace test +} // namespace android diff --git a/libs/gui/tests/SRGB_test.cpp b/libs/gui/tests/SRGB_test.cpp deleted file mode 100644 index c2640cdceb..0000000000 --- a/libs/gui/tests/SRGB_test.cpp +++ /dev/null @@ -1,486 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "SRGB_test" -//#define LOG_NDEBUG 0 - -// Ignore for this file because it flags every instance of -// ASSERT_EQ(GL_NO_ERROR, glGetError()); -#pragma clang diagnostic ignored "-Wsign-compare" - -#include "GLTest.h" - -#include <math.h> - -#include <gui/CpuConsumer.h> -#include <gui/Surface.h> -#include <gui/SurfaceComposerClient.h> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GLES3/gl3.h> - -#include <android/native_window.h> - -#include <gtest/gtest.h> - -namespace android { - -class SRGBTest : public ::testing::Test { -protected: - // Class constants - enum { - DISPLAY_WIDTH = 512, - DISPLAY_HEIGHT = 512, - PIXEL_SIZE = 4, // bytes or components - DISPLAY_SIZE = DISPLAY_WIDTH * DISPLAY_HEIGHT * PIXEL_SIZE, - ALPHA_VALUE = 223, // should be in [0, 255] - TOLERANCE = 1, - }; - static const char SHOW_DEBUG_STRING[]; - - SRGBTest() : - mInputSurface(), mCpuConsumer(), mLockedBuffer(), - mEglDisplay(EGL_NO_DISPLAY), mEglConfig(), - mEglContext(EGL_NO_CONTEXT), mEglSurface(EGL_NO_SURFACE), - mComposerClient(), mSurfaceControl(), mOutputSurface() { - } - - virtual ~SRGBTest() { - if (mEglDisplay != EGL_NO_DISPLAY) { - if (mEglSurface != EGL_NO_SURFACE) { - eglDestroySurface(mEglDisplay, mEglSurface); - } - if (mEglContext != EGL_NO_CONTEXT) { - eglDestroyContext(mEglDisplay, mEglContext); - } - eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT); - eglTerminate(mEglDisplay); - } - } - - virtual void SetUp() { - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - ASSERT_EQ(NO_ERROR, consumer->setDefaultBufferSize( - DISPLAY_WIDTH, DISPLAY_HEIGHT)); - mCpuConsumer = new CpuConsumer(consumer, 1); - String8 name("CpuConsumer_for_SRGBTest"); - mCpuConsumer->setName(name); - mInputSurface = new Surface(producer); - - ASSERT_NO_FATAL_FAILURE(createEGLSurface(mInputSurface.get())); - ASSERT_NO_FATAL_FAILURE(createDebugSurface()); - } - - virtual void TearDown() { - ASSERT_NO_FATAL_FAILURE(copyToDebugSurface()); - ASSERT_TRUE(mLockedBuffer.data != NULL); - ASSERT_EQ(NO_ERROR, mCpuConsumer->unlockBuffer(mLockedBuffer)); - } - - static float linearToSRGB(float l) { - if (l <= 0.0031308f) { - return l * 12.92f; - } else { - return 1.055f * pow(l, (1 / 2.4f)) - 0.055f; - } - } - - static float srgbToLinear(float s) { - if (s <= 0.04045) { - return s / 12.92f; - } else { - return pow(((s + 0.055f) / 1.055f), 2.4f); - } - } - - static uint8_t srgbToLinear(uint8_t u) { - float f = u / 255.0f; - return static_cast<uint8_t>(srgbToLinear(f) * 255.0f + 0.5f); - } - - void fillTexture(bool writeAsSRGB) { - uint8_t* textureData = new uint8_t[DISPLAY_SIZE]; - - for (int y = 0; y < DISPLAY_HEIGHT; ++y) { - for (int x = 0; x < DISPLAY_WIDTH; ++x) { - float realValue = static_cast<float>(x) / (DISPLAY_WIDTH - 1); - realValue *= ALPHA_VALUE / 255.0f; // Premultiply by alpha - if (writeAsSRGB) { - realValue = linearToSRGB(realValue); - } - - int offset = (y * DISPLAY_WIDTH + x) * PIXEL_SIZE; - for (int c = 0; c < 3; ++c) { - uint8_t intValue = static_cast<uint8_t>( - realValue * 255.0f + 0.5f); - textureData[offset + c] = intValue; - } - textureData[offset + 3] = ALPHA_VALUE; - } - } - - glTexImage2D(GL_TEXTURE_2D, 0, writeAsSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8, - DISPLAY_WIDTH, DISPLAY_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, - textureData); - ASSERT_EQ(GL_NO_ERROR, glGetError()); - - delete[] textureData; - } - - void initShaders() { - static const char vertexSource[] = - "attribute vec4 vPosition;\n" - "varying vec2 texCoords;\n" - "void main() {\n" - " texCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n" - " gl_Position = vPosition;\n" - "}\n"; - - static const char fragmentSource[] = - "precision mediump float;\n" - "uniform sampler2D texSampler;\n" - "varying vec2 texCoords;\n" - "void main() {\n" - " gl_FragColor = texture2D(texSampler, texCoords);\n" - "}\n"; - - GLuint program; - { - SCOPED_TRACE("Creating shader program"); - ASSERT_NO_FATAL_FAILURE(GLTest::createProgram( - vertexSource, fragmentSource, &program)); - } - - GLint positionHandle = glGetAttribLocation(program, "vPosition"); - ASSERT_EQ(GL_NO_ERROR, glGetError()); - ASSERT_NE(-1, positionHandle); - - GLint samplerHandle = glGetUniformLocation(program, "texSampler"); - ASSERT_EQ(GL_NO_ERROR, glGetError()); - ASSERT_NE(-1, samplerHandle); - - static const GLfloat vertices[] = { - -1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, -1.0f, - 1.0f, 1.0f, - }; - - glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 0, vertices); - ASSERT_EQ(GL_NO_ERROR, glGetError()); - glEnableVertexAttribArray(positionHandle); - ASSERT_EQ(GL_NO_ERROR, glGetError()); - - glUseProgram(program); - ASSERT_EQ(GL_NO_ERROR, glGetError()); - glUniform1i(samplerHandle, 0); - ASSERT_EQ(GL_NO_ERROR, glGetError()); - - GLuint textureHandle; - glGenTextures(1, &textureHandle); - ASSERT_EQ(GL_NO_ERROR, glGetError()); - glBindTexture(GL_TEXTURE_2D, textureHandle); - ASSERT_EQ(GL_NO_ERROR, glGetError()); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ASSERT_EQ(GL_NO_ERROR, glGetError()); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - ASSERT_EQ(GL_NO_ERROR, glGetError()); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - ASSERT_EQ(GL_NO_ERROR, glGetError()); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - ASSERT_EQ(GL_NO_ERROR, glGetError()); - } - - void drawTexture(bool asSRGB, GLint x, GLint y, GLsizei width, - GLsizei height) { - ASSERT_NO_FATAL_FAILURE(fillTexture(asSRGB)); - glViewport(x, y, width, height); - ASSERT_EQ(GL_NO_ERROR, glGetError()); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - ASSERT_EQ(GL_NO_ERROR, glGetError()); - } - - void checkLockedBuffer(PixelFormat format, android_dataspace dataSpace) { - ASSERT_EQ(mLockedBuffer.format, format); - ASSERT_EQ(mLockedBuffer.width, DISPLAY_WIDTH); - ASSERT_EQ(mLockedBuffer.height, DISPLAY_HEIGHT); - ASSERT_EQ(mLockedBuffer.dataSpace, dataSpace); - } - - static bool withinTolerance(int a, int b) { - int diff = a - b; - return diff >= 0 ? diff <= TOLERANCE : -diff <= TOLERANCE; - } - - // Primary producer and consumer - sp<Surface> mInputSurface; - sp<CpuConsumer> mCpuConsumer; - CpuConsumer::LockedBuffer mLockedBuffer; - - EGLDisplay mEglDisplay; - EGLConfig mEglConfig; - EGLContext mEglContext; - EGLSurface mEglSurface; - - // Auxiliary display output - sp<SurfaceComposerClient> mComposerClient; - sp<SurfaceControl> mSurfaceControl; - sp<Surface> mOutputSurface; - -private: - void createEGLSurface(Surface* inputSurface) { - mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); - - EXPECT_TRUE(eglInitialize(mEglDisplay, NULL, NULL)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - static const EGLint configAttribs[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 8, - EGL_NONE }; - - EGLint numConfigs = 0; - EXPECT_TRUE(eglChooseConfig(mEglDisplay, configAttribs, &mEglConfig, 1, - &numConfigs)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_GT(numConfigs, 0); - - static const EGLint contextAttribs[] = { - EGL_CONTEXT_CLIENT_VERSION, 3, - EGL_NONE } ; - - mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, - contextAttribs); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_CONTEXT, mEglContext); - - mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, - inputSurface, NULL); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_SURFACE, mEglSurface); - - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - } - - void createDebugSurface() { - if (getenv(SHOW_DEBUG_STRING) == NULL) return; - - mComposerClient = new SurfaceComposerClient; - ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); - - mSurfaceControl = mComposerClient->createSurface( - String8("SRGBTest Surface"), DISPLAY_WIDTH, DISPLAY_HEIGHT, - PIXEL_FORMAT_RGBA_8888); - - ASSERT_TRUE(mSurfaceControl != NULL); - ASSERT_TRUE(mSurfaceControl->isValid()); - - SurfaceComposerClient::openGlobalTransaction(); - ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF)); - ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); - SurfaceComposerClient::closeGlobalTransaction(); - - ANativeWindow_Buffer outBuffer; - ARect inOutDirtyBounds; - mOutputSurface = mSurfaceControl->getSurface(); - mOutputSurface->lock(&outBuffer, &inOutDirtyBounds); - uint8_t* bytePointer = reinterpret_cast<uint8_t*>(outBuffer.bits); - for (int y = 0; y < outBuffer.height; ++y) { - int rowOffset = y * outBuffer.stride; // pixels - for (int x = 0; x < outBuffer.width; ++x) { - int colOffset = (rowOffset + x) * PIXEL_SIZE; // bytes - for (int c = 0; c < PIXEL_SIZE; ++c) { - int offset = colOffset + c; - bytePointer[offset] = ((c + 1) * 56) - 1; - } - } - } - mOutputSurface->unlockAndPost(); - } - - void copyToDebugSurface() { - if (!mOutputSurface.get()) return; - - size_t bufferSize = mLockedBuffer.height * mLockedBuffer.stride * - PIXEL_SIZE; - - ANativeWindow_Buffer outBuffer; - ARect outBufferBounds; - mOutputSurface->lock(&outBuffer, &outBufferBounds); - ASSERT_EQ(mLockedBuffer.width, static_cast<uint32_t>(outBuffer.width)); - ASSERT_EQ(mLockedBuffer.height, static_cast<uint32_t>(outBuffer.height)); - ASSERT_EQ(mLockedBuffer.stride, static_cast<uint32_t>(outBuffer.stride)); - - if (mLockedBuffer.format == outBuffer.format) { - memcpy(outBuffer.bits, mLockedBuffer.data, bufferSize); - } else { - ASSERT_EQ(mLockedBuffer.format, PIXEL_FORMAT_RGBA_8888); - ASSERT_EQ(mLockedBuffer.dataSpace, HAL_DATASPACE_SRGB); - ASSERT_EQ(outBuffer.format, PIXEL_FORMAT_RGBA_8888); - uint8_t* outPointer = reinterpret_cast<uint8_t*>(outBuffer.bits); - for (int y = 0; y < outBuffer.height; ++y) { - int rowOffset = y * outBuffer.stride; // pixels - for (int x = 0; x < outBuffer.width; ++x) { - int colOffset = (rowOffset + x) * PIXEL_SIZE; // bytes - - // RGB are converted - for (int c = 0; c < (PIXEL_SIZE - 1); ++c) { - outPointer[colOffset + c] = srgbToLinear( - mLockedBuffer.data[colOffset + c]); - } - - // Alpha isn't converted - outPointer[colOffset + 3] = - mLockedBuffer.data[colOffset + 3]; - } - } - } - mOutputSurface->unlockAndPost(); - - int sleepSeconds = atoi(getenv(SHOW_DEBUG_STRING)); - sleep(sleepSeconds); - } -}; - -const char SRGBTest::SHOW_DEBUG_STRING[] = "DEBUG_OUTPUT_SECONDS"; - -TEST_F(SRGBTest, GLRenderFromSRGBTexture) { - ASSERT_NO_FATAL_FAILURE(initShaders()); - - // The RGB texture is displayed in the top half - ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, DISPLAY_HEIGHT / 2, - DISPLAY_WIDTH, DISPLAY_HEIGHT / 2)); - - // The SRGB texture is displayed in the bottom half - ASSERT_NO_FATAL_FAILURE(drawTexture(true, 0, 0, - DISPLAY_WIDTH, DISPLAY_HEIGHT / 2)); - - eglSwapBuffers(mEglDisplay, mEglSurface); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // Lock - ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer)); - ASSERT_NO_FATAL_FAILURE( - checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_UNKNOWN)); - - // Compare a pixel in the middle of each texture - int midSRGBOffset = (DISPLAY_HEIGHT / 4) * mLockedBuffer.stride * - PIXEL_SIZE; - int midRGBOffset = midSRGBOffset * 3; - midRGBOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE; - midSRGBOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE; - for (int c = 0; c < PIXEL_SIZE; ++c) { - int expectedValue = mLockedBuffer.data[midRGBOffset + c]; - int actualValue = mLockedBuffer.data[midSRGBOffset + c]; - ASSERT_PRED2(withinTolerance, expectedValue, actualValue); - } - - // mLockedBuffer is unlocked in TearDown so we can copy data from it to - // the debug surface if necessary -} - -// XXX: Disabled since we don't currently expect this to work -TEST_F(SRGBTest, DISABLED_RenderToSRGBSurface) { - ASSERT_NO_FATAL_FAILURE(initShaders()); - - // By default, the first buffer we write into will be RGB - - // Render an RGB texture across the whole surface - ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, 0, - DISPLAY_WIDTH, DISPLAY_HEIGHT)); - eglSwapBuffers(mEglDisplay, mEglSurface); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // Lock - ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer)); - ASSERT_NO_FATAL_FAILURE( - checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_UNKNOWN)); - - // Save the values of the middle pixel for later comparison against SRGB - uint8_t values[PIXEL_SIZE] = {}; - int middleOffset = (DISPLAY_HEIGHT / 2) * mLockedBuffer.stride * - PIXEL_SIZE; - middleOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE; - for (int c = 0; c < PIXEL_SIZE; ++c) { - values[c] = mLockedBuffer.data[middleOffset + c]; - } - - // Unlock - ASSERT_EQ(NO_ERROR, mCpuConsumer->unlockBuffer(mLockedBuffer)); - - // Switch to SRGB window surface - static const int srgbAttribs[] = { - EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, - EGL_NONE, - }; - - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - EXPECT_TRUE(eglDestroySurface(mEglDisplay, mEglSurface)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, - mInputSurface.get(), srgbAttribs); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_SURFACE, mEglSurface); - - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // Render the texture again - ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, 0, - DISPLAY_WIDTH, DISPLAY_HEIGHT)); - eglSwapBuffers(mEglDisplay, mEglSurface); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // Lock - ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer)); - - // Make sure we actually got the SRGB buffer on the consumer side - ASSERT_NO_FATAL_FAILURE( - checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_SRGB)); - - // Verify that the stored value is the same, accounting for RGB/SRGB - for (int c = 0; c < PIXEL_SIZE; ++c) { - // The alpha value should be equivalent before linear->SRGB - float rgbAsSRGB = (c == 3) ? values[c] / 255.0f : - linearToSRGB(values[c] / 255.0f); - int expectedValue = rgbAsSRGB * 255.0f + 0.5f; - int actualValue = mLockedBuffer.data[middleOffset + c]; - ASSERT_PRED2(withinTolerance, expectedValue, actualValue); - } - - // mLockedBuffer is unlocked in TearDown so we can copy data from it to - // the debug surface if necessary -} - -} // namespace android diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp index 498492e179..80e30da84a 100644 --- a/libs/gui/tests/StreamSplitter_test.cpp +++ b/libs/gui/tests/StreamSplitter_test.cpp @@ -81,7 +81,7 @@ TEST_F(StreamSplitterTest, OneInputOneOutput) { sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN)); + GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer)); uint32_t* dataIn; @@ -115,7 +115,7 @@ TEST_F(StreamSplitterTest, OneInputOneOutput) { // received the buffer back from the output BufferQueue ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN)); + GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); } TEST_F(StreamSplitterTest, OneInputMultipleOutputs) { @@ -153,7 +153,7 @@ TEST_F(StreamSplitterTest, OneInputMultipleOutputs) { sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN)); + GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer)); uint32_t* dataIn; @@ -190,7 +190,7 @@ TEST_F(StreamSplitterTest, OneInputMultipleOutputs) { // received the buffer back from the output BufferQueues ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN)); + GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); } TEST_F(StreamSplitterTest, OutputAbandonment) { @@ -217,7 +217,7 @@ TEST_F(StreamSplitterTest, OutputAbandonment) { sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN)); + GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer)); // Abandon the output @@ -230,7 +230,7 @@ TEST_F(StreamSplitterTest, OutputAbandonment) { // Input should be abandoned ASSERT_EQ(NO_INIT, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN)); + GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); } } // namespace android diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index b10d4ebde7..bd598e419e 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -23,6 +23,7 @@ #include <gtest/gtest.h> #include <gui/GLConsumer.h> #include <gui/Surface.h> +#include <gui/BufferQueue.h> #include <system/graphics.h> #include <utils/Log.h> #include <utils/Thread.h> diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp index 0606839506..0134273a07 100644 --- a/libs/gui/tests/SurfaceTextureFBO_test.cpp +++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp @@ -41,7 +41,7 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { &anb)); ASSERT_TRUE(anb != NULL); - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); // Fill the buffer with green uint8_t* img = NULL; @@ -65,7 +65,7 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { &anb)); ASSERT_TRUE(anb != NULL); - buf = new GraphicBuffer(anb, false); + buf = GraphicBuffer::from(anb); // Fill the buffer with red ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp index 308bd7d69c..c6745d034d 100644 --- a/libs/gui/tests/SurfaceTextureGL_test.cpp +++ b/libs/gui/tests/SurfaceTextureGL_test.cpp @@ -42,7 +42,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { &anb)); ASSERT_TRUE(anb != NULL); - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); // Fill the buffer with the a checkerboard pattern uint8_t* img = NULL; @@ -92,7 +92,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferPow2) { &anb)); ASSERT_TRUE(anb != NULL); - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); // Fill the buffer with the a checkerboard pattern uint8_t* img = NULL; @@ -157,7 +157,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { &anb)); ASSERT_TRUE(anb != NULL); - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); uint8_t* img = NULL; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); @@ -238,7 +238,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { return false; } - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); const int yuvTexOffsetY = 0; int stride = buf->getStride(); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 0de60c983f..e18af17bde 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -18,19 +18,38 @@ #include <gtest/gtest.h> -#include <binder/IMemory.h> +#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> +#include <binder/ProcessState.h> +#include <configstore/Utils.h> +#include <cutils/properties.h> +#include <gui/BufferItemConsumer.h> +#include <gui/IDisplayEventConnection.h> +#include <gui/IProducerListener.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> -#include <gui/BufferItemConsumer.h> +#include <private/gui/ComposerService.h> #include <ui/Rect.h> #include <utils/String8.h> -#include <private/gui/ComposerService.h> -#include <binder/ProcessState.h> +#include <limits> +#include <thread> namespace android { +using namespace std::chrono_literals; +// retrieve wide-color and hdr settings from configstore +using namespace android::hardware::configstore; +using namespace android::hardware::configstore::V1_0; + +static bool hasWideColorDisplay = + getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false); + +class FakeSurfaceComposer; +class FakeProducerFrameEventHistory; + +static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max(); + class SurfaceTest : public ::testing::Test { protected: @@ -42,6 +61,8 @@ protected: mComposerClient = new SurfaceComposerClient; ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + // TODO(brianderson): The following sometimes fails and is a source of + // test flakiness. mSurfaceControl = mComposerClient->createSurface( String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, 0); @@ -77,6 +98,8 @@ TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenVisible) { TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenPurgatorized) { mSurfaceControl.clear(); + // Wait for the async clean-up to complete. + std::this_thread::sleep_for(50ms); sp<ANativeWindow> anw(mSurface); int result = -123; @@ -96,7 +119,8 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { BufferQueue::createBufferQueue(&producer, &consumer); sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1); sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + sp<IBinder> display(sf->getBuiltInDisplay( + ISurfaceComposer::eDisplayIdMain)); ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer, Rect(), 64, 64, 0, 0x7fffffff, false)); @@ -140,6 +164,14 @@ TEST_F(SurfaceTest, ConcreteTypeIsSurface) { EXPECT_EQ(NATIVE_WINDOW_SURFACE, result); } +TEST_F(SurfaceTest, LayerCountIsOne) { + sp<ANativeWindow> anw(mSurface); + int result = -123; + int err = anw->query(anw.get(), NATIVE_WINDOW_LAYER_COUNT, &result); + EXPECT_EQ(NO_ERROR, err); + EXPECT_EQ(1, result); +} + TEST_F(SurfaceTest, QueryConsumerUsage) { const int TEST_USAGE_FLAGS = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER; @@ -232,6 +264,37 @@ TEST_F(SurfaceTest, GetConsumerName) { EXPECT_STREQ("TestConsumer", surface->getConsumerName().string()); } +TEST_F(SurfaceTest, GetWideColorSupport) { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<DummyConsumer> dummyConsumer(new DummyConsumer); + consumer->consumerConnect(dummyConsumer, false); + consumer->setConsumerName(String8("TestConsumer")); + + sp<Surface> surface = new Surface(producer); + sp<ANativeWindow> window(surface); + native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU); + + bool supported; + surface->getWideColorSupport(&supported); + + // NOTE: This test assumes that device that supports + // wide-color (as indicated by BoardConfig) must also + // have a wide-color primary display. + // That assumption allows this test to cover devices + // that advertised a wide-color color mode without + // actually supporting wide-color to pass this test + // as well as the case of a device that does support + // wide-color (via BoardConfig) and has a wide-color + // primary display. + // NOT covered at this time is a device that supports + // wide color in the BoardConfig but does not support + // a wide-color color mode on the primary display. + ASSERT_EQ(hasWideColorDisplay, supported); +} + TEST_F(SurfaceTest, DynamicSetBufferCount) { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; @@ -258,4 +321,1268 @@ TEST_F(SurfaceTest, DynamicSetBufferCount) { ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence)); } +TEST_F(SurfaceTest, GetAndFlushRemovedBuffers) { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<DummyConsumer> dummyConsumer(new DummyConsumer); + consumer->consumerConnect(dummyConsumer, false); + consumer->setConsumerName(String8("TestConsumer")); + + sp<Surface> surface = new Surface(producer); + sp<ANativeWindow> window(surface); + sp<DummyProducerListener> listener = new DummyProducerListener(); + ASSERT_EQ(OK, surface->connect( + NATIVE_WINDOW_API_CPU, + /*listener*/listener, + /*reportBufferRemoval*/true)); + const int BUFFER_COUNT = 4; + ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); + + sp<GraphicBuffer> detachedBuffer; + sp<Fence> outFence; + int fences[BUFFER_COUNT]; + ANativeWindowBuffer* buffers[BUFFER_COUNT]; + // Allocate buffers because detachNextBuffer requires allocated buffers + for (int i = 0; i < BUFFER_COUNT; i++) { + ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[i], &fences[i])); + } + for (int i = 0; i < BUFFER_COUNT; i++) { + ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], fences[i])); + } + + // Test detached buffer is correctly reported + ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence)); + std::vector<sp<GraphicBuffer>> removedBuffers; + ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers)); + ASSERT_EQ(1u, removedBuffers.size()); + ASSERT_EQ(detachedBuffer->handle, removedBuffers.at(0)->handle); + // Test the list is flushed one getAndFlushRemovedBuffers returns + ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers)); + ASSERT_EQ(0u, removedBuffers.size()); + + + // Test removed buffer list is cleanup after next dequeueBuffer call + ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence)); + ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[0], &fences[0])); + ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers)); + ASSERT_EQ(0u, removedBuffers.size()); + ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[0], fences[0])); + + // Test removed buffer list is cleanup after next detachNextBuffer call + ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence)); + ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence)); + ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers)); + ASSERT_EQ(1u, removedBuffers.size()); + ASSERT_EQ(detachedBuffer->handle, removedBuffers.at(0)->handle); + + // Re-allocate buffers since all buffers are detached up to now + for (int i = 0; i < BUFFER_COUNT; i++) { + ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[i], &fences[i])); + } + for (int i = 0; i < BUFFER_COUNT; i++) { + ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], fences[i])); + } + + ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence)); + ASSERT_EQ(NO_ERROR, surface->attachBuffer(detachedBuffer.get())); + ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers)); + // Depends on which slot GraphicBufferProducer impl pick, the attach call might + // get 0 or 1 buffer removed. + ASSERT_LE(removedBuffers.size(), 1u); +} + +TEST_F(SurfaceTest, TestGetLastDequeueStartTime) { + sp<ANativeWindow> anw(mSurface); + ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU)); + + ANativeWindowBuffer* buffer = nullptr; + int32_t fenceFd = -1; + + nsecs_t before = systemTime(CLOCK_MONOTONIC); + anw->dequeueBuffer(anw.get(), &buffer, &fenceFd); + nsecs_t after = systemTime(CLOCK_MONOTONIC); + + nsecs_t lastDequeueTime = mSurface->getLastDequeueStartTime(); + ASSERT_LE(before, lastDequeueTime); + ASSERT_GE(after, lastDequeueTime); } + +class FakeConsumer : public BnConsumerListener { +public: + void onFrameAvailable(const BufferItem& /*item*/) override {} + void onBuffersReleased() override {} + void onSidebandStreamChanged() override {} + + void addAndGetFrameTimestamps( + const NewFrameEventsEntry* newTimestamps, + FrameEventHistoryDelta* outDelta) override { + if (newTimestamps) { + if (mGetFrameTimestampsEnabled) { + EXPECT_GT(mNewFrameEntryOverride.frameNumber, 0u) << + "Test should set mNewFrameEntryOverride before queuing " + "a frame."; + EXPECT_EQ(newTimestamps->frameNumber, + mNewFrameEntryOverride.frameNumber) << + "Test attempting to add NewFrameEntryOverride with " + "incorrect frame number."; + mFrameEventHistory.addQueue(mNewFrameEntryOverride); + mNewFrameEntryOverride.frameNumber = 0; + } + mAddFrameTimestampsCount++; + mLastAddedFrameNumber = newTimestamps->frameNumber; + } + if (outDelta) { + mFrameEventHistory.getAndResetDelta(outDelta); + mGetFrameTimestampsCount++; + } + mAddAndGetFrameTimestampsCallCount++; + } + + bool mGetFrameTimestampsEnabled = false; + + ConsumerFrameEventHistory mFrameEventHistory; + int mAddAndGetFrameTimestampsCallCount = 0; + int mAddFrameTimestampsCount = 0; + int mGetFrameTimestampsCount = 0; + uint64_t mLastAddedFrameNumber = NO_FRAME_INDEX; + + NewFrameEventsEntry mNewFrameEntryOverride = { 0, 0, 0, nullptr }; +}; + + +class FakeSurfaceComposer : public ISurfaceComposer{ +public: + ~FakeSurfaceComposer() override {} + + void setSupportsPresent(bool supportsPresent) { + mSupportsPresent = supportsPresent; + } + + sp<ISurfaceComposerClient> createConnection() override { return nullptr; } + sp<ISurfaceComposerClient> createScopedConnection( + const sp<IGraphicBufferProducer>& /* parent */) override { + return nullptr; + } + sp<IDisplayEventConnection> createDisplayEventConnection(ISurfaceComposer::VsyncSource) + override { + return nullptr; + } + sp<IBinder> createDisplay(const String8& /*displayName*/, + bool /*secure*/) override { return nullptr; } + void destroyDisplay(const sp<IBinder>& /*display */) override {} + sp<IBinder> getBuiltInDisplay(int32_t /*id*/) override { return nullptr; } + void setTransactionState(const Vector<ComposerState>& /*state*/, + const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/) + override {} + void bootFinished() override {} + bool authenticateSurfaceTexture( + const sp<IGraphicBufferProducer>& /*surface*/) const override { + return false; + } + + status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) + const override { + *outSupported = { + FrameEvent::REQUESTED_PRESENT, + FrameEvent::ACQUIRE, + FrameEvent::LATCH, + FrameEvent::FIRST_REFRESH_START, + FrameEvent::LAST_REFRESH_START, + FrameEvent::GPU_COMPOSITION_DONE, + FrameEvent::DEQUEUE_READY, + FrameEvent::RELEASE + }; + if (mSupportsPresent) { + outSupported->push_back( + FrameEvent::DISPLAY_PRESENT); + } + return NO_ERROR; + } + + void setPowerMode(const sp<IBinder>& /*display*/, int /*mode*/) override {} + status_t getDisplayConfigs(const sp<IBinder>& /*display*/, + Vector<DisplayInfo>* /*configs*/) override { return NO_ERROR; } + status_t getDisplayStats(const sp<IBinder>& /*display*/, + DisplayStatInfo* /*stats*/) override { return NO_ERROR; } + int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; } + status_t setActiveConfig(const sp<IBinder>& /*display*/, int /*id*/) + override { + return NO_ERROR; + } + status_t getDisplayColorModes(const sp<IBinder>& /*display*/, + Vector<android_color_mode_t>* /*outColorModes*/) override { + return NO_ERROR; + } + android_color_mode_t getActiveColorMode(const sp<IBinder>& /*display*/) + override { + return HAL_COLOR_MODE_NATIVE; + } + status_t setActiveColorMode(const sp<IBinder>& /*display*/, + android_color_mode_t /*colorMode*/) override { return NO_ERROR; } + status_t captureScreen(const sp<IBinder>& /*display*/, + const sp<IGraphicBufferProducer>& /*producer*/, + Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/, + int32_t /*minLayerZ*/, int32_t /*maxLayerZ*/, + bool /*useIdentityTransform*/, + Rotation /*rotation*/) override { return NO_ERROR; } + status_t clearAnimationFrameStats() override { return NO_ERROR; } + status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override { + return NO_ERROR; + } + status_t getHdrCapabilities(const sp<IBinder>& /*display*/, + HdrCapabilities* /*outCapabilities*/) const override { + return NO_ERROR; + } + status_t enableVSyncInjections(bool /*enable*/) override { + return NO_ERROR; + } + status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; } + +protected: + IBinder* onAsBinder() override { return nullptr; } + +private: + bool mSupportsPresent{true}; +}; + +class FakeProducerFrameEventHistory : public ProducerFrameEventHistory { +public: + FakeProducerFrameEventHistory(FenceToFenceTimeMap* fenceMap) + : mFenceMap(fenceMap) {} + + ~FakeProducerFrameEventHistory() {} + + void updateAcquireFence(uint64_t frameNumber, + std::shared_ptr<FenceTime>&& acquire) override { + // Verify the acquire fence being added isn't the one from the consumer. + EXPECT_NE(mConsumerAcquireFence, acquire); + // Override the fence, so we can verify this was called by the + // producer after the frame is queued. + ProducerFrameEventHistory::updateAcquireFence(frameNumber, + std::shared_ptr<FenceTime>(mAcquireFenceOverride)); + } + + void setAcquireFenceOverride( + const std::shared_ptr<FenceTime>& acquireFenceOverride, + const std::shared_ptr<FenceTime>& consumerAcquireFence) { + mAcquireFenceOverride = acquireFenceOverride; + mConsumerAcquireFence = consumerAcquireFence; + } + +protected: + std::shared_ptr<FenceTime> createFenceTime(const sp<Fence>& fence) + const override { + return mFenceMap->createFenceTimeForTest(fence); + } + + FenceToFenceTimeMap* mFenceMap{nullptr}; + + std::shared_ptr<FenceTime> mAcquireFenceOverride{FenceTime::NO_FENCE}; + std::shared_ptr<FenceTime> mConsumerAcquireFence{FenceTime::NO_FENCE}; +}; + + +class TestSurface : public Surface { +public: + TestSurface(const sp<IGraphicBufferProducer>& bufferProducer, + FenceToFenceTimeMap* fenceMap) + : Surface(bufferProducer), + mFakeSurfaceComposer(new FakeSurfaceComposer) { + mFakeFrameEventHistory = new FakeProducerFrameEventHistory(fenceMap); + mFrameEventHistory.reset(mFakeFrameEventHistory); + } + + ~TestSurface() override {} + + sp<ISurfaceComposer> composerService() const override { + return mFakeSurfaceComposer; + } + + nsecs_t now() const override { + return mNow; + } + + void setNow(nsecs_t now) { + mNow = now; + } + +public: + sp<FakeSurfaceComposer> mFakeSurfaceComposer; + nsecs_t mNow = 0; + + // mFrameEventHistory owns the instance of FakeProducerFrameEventHistory, + // but this raw pointer gives access to test functionality. + FakeProducerFrameEventHistory* mFakeFrameEventHistory; +}; + + +class GetFrameTimestampsTest : public ::testing::Test { +protected: + struct FenceAndFenceTime { + explicit FenceAndFenceTime(FenceToFenceTimeMap& fenceMap) + : mFence(new Fence), + mFenceTime(fenceMap.createFenceTimeForTest(mFence)) {} + sp<Fence> mFence { nullptr }; + std::shared_ptr<FenceTime> mFenceTime { nullptr }; + }; + + struct RefreshEvents { + RefreshEvents(FenceToFenceTimeMap& fenceMap, nsecs_t refreshStart) + : mFenceMap(fenceMap), + kCompositorTiming( + {refreshStart, refreshStart + 1, refreshStart + 2 }), + kStartTime(refreshStart + 3), + kGpuCompositionDoneTime(refreshStart + 4), + kPresentTime(refreshStart + 5) {} + + void signalPostCompositeFences() { + mFenceMap.signalAllForTest( + mGpuCompositionDone.mFence, kGpuCompositionDoneTime); + mFenceMap.signalAllForTest(mPresent.mFence, kPresentTime); + } + + FenceToFenceTimeMap& mFenceMap; + + FenceAndFenceTime mGpuCompositionDone { mFenceMap }; + FenceAndFenceTime mPresent { mFenceMap }; + + const CompositorTiming kCompositorTiming; + + const nsecs_t kStartTime; + const nsecs_t kGpuCompositionDoneTime; + const nsecs_t kPresentTime; + }; + + struct FrameEvents { + FrameEvents(FenceToFenceTimeMap& fenceMap, nsecs_t frameStartTime) + : mFenceMap(fenceMap), + kPostedTime(frameStartTime + 100), + kRequestedPresentTime(frameStartTime + 200), + kProducerAcquireTime(frameStartTime + 300), + kConsumerAcquireTime(frameStartTime + 301), + kLatchTime(frameStartTime + 500), + kDequeueReadyTime(frameStartTime + 600), + kReleaseTime(frameStartTime + 700), + mRefreshes { + { mFenceMap, frameStartTime + 410 }, + { mFenceMap, frameStartTime + 420 }, + { mFenceMap, frameStartTime + 430 } } {} + + void signalQueueFences() { + mFenceMap.signalAllForTest( + mAcquireConsumer.mFence, kConsumerAcquireTime); + mFenceMap.signalAllForTest( + mAcquireProducer.mFence, kProducerAcquireTime); + } + + void signalRefreshFences() { + for (auto& re : mRefreshes) { + re.signalPostCompositeFences(); + } + } + + void signalReleaseFences() { + mFenceMap.signalAllForTest(mRelease.mFence, kReleaseTime); + } + + FenceToFenceTimeMap& mFenceMap; + + FenceAndFenceTime mAcquireConsumer { mFenceMap }; + FenceAndFenceTime mAcquireProducer { mFenceMap }; + FenceAndFenceTime mRelease { mFenceMap }; + + const nsecs_t kPostedTime; + const nsecs_t kRequestedPresentTime; + const nsecs_t kProducerAcquireTime; + const nsecs_t kConsumerAcquireTime; + const nsecs_t kLatchTime; + const nsecs_t kDequeueReadyTime; + const nsecs_t kReleaseTime; + + RefreshEvents mRefreshes[3]; + }; + + GetFrameTimestampsTest() {} + + virtual void SetUp() { + BufferQueue::createBufferQueue(&mProducer, &mConsumer); + mFakeConsumer = new FakeConsumer; + mCfeh = &mFakeConsumer->mFrameEventHistory; + mConsumer->consumerConnect(mFakeConsumer, false); + mConsumer->setConsumerName(String8("TestConsumer")); + mSurface = new TestSurface(mProducer, &mFenceMap); + mWindow = mSurface; + + ASSERT_EQ(NO_ERROR, native_window_api_connect(mWindow.get(), + NATIVE_WINDOW_API_CPU)); + native_window_set_buffer_count(mWindow.get(), 4); + } + + void disableFrameTimestamps() { + mFakeConsumer->mGetFrameTimestampsEnabled = false; + native_window_enable_frame_timestamps(mWindow.get(), 0); + mFrameTimestampsEnabled = false; + } + + void enableFrameTimestamps() { + mFakeConsumer->mGetFrameTimestampsEnabled = true; + native_window_enable_frame_timestamps(mWindow.get(), 1); + mFrameTimestampsEnabled = true; + } + + int getAllFrameTimestamps(uint64_t frameId) { + return native_window_get_frame_timestamps(mWindow.get(), frameId, + &outRequestedPresentTime, &outAcquireTime, &outLatchTime, + &outFirstRefreshStartTime, &outLastRefreshStartTime, + &outGpuCompositionDoneTime, &outDisplayPresentTime, + &outDequeueReadyTime, &outReleaseTime); + } + + void resetTimestamps() { + outRequestedPresentTime = -1; + outAcquireTime = -1; + outLatchTime = -1; + outFirstRefreshStartTime = -1; + outLastRefreshStartTime = -1; + outGpuCompositionDoneTime = -1; + outDisplayPresentTime = -1; + outDequeueReadyTime = -1; + outReleaseTime = -1; + } + + uint64_t getNextFrameId() { + uint64_t frameId = -1; + int status = native_window_get_next_frame_id(mWindow.get(), &frameId); + EXPECT_EQ(status, NO_ERROR); + return frameId; + } + + void dequeueAndQueue(uint64_t frameIndex) { + int fence = -1; + ANativeWindowBuffer* buffer = nullptr; + ASSERT_EQ(NO_ERROR, + mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence)); + + int oldAddFrameTimestampsCount = + mFakeConsumer->mAddFrameTimestampsCount; + + FrameEvents* frame = &mFrames[frameIndex]; + uint64_t frameNumber = frameIndex + 1; + + NewFrameEventsEntry fe; + fe.frameNumber = frameNumber; + fe.postedTime = frame->kPostedTime; + fe.requestedPresentTime = frame->kRequestedPresentTime; + fe.acquireFence = frame->mAcquireConsumer.mFenceTime; + mFakeConsumer->mNewFrameEntryOverride = fe; + + mSurface->mFakeFrameEventHistory->setAcquireFenceOverride( + frame->mAcquireProducer.mFenceTime, + frame->mAcquireConsumer.mFenceTime); + + ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence)); + + EXPECT_EQ(frameNumber, mFakeConsumer->mLastAddedFrameNumber); + + EXPECT_EQ( + oldAddFrameTimestampsCount + (mFrameTimestampsEnabled ? 1 : 0), + mFakeConsumer->mAddFrameTimestampsCount); + } + + void addFrameEvents( + bool gpuComposited, uint64_t iOldFrame, int64_t iNewFrame) { + FrameEvents* oldFrame = + (iOldFrame == NO_FRAME_INDEX) ? nullptr : &mFrames[iOldFrame]; + FrameEvents* newFrame = &mFrames[iNewFrame]; + + uint64_t nOldFrame = iOldFrame + 1; + uint64_t nNewFrame = iNewFrame + 1; + + // Latch, Composite, and Release the frames in a plausible order. + // Note: The timestamps won't necessarily match the order, but + // that's okay for the purposes of this test. + std::shared_ptr<FenceTime> gpuDoneFenceTime = FenceTime::NO_FENCE; + + // Composite the previous frame one more time, which helps verify + // LastRefresh is updated properly. + if (oldFrame != nullptr) { + mCfeh->addPreComposition(nOldFrame, + oldFrame->mRefreshes[2].kStartTime); + gpuDoneFenceTime = gpuComposited ? + oldFrame->mRefreshes[2].mGpuCompositionDone.mFenceTime : + FenceTime::NO_FENCE; + mCfeh->addPostComposition(nOldFrame, gpuDoneFenceTime, + oldFrame->mRefreshes[2].mPresent.mFenceTime, + oldFrame->mRefreshes[2].kCompositorTiming); + } + + // Latch the new frame. + mCfeh->addLatch(nNewFrame, newFrame->kLatchTime); + + mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[0].kStartTime); + gpuDoneFenceTime = gpuComposited ? + newFrame->mRefreshes[0].mGpuCompositionDone.mFenceTime : + FenceTime::NO_FENCE; + // HWC2 releases the previous buffer after a new latch just before + // calling postComposition. + if (oldFrame != nullptr) { + mCfeh->addRelease(nOldFrame, oldFrame->kDequeueReadyTime, + std::shared_ptr<FenceTime>(oldFrame->mRelease.mFenceTime)); + } + mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime, + newFrame->mRefreshes[0].mPresent.mFenceTime, + newFrame->mRefreshes[0].kCompositorTiming); + + mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[1].kStartTime); + gpuDoneFenceTime = gpuComposited ? + newFrame->mRefreshes[1].mGpuCompositionDone.mFenceTime : + FenceTime::NO_FENCE; + mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime, + newFrame->mRefreshes[1].mPresent.mFenceTime, + newFrame->mRefreshes[1].kCompositorTiming); + } + + sp<IGraphicBufferProducer> mProducer; + sp<IGraphicBufferConsumer> mConsumer; + sp<FakeConsumer> mFakeConsumer; + ConsumerFrameEventHistory* mCfeh; + sp<TestSurface> mSurface; + sp<ANativeWindow> mWindow; + + FenceToFenceTimeMap mFenceMap; + + bool mFrameTimestampsEnabled = false; + + int64_t outRequestedPresentTime = -1; + int64_t outAcquireTime = -1; + int64_t outLatchTime = -1; + int64_t outFirstRefreshStartTime = -1; + int64_t outLastRefreshStartTime = -1; + int64_t outGpuCompositionDoneTime = -1; + int64_t outDisplayPresentTime = -1; + int64_t outDequeueReadyTime = -1; + int64_t outReleaseTime = -1; + + FrameEvents mFrames[3] { + { mFenceMap, 1000 }, { mFenceMap, 2000 }, { mFenceMap, 3000 } }; +}; + + +// This test verifies that the frame timestamps are not retrieved when not +// explicitly enabled via native_window_enable_frame_timestamps. +// We want to check this to make sure there's no overhead for users +// that don't need the timestamp information. +TEST_F(GetFrameTimestampsTest, DefaultDisabled) { + int fence; + ANativeWindowBuffer* buffer; + + EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); + EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); + + const uint64_t fId = getNextFrameId(); + + // Verify the producer doesn't get frame timestamps piggybacked on dequeue. + ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence)); + EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); + EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); + + // Verify the producer doesn't get frame timestamps piggybacked on queue. + // It is okay that frame timestamps are added in the consumer since it is + // still needed for SurfaceFlinger dumps. + ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence)); + EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount); + EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); + + // Verify attempts to get frame timestamps fail. + int result = getAllFrameTimestamps(fId); + EXPECT_EQ(INVALID_OPERATION, result); + EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); + + // Verify compositor timing query fails. + nsecs_t compositeDeadline = 0; + nsecs_t compositeInterval = 0; + nsecs_t compositeToPresentLatency = 0; + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(INVALID_OPERATION, result); +} + +// This test verifies that the frame timestamps are retrieved if explicitly +// enabled via native_window_enable_frame_timestamps. +TEST_F(GetFrameTimestampsTest, EnabledSimple) { + CompositorTiming initialCompositorTiming { + 1000000000, // 1s deadline + 16666667, // 16ms interval + 50000000, // 50ms present latency + }; + mCfeh->initializeCompositorTiming(initialCompositorTiming); + + enableFrameTimestamps(); + + // Verify the compositor timing query gets the initial compositor values + // after timststamps are enabled; even before the first frame is queued + // or dequeued. + nsecs_t compositeDeadline = 0; + nsecs_t compositeInterval = 0; + nsecs_t compositeToPresentLatency = 0; + mSurface->setNow(initialCompositorTiming.deadline - 1); + int result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); + EXPECT_EQ(initialCompositorTiming.interval, compositeInterval); + EXPECT_EQ(initialCompositorTiming.presentLatency, + compositeToPresentLatency); + + int fence; + ANativeWindowBuffer* buffer; + + EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); + EXPECT_EQ(1, mFakeConsumer->mGetFrameTimestampsCount); + + const uint64_t fId1 = getNextFrameId(); + + // Verify getFrameTimestamps is piggybacked on dequeue. + ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence)); + EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); + EXPECT_EQ(2, mFakeConsumer->mGetFrameTimestampsCount); + + NewFrameEventsEntry f1; + f1.frameNumber = 1; + f1.postedTime = mFrames[0].kPostedTime; + f1.requestedPresentTime = mFrames[0].kRequestedPresentTime; + f1.acquireFence = mFrames[0].mAcquireConsumer.mFenceTime; + mSurface->mFakeFrameEventHistory->setAcquireFenceOverride( + mFrames[0].mAcquireProducer.mFenceTime, + mFrames[0].mAcquireConsumer.mFenceTime); + mFakeConsumer->mNewFrameEntryOverride = f1; + mFrames[0].signalQueueFences(); + + // Verify getFrameTimestamps is piggybacked on queue. + ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence)); + EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount); + EXPECT_EQ(1u, mFakeConsumer->mLastAddedFrameNumber); + EXPECT_EQ(3, mFakeConsumer->mGetFrameTimestampsCount); + + // Verify queries for timestamps that the producer doesn't know about + // triggers a call to see if the consumer has any new timestamps. + result = getAllFrameTimestamps(fId1); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(4, mFakeConsumer->mGetFrameTimestampsCount); +} + +TEST_F(GetFrameTimestampsTest, QueryPresentSupported) { + bool displayPresentSupported = true; + mSurface->mFakeSurfaceComposer->setSupportsPresent(displayPresentSupported); + + // Verify supported bits are forwarded. + int supportsPresent = -1; + mWindow.get()->query(mWindow.get(), + NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent); + EXPECT_EQ(displayPresentSupported, supportsPresent); +} + +TEST_F(GetFrameTimestampsTest, QueryPresentNotSupported) { + bool displayPresentSupported = false; + mSurface->mFakeSurfaceComposer->setSupportsPresent(displayPresentSupported); + + // Verify supported bits are forwarded. + int supportsPresent = -1; + mWindow.get()->query(mWindow.get(), + NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent); + EXPECT_EQ(displayPresentSupported, supportsPresent); +} + +TEST_F(GetFrameTimestampsTest, SnapToNextTickBasic) { + nsecs_t phase = 4000; + nsecs_t interval = 1000; + + // Timestamp in previous interval. + nsecs_t timestamp = 3500; + EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick( + timestamp, phase, interval)); + + // Timestamp in next interval. + timestamp = 4500; + EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick( + timestamp, phase, interval)); + + // Timestamp multiple intervals before. + timestamp = 2500; + EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick( + timestamp, phase, interval)); + + // Timestamp multiple intervals after. + timestamp = 6500; + EXPECT_EQ(7000, ProducerFrameEventHistory::snapToNextTick( + timestamp, phase, interval)); + + // Timestamp on previous interval. + timestamp = 3000; + EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick( + timestamp, phase, interval)); + + // Timestamp on next interval. + timestamp = 5000; + EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick( + timestamp, phase, interval)); + + // Timestamp equal to phase. + timestamp = 4000; + EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick( + timestamp, phase, interval)); +} + +// int(big_timestamp / interval) < 0, which can cause a crash or invalid result +// if the number of intervals elapsed is internally stored in an int. +TEST_F(GetFrameTimestampsTest, SnapToNextTickOverflow) { + nsecs_t phase = 0; + nsecs_t interval = 4000; + nsecs_t big_timestamp = 8635916564000; + int32_t intervals = big_timestamp / interval; + + EXPECT_LT(intervals, 0); + EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick( + big_timestamp, phase, interval)); + EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick( + big_timestamp, big_timestamp, interval)); +} + +// This verifies the compositor timing is updated by refresh events +// and piggy backed on a queue, dequeue, and enabling of timestamps.. +TEST_F(GetFrameTimestampsTest, CompositorTimingUpdatesBasic) { + CompositorTiming initialCompositorTiming { + 1000000000, // 1s deadline + 16666667, // 16ms interval + 50000000, // 50ms present latency + }; + mCfeh->initializeCompositorTiming(initialCompositorTiming); + + enableFrameTimestamps(); + + // We get the initial values before any frames are submitted. + nsecs_t compositeDeadline = 0; + nsecs_t compositeInterval = 0; + nsecs_t compositeToPresentLatency = 0; + mSurface->setNow(initialCompositorTiming.deadline - 1); + int result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); + EXPECT_EQ(initialCompositorTiming.interval, compositeInterval); + EXPECT_EQ(initialCompositorTiming.presentLatency, + compositeToPresentLatency); + + const uint64_t fId1 = getNextFrameId(); + dequeueAndQueue(0); + addFrameEvents(true, NO_FRAME_INDEX, 0); + + // Still get the initial values because the frame events for frame 0 + // didn't get a chance to piggyback on a queue or dequeue yet. + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); + EXPECT_EQ(initialCompositorTiming.interval, compositeInterval); + EXPECT_EQ(initialCompositorTiming.presentLatency, + compositeToPresentLatency); + + const uint64_t fId2 = getNextFrameId(); + dequeueAndQueue(1); + addFrameEvents(true, 0, 1); + + // Now expect the composite values associated with frame 1. + mSurface->setNow(mFrames[0].mRefreshes[1].kCompositorTiming.deadline); + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.deadline, + compositeDeadline); + EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.interval, + compositeInterval); + EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.presentLatency, + compositeToPresentLatency); + + dequeueAndQueue(2); + addFrameEvents(true, 1, 2); + + // Now expect the composite values associated with frame 2. + mSurface->setNow(mFrames[1].mRefreshes[1].kCompositorTiming.deadline); + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.deadline, + compositeDeadline); + EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.interval, + compositeInterval); + EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.presentLatency, + compositeToPresentLatency); + + // Re-enabling frame timestamps should get the latest values. + disableFrameTimestamps(); + enableFrameTimestamps(); + + // Now expect the composite values associated with frame 3. + mSurface->setNow(mFrames[2].mRefreshes[1].kCompositorTiming.deadline); + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.deadline, + compositeDeadline); + EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.interval, + compositeInterval); + EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.presentLatency, + compositeToPresentLatency); +} + +// This verifies the compositor deadline properly snaps to the the next +// deadline based on the current time. +TEST_F(GetFrameTimestampsTest, CompositorTimingDeadlineSnaps) { + CompositorTiming initialCompositorTiming { + 1000000000, // 1s deadline + 16666667, // 16ms interval + 50000000, // 50ms present latency + }; + mCfeh->initializeCompositorTiming(initialCompositorTiming); + + enableFrameTimestamps(); + + nsecs_t compositeDeadline = 0; + nsecs_t compositeInterval = 0; + nsecs_t compositeToPresentLatency = 0; + + // A "now" just before the deadline snaps to the deadline. + mSurface->setNow(initialCompositorTiming.deadline - 1); + int result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); + nsecs_t expectedDeadline = initialCompositorTiming.deadline; + EXPECT_EQ(expectedDeadline, compositeDeadline); + + const uint64_t fId1 = getNextFrameId(); + dequeueAndQueue(0); + addFrameEvents(true, NO_FRAME_INDEX, 0); + + // A "now" just after the deadline snaps properly. + mSurface->setNow(initialCompositorTiming.deadline + 1); + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + expectedDeadline = + initialCompositorTiming.deadline +initialCompositorTiming.interval; + EXPECT_EQ(expectedDeadline, compositeDeadline); + + const uint64_t fId2 = getNextFrameId(); + dequeueAndQueue(1); + addFrameEvents(true, 0, 1); + + // A "now" just after the next interval snaps properly. + mSurface->setNow( + mFrames[0].mRefreshes[1].kCompositorTiming.deadline + + mFrames[0].mRefreshes[1].kCompositorTiming.interval + 1); + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + expectedDeadline = + mFrames[0].mRefreshes[1].kCompositorTiming.deadline + + mFrames[0].mRefreshes[1].kCompositorTiming.interval * 2; + EXPECT_EQ(expectedDeadline, compositeDeadline); + + dequeueAndQueue(2); + addFrameEvents(true, 1, 2); + + // A "now" over 1 interval before the deadline snaps properly. + mSurface->setNow( + mFrames[1].mRefreshes[1].kCompositorTiming.deadline - + mFrames[1].mRefreshes[1].kCompositorTiming.interval - 1); + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + expectedDeadline = + mFrames[1].mRefreshes[1].kCompositorTiming.deadline - + mFrames[1].mRefreshes[1].kCompositorTiming.interval; + EXPECT_EQ(expectedDeadline, compositeDeadline); + + // Re-enabling frame timestamps should get the latest values. + disableFrameTimestamps(); + enableFrameTimestamps(); + + // A "now" over 2 intervals before the deadline snaps properly. + mSurface->setNow( + mFrames[2].mRefreshes[1].kCompositorTiming.deadline - + mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2 - 1); + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + expectedDeadline = + mFrames[2].mRefreshes[1].kCompositorTiming.deadline - + mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2; + EXPECT_EQ(expectedDeadline, compositeDeadline); +} + +// This verifies the timestamps recorded in the consumer's +// FrameTimestampsHistory are properly retrieved by the producer for the +// correct frames. +TEST_F(GetFrameTimestampsTest, TimestampsAssociatedWithCorrectFrame) { + enableFrameTimestamps(); + + const uint64_t fId1 = getNextFrameId(); + dequeueAndQueue(0); + mFrames[0].signalQueueFences(); + + const uint64_t fId2 = getNextFrameId(); + dequeueAndQueue(1); + mFrames[1].signalQueueFences(); + + addFrameEvents(true, NO_FRAME_INDEX, 0); + mFrames[0].signalRefreshFences(); + addFrameEvents(true, 0, 1); + mFrames[0].signalReleaseFences(); + mFrames[1].signalRefreshFences(); + + // Verify timestamps are correct for frame 1. + resetTimestamps(); + int result = getAllFrameTimestamps(fId1); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); + EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime, + outGpuCompositionDoneTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime); + EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); + EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime); + + // Verify timestamps are correct for frame 2. + resetTimestamps(); + result = getAllFrameTimestamps(fId2); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[1].kLatchTime, outLatchTime); + EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime); + EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime); + EXPECT_EQ(mFrames[1].mRefreshes[0].kGpuCompositionDoneTime, + outGpuCompositionDoneTime); + EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDequeueReadyTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime); +} + +// This test verifies the acquire fence recorded by the consumer is not sent +// back to the producer and the producer saves its own fence. +TEST_F(GetFrameTimestampsTest, QueueTimestampsNoSync) { + enableFrameTimestamps(); + + // Dequeue and queue frame 1. + const uint64_t fId1 = getNextFrameId(); + dequeueAndQueue(0); + + // Verify queue-related timestamps for f1 are available immediately in the + // producer without asking the consumer again, even before signaling the + // acquire fence. + resetTimestamps(); + int oldCount = mFakeConsumer->mGetFrameTimestampsCount; + int result = native_window_get_frame_timestamps(mWindow.get(), fId1, + &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outAcquireTime); + + // Signal acquire fences. Verify a sync call still isn't necessary. + mFrames[0].signalQueueFences(); + + oldCount = mFakeConsumer->mGetFrameTimestampsCount; + result = native_window_get_frame_timestamps(mWindow.get(), fId1, + &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + + // Dequeue and queue frame 2. + const uint64_t fId2 = getNextFrameId(); + dequeueAndQueue(1); + + // Verify queue-related timestamps for f2 are available immediately in the + // producer without asking the consumer again, even before signaling the + // acquire fence. + resetTimestamps(); + oldCount = mFakeConsumer->mGetFrameTimestampsCount; + result = native_window_get_frame_timestamps(mWindow.get(), fId2, + &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outAcquireTime); + + // Signal acquire fences. Verify a sync call still isn't necessary. + mFrames[1].signalQueueFences(); + + oldCount = mFakeConsumer->mGetFrameTimestampsCount; + result = native_window_get_frame_timestamps(mWindow.get(), fId2, + &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime); +} + +TEST_F(GetFrameTimestampsTest, ZeroRequestedTimestampsNoSync) { + enableFrameTimestamps(); + + // Dequeue and queue frame 1. + dequeueAndQueue(0); + mFrames[0].signalQueueFences(); + + // Dequeue and queue frame 2. + const uint64_t fId2 = getNextFrameId(); + dequeueAndQueue(1); + mFrames[1].signalQueueFences(); + + addFrameEvents(true, NO_FRAME_INDEX, 0); + mFrames[0].signalRefreshFences(); + addFrameEvents(true, 0, 1); + mFrames[0].signalReleaseFences(); + mFrames[1].signalRefreshFences(); + + // Verify a request for no timestamps doesn't result in a sync call. + int oldCount = mFakeConsumer->mGetFrameTimestampsCount; + int result = native_window_get_frame_timestamps(mWindow.get(), fId2, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); +} + +// This test verifies that fences can signal and update timestamps producer +// side without an additional sync call to the consumer. +TEST_F(GetFrameTimestampsTest, FencesInProducerNoSync) { + enableFrameTimestamps(); + + // Dequeue and queue frame 1. + const uint64_t fId1 = getNextFrameId(); + dequeueAndQueue(0); + mFrames[0].signalQueueFences(); + + // Dequeue and queue frame 2. + dequeueAndQueue(1); + mFrames[1].signalQueueFences(); + + addFrameEvents(true, NO_FRAME_INDEX, 0); + addFrameEvents(true, 0, 1); + + // Verify available timestamps are correct for frame 1, before any + // fence has been signaled. + // Note: A sync call is necessary here since the events triggered by + // addFrameEvents didn't get to piggyback on the earlier queues/dequeues. + resetTimestamps(); + int oldCount = mFakeConsumer->mGetFrameTimestampsCount; + int result = getAllFrameTimestamps(fId1); + EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); + EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outGpuCompositionDoneTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime); + EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime); + + // Verify available timestamps are correct for frame 1 again, before any + // fence has been signaled. + // This time a sync call should not be necessary. + resetTimestamps(); + oldCount = mFakeConsumer->mGetFrameTimestampsCount; + result = getAllFrameTimestamps(fId1); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); + EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outGpuCompositionDoneTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime); + EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime); + + // Signal the fences for frame 1. + mFrames[0].signalRefreshFences(); + mFrames[0].signalReleaseFences(); + + // Verify all timestamps are available without a sync call. + resetTimestamps(); + oldCount = mFakeConsumer->mGetFrameTimestampsCount; + result = getAllFrameTimestamps(fId1); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); + EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime, + outGpuCompositionDoneTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime); + EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); + EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime); +} + +// This test verifies that if the frame wasn't GPU composited but has a refresh +// event a sync call isn't made to get the GPU composite done time since it will +// never exist. +TEST_F(GetFrameTimestampsTest, NoGpuNoSync) { + enableFrameTimestamps(); + + // Dequeue and queue frame 1. + const uint64_t fId1 = getNextFrameId(); + dequeueAndQueue(0); + mFrames[0].signalQueueFences(); + + // Dequeue and queue frame 2. + dequeueAndQueue(1); + mFrames[1].signalQueueFences(); + + addFrameEvents(false, NO_FRAME_INDEX, 0); + addFrameEvents(false, 0, 1); + + // Verify available timestamps are correct for frame 1, before any + // fence has been signaled. + // Note: A sync call is necessary here since the events triggered by + // addFrameEvents didn't get to piggyback on the earlier queues/dequeues. + resetTimestamps(); + int oldCount = mFakeConsumer->mGetFrameTimestampsCount; + int result = getAllFrameTimestamps(fId1); + EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); + EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime); + EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime); + + // Signal the fences for frame 1. + mFrames[0].signalRefreshFences(); + mFrames[0].signalReleaseFences(); + + // Verify all timestamps, except GPU composition, are available without a + // sync call. + resetTimestamps(); + oldCount = mFakeConsumer->mGetFrameTimestampsCount; + result = getAllFrameTimestamps(fId1); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); + EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime); + EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); + EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime); +} + +// This test verifies that if the certain timestamps can't possibly exist for +// the most recent frame, then a sync call is not done. +TEST_F(GetFrameTimestampsTest, NoReleaseNoSync) { + enableFrameTimestamps(); + + // Dequeue and queue frame 1. + const uint64_t fId1 = getNextFrameId(); + dequeueAndQueue(0); + mFrames[0].signalQueueFences(); + + // Dequeue and queue frame 2. + const uint64_t fId2 = getNextFrameId(); + dequeueAndQueue(1); + mFrames[1].signalQueueFences(); + + addFrameEvents(false, NO_FRAME_INDEX, 0); + addFrameEvents(false, 0, 1); + + // Verify available timestamps are correct for frame 1, before any + // fence has been signaled. + // Note: A sync call is necessary here since the events triggered by + // addFrameEvents didn't get to piggyback on the earlier queues/dequeues. + resetTimestamps(); + int oldCount = mFakeConsumer->mGetFrameTimestampsCount; + int result = getAllFrameTimestamps(fId1); + EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); + EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime); + EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime); + + mFrames[0].signalRefreshFences(); + mFrames[0].signalReleaseFences(); + mFrames[1].signalRefreshFences(); + + // Verify querying for all timestmaps of f2 does not do a sync call. Even + // though the lastRefresh, dequeueReady, and release times aren't + // available, a sync call should not occur because it's not possible for f2 + // to encounter the final value for those events until another frame is + // queued. + resetTimestamps(); + oldCount = mFakeConsumer->mGetFrameTimestampsCount; + result = getAllFrameTimestamps(fId2); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[1].kLatchTime, outLatchTime); + EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime); + EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime); + EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDequeueReadyTime); + EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime); +} + +// This test verifies there are no sync calls for present times +// when they aren't supported and that an error is returned. + +TEST_F(GetFrameTimestampsTest, PresentUnsupportedNoSync) { + enableFrameTimestamps(); + mSurface->mFakeSurfaceComposer->setSupportsPresent(false); + + // Dequeue and queue frame 1. + const uint64_t fId1 = getNextFrameId(); + dequeueAndQueue(0); + + // Verify a query for the Present times do not trigger a sync call if they + // are not supported. + resetTimestamps(); + int oldCount = mFakeConsumer->mGetFrameTimestampsCount; + int result = native_window_get_frame_timestamps(mWindow.get(), fId1, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + &outDisplayPresentTime, nullptr, nullptr); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(BAD_VALUE, result); + EXPECT_EQ(-1, outDisplayPresentTime); +} + +} // namespace android diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp new file mode 100644 index 0000000000..5ed3d3bebb --- /dev/null +++ b/libs/gui/view/Surface.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 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. + */ + +#define LOG_TAG "Surface" + +#include <gui/view/Surface.h> + +#include <binder/Parcel.h> + +#include <utils/Log.h> + +#include <gui/IGraphicBufferProducer.h> + +namespace android { +namespace view { + +status_t Surface::writeToParcel(Parcel* parcel) const { + return writeToParcel(parcel, false); +} + +status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const { + if (parcel == nullptr) return BAD_VALUE; + + status_t res = OK; + + if (!nameAlreadyWritten) { + res = parcel->writeString16(name); + if (res != OK) return res; + + /* isSingleBuffered defaults to no */ + res = parcel->writeInt32(0); + if (res != OK) return res; + } + + res = parcel->writeStrongBinder( + IGraphicBufferProducer::asBinder(graphicBufferProducer)); + + return res; +} + +status_t Surface::readFromParcel(const Parcel* parcel) { + return readFromParcel(parcel, false); +} + +status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) { + if (parcel == nullptr) return BAD_VALUE; + + status_t res = OK; + if (!nameAlreadyRead) { + name = readMaybeEmptyString16(parcel); + // Discard this for now + int isSingleBuffered; + res = parcel->readInt32(&isSingleBuffered); + if (res != OK) { + ALOGE("Can't read isSingleBuffered"); + return res; + } + } + + sp<IBinder> binder; + + res = parcel->readNullableStrongBinder(&binder); + if (res != OK) { + ALOGE("%s: Can't read strong binder", __FUNCTION__); + return res; + } + + graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder); + + return OK; +} + +String16 Surface::readMaybeEmptyString16(const Parcel* parcel) { + size_t len; + const char16_t* str = parcel->readString16Inplace(&len); + if (str != nullptr) { + return String16(str, len); + } else { + return String16(); + } +} + +} // namespace view +} // namespace android diff --git a/libs/hwc2on1adapter/Android.bp b/libs/hwc2on1adapter/Android.bp new file mode 100644 index 0000000000..ec9cbf8429 --- /dev/null +++ b/libs/hwc2on1adapter/Android.bp @@ -0,0 +1,72 @@ +// 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. + +cc_library_shared { + name: "libhwc2on1adapter", + vendor: true, + + clang: true, + cppflags: [ + "-Weverything", + "-Wall", + "-Wunused", + "-Wunreachable-code", + + // The static constructors and destructors in this library have not been noted to + // introduce significant overheads + "-Wno-exit-time-destructors", + "-Wno-global-constructors", + + // We only care about compiling as C++14 + "-Wno-c++98-compat-pedantic", + + // android/sensors.h uses nested anonymous unions and anonymous structs + "-Wno-nested-anon-types", + "-Wno-gnu-anonymous-struct", + + // Don't warn about struct padding + "-Wno-padded", + + // hwcomposer2.h features switch covering all cases. + "-Wno-covered-switch-default", + + // hwcomposer.h features zero size array. + "-Wno-zero-length-array", + + // Disabling warning specific to hwc2on1adapter code + "-Wno-double-promotion", + "-Wno-sign-conversion", + "-Wno-switch-enum", + "-Wno-float-equal", + "-Wno-shorten-64-to-32", + "-Wno-sign-compare", + "-Wno-missing-prototypes", + ], + + srcs: [ + "HWC2On1Adapter.cpp", + "MiniFence.cpp", + ], + + shared_libs: [ + "libutils", + "libcutils", + "liblog", + "libhardware", + ], + + export_include_dirs: ["include"], + + export_shared_lib_headers: ["libutils"], +} diff --git a/libs/hwc2on1adapter/CleanSpec.mk b/libs/hwc2on1adapter/CleanSpec.mk new file mode 100644 index 0000000000..7fc22161fa --- /dev/null +++ b/libs/hwc2on1adapter/CleanSpec.mk @@ -0,0 +1,52 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libhwc2on1adapter_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libhwc2on1adapter.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/libhwc2on1adapter.so) diff --git a/libs/hwc2on1adapter/HWC2On1Adapter.cpp b/libs/hwc2on1adapter/HWC2On1Adapter.cpp new file mode 100644 index 0000000000..8c6ef691a2 --- /dev/null +++ b/libs/hwc2on1adapter/HWC2On1Adapter.cpp @@ -0,0 +1,2620 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hwc2on1adapter/HWC2On1Adapter.h" + +//#define LOG_NDEBUG 0 + +#undef LOG_TAG +#define LOG_TAG "HWC2On1Adapter" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + + +#include <inttypes.h> + +#include <chrono> +#include <cstdlib> +#include <sstream> + +#include <hardware/hwcomposer.h> +#include <log/log.h> +#include <utils/Trace.h> + +using namespace std::chrono_literals; + +static uint8_t getMinorVersion(struct hwc_composer_device_1* device) +{ + auto version = device->common.version & HARDWARE_API_VERSION_2_MAJ_MIN_MASK; + return (version >> 16) & 0xF; +} + +template <typename PFN, typename T> +static hwc2_function_pointer_t asFP(T function) +{ + static_assert(std::is_same<PFN, T>::value, "Incompatible function pointer"); + return reinterpret_cast<hwc2_function_pointer_t>(function); +} + +using namespace HWC2; + +static constexpr Attribute ColorMode = static_cast<Attribute>(6); + +namespace android { + +class HWC2On1Adapter::Callbacks : public hwc_procs_t { + public: + explicit Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) { + invalidate = &invalidateHook; + vsync = &vsyncHook; + hotplug = &hotplugHook; + } + + static void invalidateHook(const hwc_procs_t* procs) { + auto callbacks = static_cast<const Callbacks*>(procs); + callbacks->mAdapter.hwc1Invalidate(); + } + + static void vsyncHook(const hwc_procs_t* procs, int display, + int64_t timestamp) { + auto callbacks = static_cast<const Callbacks*>(procs); + callbacks->mAdapter.hwc1Vsync(display, timestamp); + } + + static void hotplugHook(const hwc_procs_t* procs, int display, + int connected) { + auto callbacks = static_cast<const Callbacks*>(procs); + callbacks->mAdapter.hwc1Hotplug(display, connected); + } + + private: + HWC2On1Adapter& mAdapter; +}; + +static int closeHook(hw_device_t* /*device*/) +{ + // Do nothing, since the real work is done in the class destructor, but we + // need to provide a valid function pointer for hwc2_close to call + return 0; +} + +HWC2On1Adapter::HWC2On1Adapter(hwc_composer_device_1_t* hwc1Device) + : mDumpString(), + mHwc1Device(hwc1Device), + mHwc1MinorVersion(getMinorVersion(hwc1Device)), + mHwc1SupportsVirtualDisplays(false), + mHwc1SupportsBackgroundColor(false), + mHwc1Callbacks(std::make_unique<Callbacks>(*this)), + mCapabilities(), + mLayers(), + mHwc1VirtualDisplay(), + mStateMutex(), + mCallbacks(), + mHasPendingInvalidate(false), + mPendingVsyncs(), + mPendingHotplugs(), + mDisplays(), + mHwc1DisplayMap() +{ + common.close = closeHook; + getCapabilities = getCapabilitiesHook; + getFunction = getFunctionHook; + populateCapabilities(); + populatePrimary(); + mHwc1Device->registerProcs(mHwc1Device, + static_cast<const hwc_procs_t*>(mHwc1Callbacks.get())); +} + +HWC2On1Adapter::~HWC2On1Adapter() { + hwc_close_1(mHwc1Device); +} + +void HWC2On1Adapter::doGetCapabilities(uint32_t* outCount, + int32_t* outCapabilities) { + if (outCapabilities == nullptr) { + *outCount = mCapabilities.size(); + return; + } + + auto capabilityIter = mCapabilities.cbegin(); + for (size_t written = 0; written < *outCount; ++written) { + if (capabilityIter == mCapabilities.cend()) { + return; + } + outCapabilities[written] = static_cast<int32_t>(*capabilityIter); + ++capabilityIter; + } +} + +hwc2_function_pointer_t HWC2On1Adapter::doGetFunction( + FunctionDescriptor descriptor) { + switch (descriptor) { + // Device functions + case FunctionDescriptor::CreateVirtualDisplay: + return asFP<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>( + createVirtualDisplayHook); + case FunctionDescriptor::DestroyVirtualDisplay: + return asFP<HWC2_PFN_DESTROY_VIRTUAL_DISPLAY>( + destroyVirtualDisplayHook); + case FunctionDescriptor::Dump: + return asFP<HWC2_PFN_DUMP>(dumpHook); + case FunctionDescriptor::GetMaxVirtualDisplayCount: + return asFP<HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT>( + getMaxVirtualDisplayCountHook); + case FunctionDescriptor::RegisterCallback: + return asFP<HWC2_PFN_REGISTER_CALLBACK>(registerCallbackHook); + + // Display functions + case FunctionDescriptor::AcceptDisplayChanges: + return asFP<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>( + displayHook<decltype(&Display::acceptChanges), + &Display::acceptChanges>); + case FunctionDescriptor::CreateLayer: + return asFP<HWC2_PFN_CREATE_LAYER>( + displayHook<decltype(&Display::createLayer), + &Display::createLayer, hwc2_layer_t*>); + case FunctionDescriptor::DestroyLayer: + return asFP<HWC2_PFN_DESTROY_LAYER>( + displayHook<decltype(&Display::destroyLayer), + &Display::destroyLayer, hwc2_layer_t>); + case FunctionDescriptor::GetActiveConfig: + return asFP<HWC2_PFN_GET_ACTIVE_CONFIG>( + displayHook<decltype(&Display::getActiveConfig), + &Display::getActiveConfig, hwc2_config_t*>); + case FunctionDescriptor::GetChangedCompositionTypes: + return asFP<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>( + displayHook<decltype(&Display::getChangedCompositionTypes), + &Display::getChangedCompositionTypes, uint32_t*, + hwc2_layer_t*, int32_t*>); + case FunctionDescriptor::GetColorModes: + return asFP<HWC2_PFN_GET_COLOR_MODES>( + displayHook<decltype(&Display::getColorModes), + &Display::getColorModes, uint32_t*, int32_t*>); + case FunctionDescriptor::GetDisplayAttribute: + return asFP<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>( + getDisplayAttributeHook); + case FunctionDescriptor::GetDisplayConfigs: + return asFP<HWC2_PFN_GET_DISPLAY_CONFIGS>( + displayHook<decltype(&Display::getConfigs), + &Display::getConfigs, uint32_t*, hwc2_config_t*>); + case FunctionDescriptor::GetDisplayName: + return asFP<HWC2_PFN_GET_DISPLAY_NAME>( + displayHook<decltype(&Display::getName), + &Display::getName, uint32_t*, char*>); + case FunctionDescriptor::GetDisplayRequests: + return asFP<HWC2_PFN_GET_DISPLAY_REQUESTS>( + displayHook<decltype(&Display::getRequests), + &Display::getRequests, int32_t*, uint32_t*, hwc2_layer_t*, + int32_t*>); + case FunctionDescriptor::GetDisplayType: + return asFP<HWC2_PFN_GET_DISPLAY_TYPE>( + displayHook<decltype(&Display::getType), + &Display::getType, int32_t*>); + case FunctionDescriptor::GetDozeSupport: + return asFP<HWC2_PFN_GET_DOZE_SUPPORT>( + displayHook<decltype(&Display::getDozeSupport), + &Display::getDozeSupport, int32_t*>); + case FunctionDescriptor::GetHdrCapabilities: + return asFP<HWC2_PFN_GET_HDR_CAPABILITIES>( + displayHook<decltype(&Display::getHdrCapabilities), + &Display::getHdrCapabilities, uint32_t*, int32_t*, float*, + float*, float*>); + case FunctionDescriptor::GetReleaseFences: + return asFP<HWC2_PFN_GET_RELEASE_FENCES>( + displayHook<decltype(&Display::getReleaseFences), + &Display::getReleaseFences, uint32_t*, hwc2_layer_t*, + int32_t*>); + case FunctionDescriptor::PresentDisplay: + return asFP<HWC2_PFN_PRESENT_DISPLAY>( + displayHook<decltype(&Display::present), + &Display::present, int32_t*>); + case FunctionDescriptor::SetActiveConfig: + return asFP<HWC2_PFN_SET_ACTIVE_CONFIG>( + displayHook<decltype(&Display::setActiveConfig), + &Display::setActiveConfig, hwc2_config_t>); + case FunctionDescriptor::SetClientTarget: + return asFP<HWC2_PFN_SET_CLIENT_TARGET>( + displayHook<decltype(&Display::setClientTarget), + &Display::setClientTarget, buffer_handle_t, int32_t, + int32_t, hwc_region_t>); + case FunctionDescriptor::SetColorMode: + return asFP<HWC2_PFN_SET_COLOR_MODE>(setColorModeHook); + case FunctionDescriptor::SetColorTransform: + return asFP<HWC2_PFN_SET_COLOR_TRANSFORM>(setColorTransformHook); + case FunctionDescriptor::SetOutputBuffer: + return asFP<HWC2_PFN_SET_OUTPUT_BUFFER>( + displayHook<decltype(&Display::setOutputBuffer), + &Display::setOutputBuffer, buffer_handle_t, int32_t>); + case FunctionDescriptor::SetPowerMode: + return asFP<HWC2_PFN_SET_POWER_MODE>(setPowerModeHook); + case FunctionDescriptor::SetVsyncEnabled: + return asFP<HWC2_PFN_SET_VSYNC_ENABLED>(setVsyncEnabledHook); + case FunctionDescriptor::ValidateDisplay: + return asFP<HWC2_PFN_VALIDATE_DISPLAY>( + displayHook<decltype(&Display::validate), + &Display::validate, uint32_t*, uint32_t*>); + case FunctionDescriptor::GetClientTargetSupport: + return asFP<HWC2_PFN_GET_CLIENT_TARGET_SUPPORT>( + displayHook<decltype(&Display::getClientTargetSupport), + &Display::getClientTargetSupport, uint32_t, uint32_t, + int32_t, int32_t>); + + // Layer functions + case FunctionDescriptor::SetCursorPosition: + return asFP<HWC2_PFN_SET_CURSOR_POSITION>( + layerHook<decltype(&Layer::setCursorPosition), + &Layer::setCursorPosition, int32_t, int32_t>); + case FunctionDescriptor::SetLayerBuffer: + return asFP<HWC2_PFN_SET_LAYER_BUFFER>( + layerHook<decltype(&Layer::setBuffer), &Layer::setBuffer, + buffer_handle_t, int32_t>); + case FunctionDescriptor::SetLayerSurfaceDamage: + return asFP<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>( + layerHook<decltype(&Layer::setSurfaceDamage), + &Layer::setSurfaceDamage, hwc_region_t>); + + // Layer state functions + case FunctionDescriptor::SetLayerBlendMode: + return asFP<HWC2_PFN_SET_LAYER_BLEND_MODE>( + setLayerBlendModeHook); + case FunctionDescriptor::SetLayerColor: + return asFP<HWC2_PFN_SET_LAYER_COLOR>( + layerHook<decltype(&Layer::setColor), &Layer::setColor, + hwc_color_t>); + case FunctionDescriptor::SetLayerCompositionType: + return asFP<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>( + setLayerCompositionTypeHook); + case FunctionDescriptor::SetLayerDataspace: + return asFP<HWC2_PFN_SET_LAYER_DATASPACE>(setLayerDataspaceHook); + case FunctionDescriptor::SetLayerDisplayFrame: + return asFP<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>( + layerHook<decltype(&Layer::setDisplayFrame), + &Layer::setDisplayFrame, hwc_rect_t>); + case FunctionDescriptor::SetLayerPlaneAlpha: + return asFP<HWC2_PFN_SET_LAYER_PLANE_ALPHA>( + layerHook<decltype(&Layer::setPlaneAlpha), + &Layer::setPlaneAlpha, float>); + case FunctionDescriptor::SetLayerSidebandStream: + return asFP<HWC2_PFN_SET_LAYER_SIDEBAND_STREAM>( + layerHook<decltype(&Layer::setSidebandStream), + &Layer::setSidebandStream, const native_handle_t*>); + case FunctionDescriptor::SetLayerSourceCrop: + return asFP<HWC2_PFN_SET_LAYER_SOURCE_CROP>( + layerHook<decltype(&Layer::setSourceCrop), + &Layer::setSourceCrop, hwc_frect_t>); + case FunctionDescriptor::SetLayerTransform: + return asFP<HWC2_PFN_SET_LAYER_TRANSFORM>(setLayerTransformHook); + case FunctionDescriptor::SetLayerVisibleRegion: + return asFP<HWC2_PFN_SET_LAYER_VISIBLE_REGION>( + layerHook<decltype(&Layer::setVisibleRegion), + &Layer::setVisibleRegion, hwc_region_t>); + case FunctionDescriptor::SetLayerZOrder: + return asFP<HWC2_PFN_SET_LAYER_Z_ORDER>(setLayerZOrderHook); + + default: + ALOGE("doGetFunction: Unknown function descriptor: %d (%s)", + static_cast<int32_t>(descriptor), + to_string(descriptor).c_str()); + return nullptr; + } +} + +// Device functions + +Error HWC2On1Adapter::createVirtualDisplay(uint32_t width, + uint32_t height, hwc2_display_t* outDisplay) { + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + if (mHwc1VirtualDisplay) { + // We have already allocated our only HWC1 virtual display + ALOGE("createVirtualDisplay: HWC1 virtual display already allocated"); + return Error::NoResources; + } + + mHwc1VirtualDisplay = std::make_shared<HWC2On1Adapter::Display>(*this, + HWC2::DisplayType::Virtual); + mHwc1VirtualDisplay->populateConfigs(width, height); + const auto displayId = mHwc1VirtualDisplay->getId(); + mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL] = displayId; + mHwc1VirtualDisplay->setHwc1Id(HWC_DISPLAY_VIRTUAL); + mDisplays.emplace(displayId, mHwc1VirtualDisplay); + *outDisplay = displayId; + + return Error::None; +} + +Error HWC2On1Adapter::destroyVirtualDisplay(hwc2_display_t displayId) { + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + if (!mHwc1VirtualDisplay || (mHwc1VirtualDisplay->getId() != displayId)) { + return Error::BadDisplay; + } + + mHwc1VirtualDisplay.reset(); + mHwc1DisplayMap.erase(HWC_DISPLAY_VIRTUAL); + mDisplays.erase(displayId); + + return Error::None; +} + +void HWC2On1Adapter::dump(uint32_t* outSize, char* outBuffer) { + if (outBuffer != nullptr) { + auto copiedBytes = mDumpString.copy(outBuffer, *outSize); + *outSize = static_cast<uint32_t>(copiedBytes); + return; + } + + std::stringstream output; + + output << "-- HWC2On1Adapter --\n"; + + output << "Adapting to a HWC 1." << static_cast<int>(mHwc1MinorVersion) << + " device\n"; + + // Attempt to acquire the lock for 1 second, but proceed without the lock + // after that, so we can still get some information if we're deadlocked + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex, + std::defer_lock); + lock.try_lock_for(1s); + + if (mCapabilities.empty()) { + output << "Capabilities: None\n"; + } else { + output << "Capabilities:\n"; + for (auto capability : mCapabilities) { + output << " " << to_string(capability) << '\n'; + } + } + + output << "Displays:\n"; + for (const auto& element : mDisplays) { + const auto& display = element.second; + output << display->dump(); + } + output << '\n'; + + // Release the lock before calling into HWC1, and since we no longer require + // mutual exclusion to access mCapabilities or mDisplays + lock.unlock(); + + if (mHwc1Device->dump) { + output << "HWC1 dump:\n"; + std::vector<char> hwc1Dump(4096); + // Call with size - 1 to preserve a null character at the end + mHwc1Device->dump(mHwc1Device, hwc1Dump.data(), + static_cast<int>(hwc1Dump.size() - 1)); + output << hwc1Dump.data(); + } + + mDumpString = output.str(); + *outSize = static_cast<uint32_t>(mDumpString.size()); +} + +uint32_t HWC2On1Adapter::getMaxVirtualDisplayCount() { + return mHwc1SupportsVirtualDisplays ? 1 : 0; +} + +static bool isValid(Callback descriptor) { + switch (descriptor) { + case Callback::Hotplug: // Fall-through + case Callback::Refresh: // Fall-through + case Callback::Vsync: return true; + default: return false; + } +} + +Error HWC2On1Adapter::registerCallback(Callback descriptor, + hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer) { + if (!isValid(descriptor)) { + return Error::BadParameter; + } + + ALOGV("registerCallback(%s, %p, %p)", to_string(descriptor).c_str(), + callbackData, pointer); + + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + mCallbacks[descriptor] = {callbackData, pointer}; + + bool hasPendingInvalidate = false; + std::vector<hwc2_display_t> displayIds; + std::vector<std::pair<hwc2_display_t, int64_t>> pendingVsyncs; + std::vector<std::pair<hwc2_display_t, int>> pendingHotplugs; + + if (descriptor == Callback::Refresh) { + hasPendingInvalidate = mHasPendingInvalidate; + if (hasPendingInvalidate) { + for (auto& displayPair : mDisplays) { + displayIds.emplace_back(displayPair.first); + } + } + mHasPendingInvalidate = false; + } else if (descriptor == Callback::Vsync) { + for (auto pending : mPendingVsyncs) { + auto hwc1DisplayId = pending.first; + if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) { + ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d", + hwc1DisplayId); + continue; + } + auto displayId = mHwc1DisplayMap[hwc1DisplayId]; + auto timestamp = pending.second; + pendingVsyncs.emplace_back(displayId, timestamp); + } + mPendingVsyncs.clear(); + } else if (descriptor == Callback::Hotplug) { + // Hotplug the primary display + pendingHotplugs.emplace_back(mHwc1DisplayMap[HWC_DISPLAY_PRIMARY], + static_cast<int32_t>(Connection::Connected)); + + for (auto pending : mPendingHotplugs) { + auto hwc1DisplayId = pending.first; + if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) { + ALOGE("hwc1Hotplug: Couldn't find display for HWC1 id %d", + hwc1DisplayId); + continue; + } + auto displayId = mHwc1DisplayMap[hwc1DisplayId]; + auto connected = pending.second; + pendingHotplugs.emplace_back(displayId, connected); + } + } + + // Call pending callbacks without the state lock held + lock.unlock(); + + if (hasPendingInvalidate) { + auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(pointer); + for (auto displayId : displayIds) { + refresh(callbackData, displayId); + } + } + if (!pendingVsyncs.empty()) { + auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(pointer); + for (auto& pendingVsync : pendingVsyncs) { + vsync(callbackData, pendingVsync.first, pendingVsync.second); + } + } + if (!pendingHotplugs.empty()) { + auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(pointer); + for (auto& pendingHotplug : pendingHotplugs) { + hotplug(callbackData, pendingHotplug.first, pendingHotplug.second); + } + } + return Error::None; +} + +// Display functions + +std::atomic<hwc2_display_t> HWC2On1Adapter::Display::sNextId(1); + +HWC2On1Adapter::Display::Display(HWC2On1Adapter& device, HWC2::DisplayType type) + : mId(sNextId++), + mDevice(device), + mStateMutex(), + mHwc1RequestedContents(nullptr), + mRetireFence(), + mChanges(), + mHwc1Id(-1), + mConfigs(), + mActiveConfig(nullptr), + mActiveColorMode(static_cast<android_color_mode_t>(-1)), + mName(), + mType(type), + mPowerMode(PowerMode::Off), + mVsyncEnabled(Vsync::Invalid), + mClientTarget(), + mOutputBuffer(), + mHasColorTransform(false), + mLayers(), + mHwc1LayerMap(), + mNumAvailableRects(0), + mNextAvailableRect(nullptr), + mGeometryChanged(false) + {} + +Error HWC2On1Adapter::Display::acceptChanges() { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!mChanges) { + ALOGV("[%" PRIu64 "] acceptChanges failed, not validated", mId); + return Error::NotValidated; + } + + ALOGV("[%" PRIu64 "] acceptChanges", mId); + + for (auto& change : mChanges->getTypeChanges()) { + auto layerId = change.first; + auto type = change.second; + if (mDevice.mLayers.count(layerId) == 0) { + // This should never happen but somehow does. + ALOGW("Cannot accept change for unknown layer (%" PRIu64 ")", + layerId); + continue; + } + auto layer = mDevice.mLayers[layerId]; + layer->setCompositionType(type); + } + + mChanges->clearTypeChanges(); + + return Error::None; +} + +Error HWC2On1Adapter::Display::createLayer(hwc2_layer_t* outLayerId) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + auto layer = *mLayers.emplace(std::make_shared<Layer>(*this)); + mDevice.mLayers.emplace(std::make_pair(layer->getId(), layer)); + *outLayerId = layer->getId(); + ALOGV("[%" PRIu64 "] created layer %" PRIu64, mId, *outLayerId); + markGeometryChanged(); + return Error::None; +} + +Error HWC2On1Adapter::Display::destroyLayer(hwc2_layer_t layerId) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + const auto mapLayer = mDevice.mLayers.find(layerId); + if (mapLayer == mDevice.mLayers.end()) { + ALOGV("[%" PRIu64 "] destroyLayer(%" PRIu64 ") failed: no such layer", + mId, layerId); + return Error::BadLayer; + } + const auto layer = mapLayer->second; + mDevice.mLayers.erase(mapLayer); + const auto zRange = mLayers.equal_range(layer); + for (auto current = zRange.first; current != zRange.second; ++current) { + if (**current == *layer) { + current = mLayers.erase(current); + break; + } + } + ALOGV("[%" PRIu64 "] destroyed layer %" PRIu64, mId, layerId); + markGeometryChanged(); + return Error::None; +} + +Error HWC2On1Adapter::Display::getActiveConfig(hwc2_config_t* outConfig) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!mActiveConfig) { + ALOGV("[%" PRIu64 "] getActiveConfig --> %s", mId, + to_string(Error::BadConfig).c_str()); + return Error::BadConfig; + } + auto configId = mActiveConfig->getId(); + ALOGV("[%" PRIu64 "] getActiveConfig --> %u", mId, configId); + *outConfig = configId; + return Error::None; +} + +Error HWC2On1Adapter::Display::getAttribute(hwc2_config_t configId, + Attribute attribute, int32_t* outValue) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) { + ALOGV("[%" PRIu64 "] getAttribute failed: bad config (%u)", mId, + configId); + return Error::BadConfig; + } + *outValue = mConfigs[configId]->getAttribute(attribute); + ALOGV("[%" PRIu64 "] getAttribute(%u, %s) --> %d", mId, configId, + to_string(attribute).c_str(), *outValue); + return Error::None; +} + +Error HWC2On1Adapter::Display::getChangedCompositionTypes( + uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outTypes) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!mChanges) { + ALOGE("[%" PRIu64 "] getChangedCompositionTypes failed: not validated", + mId); + return Error::NotValidated; + } + + if ((outLayers == nullptr) || (outTypes == nullptr)) { + *outNumElements = mChanges->getTypeChanges().size(); + return Error::None; + } + + uint32_t numWritten = 0; + for (const auto& element : mChanges->getTypeChanges()) { + if (numWritten == *outNumElements) { + break; + } + auto layerId = element.first; + auto intType = static_cast<int32_t>(element.second); + ALOGV("Adding %" PRIu64 " %s", layerId, + to_string(element.second).c_str()); + outLayers[numWritten] = layerId; + outTypes[numWritten] = intType; + ++numWritten; + } + *outNumElements = numWritten; + + return Error::None; +} + +Error HWC2On1Adapter::Display::getColorModes(uint32_t* outNumModes, + int32_t* outModes) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!outModes) { + *outNumModes = mColorModes.size(); + return Error::None; + } + uint32_t numModes = std::min(*outNumModes, + static_cast<uint32_t>(mColorModes.size())); + std::copy_n(mColorModes.cbegin(), numModes, outModes); + *outNumModes = numModes; + return Error::None; +} + +Error HWC2On1Adapter::Display::getConfigs(uint32_t* outNumConfigs, + hwc2_config_t* outConfigs) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!outConfigs) { + *outNumConfigs = mConfigs.size(); + return Error::None; + } + uint32_t numWritten = 0; + for (const auto& config : mConfigs) { + if (numWritten == *outNumConfigs) { + break; + } + outConfigs[numWritten] = config->getId(); + ++numWritten; + } + *outNumConfigs = numWritten; + return Error::None; +} + +Error HWC2On1Adapter::Display::getDozeSupport(int32_t* outSupport) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (mDevice.mHwc1MinorVersion < 4 || mHwc1Id != 0) { + *outSupport = 0; + } else { + *outSupport = 1; + } + return Error::None; +} + +Error HWC2On1Adapter::Display::getHdrCapabilities(uint32_t* outNumTypes, + int32_t* /*outTypes*/, float* /*outMaxLuminance*/, + float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) { + // This isn't supported on HWC1, so per the HWC2 header, return numTypes = 0 + *outNumTypes = 0; + return Error::None; +} + +Error HWC2On1Adapter::Display::getName(uint32_t* outSize, char* outName) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!outName) { + *outSize = mName.size(); + return Error::None; + } + auto numCopied = mName.copy(outName, *outSize); + *outSize = numCopied; + return Error::None; +} + +Error HWC2On1Adapter::Display::getReleaseFences(uint32_t* outNumElements, + hwc2_layer_t* outLayers, int32_t* outFences) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + uint32_t numWritten = 0; + bool outputsNonNull = (outLayers != nullptr) && (outFences != nullptr); + for (const auto& layer : mLayers) { + if (outputsNonNull && (numWritten == *outNumElements)) { + break; + } + + auto releaseFence = layer->getReleaseFence(); + if (releaseFence != MiniFence::NO_FENCE) { + if (outputsNonNull) { + outLayers[numWritten] = layer->getId(); + outFences[numWritten] = releaseFence->dup(); + } + ++numWritten; + } + } + *outNumElements = numWritten; + + return Error::None; +} + +Error HWC2On1Adapter::Display::getRequests(int32_t* outDisplayRequests, + uint32_t* outNumElements, hwc2_layer_t* outLayers, + int32_t* outLayerRequests) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!mChanges) { + return Error::NotValidated; + } + + if (outLayers == nullptr || outLayerRequests == nullptr) { + *outNumElements = mChanges->getNumLayerRequests(); + return Error::None; + } + + // Display requests (HWC2::DisplayRequest) are not supported by hwc1: + // A hwc1 has always zero requests for the client. + *outDisplayRequests = 0; + + uint32_t numWritten = 0; + for (const auto& request : mChanges->getLayerRequests()) { + if (numWritten == *outNumElements) { + break; + } + outLayers[numWritten] = request.first; + outLayerRequests[numWritten] = static_cast<int32_t>(request.second); + ++numWritten; + } + + return Error::None; +} + +Error HWC2On1Adapter::Display::getType(int32_t* outType) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + *outType = static_cast<int32_t>(mType); + return Error::None; +} + +Error HWC2On1Adapter::Display::present(int32_t* outRetireFence) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (mChanges) { + Error error = mDevice.setAllDisplays(); + if (error != Error::None) { + ALOGE("[%" PRIu64 "] present: setAllDisplaysFailed (%s)", mId, + to_string(error).c_str()); + return error; + } + } + + *outRetireFence = mRetireFence.get()->dup(); + ALOGV("[%" PRIu64 "] present returning retire fence %d", mId, + *outRetireFence); + + return Error::None; +} + +Error HWC2On1Adapter::Display::setActiveConfig(hwc2_config_t configId) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + auto config = getConfig(configId); + if (!config) { + return Error::BadConfig; + } + if (config == mActiveConfig) { + return Error::None; + } + + if (mDevice.mHwc1MinorVersion >= 4) { + uint32_t hwc1Id = 0; + auto error = config->getHwc1IdForColorMode(mActiveColorMode, &hwc1Id); + if (error != Error::None) { + return error; + } + + int intError = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device, + mHwc1Id, static_cast<int>(hwc1Id)); + if (intError != 0) { + ALOGE("setActiveConfig: Failed to set active config on HWC1 (%d)", + intError); + return Error::BadConfig; + } + mActiveConfig = config; + } + + return Error::None; +} + +Error HWC2On1Adapter::Display::setClientTarget(buffer_handle_t target, + int32_t acquireFence, int32_t /*dataspace*/, hwc_region_t /*damage*/) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + ALOGV("[%" PRIu64 "] setClientTarget(%p, %d)", mId, target, acquireFence); + mClientTarget.setBuffer(target); + mClientTarget.setFence(acquireFence); + // dataspace and damage can't be used by HWC1, so ignore them + return Error::None; +} + +Error HWC2On1Adapter::Display::setColorMode(android_color_mode_t mode) { + std::unique_lock<std::recursive_mutex> lock (mStateMutex); + + ALOGV("[%" PRIu64 "] setColorMode(%d)", mId, mode); + + if (mode == mActiveColorMode) { + return Error::None; + } + if (mColorModes.count(mode) == 0) { + ALOGE("[%" PRIu64 "] Mode %d not found in mColorModes", mId, mode); + return Error::Unsupported; + } + + uint32_t hwc1Config = 0; + auto error = mActiveConfig->getHwc1IdForColorMode(mode, &hwc1Config); + if (error != Error::None) { + return error; + } + + ALOGV("[%" PRIu64 "] Setting HWC1 config %u", mId, hwc1Config); + int intError = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device, + mHwc1Id, hwc1Config); + if (intError != 0) { + ALOGE("[%" PRIu64 "] Failed to set HWC1 config (%d)", mId, intError); + return Error::Unsupported; + } + + mActiveColorMode = mode; + return Error::None; +} + +Error HWC2On1Adapter::Display::setColorTransform(android_color_transform_t hint) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + ALOGV("%" PRIu64 "] setColorTransform(%d)", mId, + static_cast<int32_t>(hint)); + mHasColorTransform = (hint != HAL_COLOR_TRANSFORM_IDENTITY); + return Error::None; +} + +Error HWC2On1Adapter::Display::setOutputBuffer(buffer_handle_t buffer, + int32_t releaseFence) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + ALOGV("[%" PRIu64 "] setOutputBuffer(%p, %d)", mId, buffer, releaseFence); + mOutputBuffer.setBuffer(buffer); + mOutputBuffer.setFence(releaseFence); + return Error::None; +} + +static bool isValid(PowerMode mode) { + switch (mode) { + case PowerMode::Off: // Fall-through + case PowerMode::DozeSuspend: // Fall-through + case PowerMode::Doze: // Fall-through + case PowerMode::On: return true; + } +} + +static int getHwc1PowerMode(PowerMode mode) { + switch (mode) { + case PowerMode::Off: return HWC_POWER_MODE_OFF; + case PowerMode::DozeSuspend: return HWC_POWER_MODE_DOZE_SUSPEND; + case PowerMode::Doze: return HWC_POWER_MODE_DOZE; + case PowerMode::On: return HWC_POWER_MODE_NORMAL; + } +} + +Error HWC2On1Adapter::Display::setPowerMode(PowerMode mode) { + if (!isValid(mode)) { + return Error::BadParameter; + } + if (mode == mPowerMode) { + return Error::None; + } + + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + int error = 0; + if (mDevice.mHwc1MinorVersion < 4) { + error = mDevice.mHwc1Device->blank(mDevice.mHwc1Device, mHwc1Id, + mode == PowerMode::Off); + } else { + error = mDevice.mHwc1Device->setPowerMode(mDevice.mHwc1Device, + mHwc1Id, getHwc1PowerMode(mode)); + } + ALOGE_IF(error != 0, "setPowerMode: Failed to set power mode on HWC1 (%d)", + error); + + ALOGV("[%" PRIu64 "] setPowerMode(%s)", mId, to_string(mode).c_str()); + mPowerMode = mode; + return Error::None; +} + +static bool isValid(Vsync enable) { + switch (enable) { + case Vsync::Enable: // Fall-through + case Vsync::Disable: return true; + case Vsync::Invalid: return false; + } +} + +Error HWC2On1Adapter::Display::setVsyncEnabled(Vsync enable) { + if (!isValid(enable)) { + return Error::BadParameter; + } + if (enable == mVsyncEnabled) { + return Error::None; + } + + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + int error = mDevice.mHwc1Device->eventControl(mDevice.mHwc1Device, + mHwc1Id, HWC_EVENT_VSYNC, enable == Vsync::Enable); + ALOGE_IF(error != 0, "setVsyncEnabled: Failed to set vsync on HWC1 (%d)", + error); + + mVsyncEnabled = enable; + return Error::None; +} + +Error HWC2On1Adapter::Display::validate(uint32_t* outNumTypes, + uint32_t* outNumRequests) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!mChanges) { + if (!mDevice.prepareAllDisplays()) { + return Error::BadDisplay; + } + } else { + ALOGE("Validate was called more than once!"); + } + + *outNumTypes = mChanges->getNumTypes(); + *outNumRequests = mChanges->getNumLayerRequests(); + ALOGV("[%" PRIu64 "] validate --> %u types, %u requests", mId, *outNumTypes, + *outNumRequests); + for (auto request : mChanges->getTypeChanges()) { + ALOGV("Layer %" PRIu64 " --> %s", request.first, + to_string(request.second).c_str()); + } + return *outNumTypes > 0 ? Error::HasChanges : Error::None; +} + +Error HWC2On1Adapter::Display::updateLayerZ(hwc2_layer_t layerId, uint32_t z) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + const auto mapLayer = mDevice.mLayers.find(layerId); + if (mapLayer == mDevice.mLayers.end()) { + ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer", mId); + return Error::BadLayer; + } + + const auto layer = mapLayer->second; + const auto zRange = mLayers.equal_range(layer); + bool layerOnDisplay = false; + for (auto current = zRange.first; current != zRange.second; ++current) { + if (**current == *layer) { + if ((*current)->getZ() == z) { + // Don't change anything if the Z hasn't changed + return Error::None; + } + current = mLayers.erase(current); + layerOnDisplay = true; + break; + } + } + + if (!layerOnDisplay) { + ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer on display", + mId); + return Error::BadLayer; + } + + layer->setZ(z); + mLayers.emplace(std::move(layer)); + markGeometryChanged(); + + return Error::None; +} + +Error HWC2On1Adapter::Display::getClientTargetSupport(uint32_t width, uint32_t height, + int32_t format, int32_t dataspace){ + if (mActiveConfig == nullptr) { + return Error::Unsupported; + } + + if (width == mActiveConfig->getAttribute(Attribute::Width) && + height == mActiveConfig->getAttribute(Attribute::Height) && + format == HAL_PIXEL_FORMAT_RGBA_8888 && + dataspace == HAL_DATASPACE_UNKNOWN) { + return Error::None; + } + + return Error::Unsupported; +} + +static constexpr uint32_t ATTRIBUTES_WITH_COLOR[] = { + HWC_DISPLAY_VSYNC_PERIOD, + HWC_DISPLAY_WIDTH, + HWC_DISPLAY_HEIGHT, + HWC_DISPLAY_DPI_X, + HWC_DISPLAY_DPI_Y, + HWC_DISPLAY_COLOR_TRANSFORM, + HWC_DISPLAY_NO_ATTRIBUTE, +}; + +static constexpr uint32_t ATTRIBUTES_WITHOUT_COLOR[] = { + HWC_DISPLAY_VSYNC_PERIOD, + HWC_DISPLAY_WIDTH, + HWC_DISPLAY_HEIGHT, + HWC_DISPLAY_DPI_X, + HWC_DISPLAY_DPI_Y, + HWC_DISPLAY_NO_ATTRIBUTE, +}; + +static constexpr size_t NUM_ATTRIBUTES_WITH_COLOR = + sizeof(ATTRIBUTES_WITH_COLOR) / sizeof(uint32_t); +static_assert(sizeof(ATTRIBUTES_WITH_COLOR) > sizeof(ATTRIBUTES_WITHOUT_COLOR), + "Attribute tables have unexpected sizes"); + +static constexpr uint32_t ATTRIBUTE_MAP_WITH_COLOR[] = { + 6, // HWC_DISPLAY_NO_ATTRIBUTE = 0 + 0, // HWC_DISPLAY_VSYNC_PERIOD = 1, + 1, // HWC_DISPLAY_WIDTH = 2, + 2, // HWC_DISPLAY_HEIGHT = 3, + 3, // HWC_DISPLAY_DPI_X = 4, + 4, // HWC_DISPLAY_DPI_Y = 5, + 5, // HWC_DISPLAY_COLOR_TRANSFORM = 6, +}; + +static constexpr uint32_t ATTRIBUTE_MAP_WITHOUT_COLOR[] = { + 5, // HWC_DISPLAY_NO_ATTRIBUTE = 0 + 0, // HWC_DISPLAY_VSYNC_PERIOD = 1, + 1, // HWC_DISPLAY_WIDTH = 2, + 2, // HWC_DISPLAY_HEIGHT = 3, + 3, // HWC_DISPLAY_DPI_X = 4, + 4, // HWC_DISPLAY_DPI_Y = 5, +}; + +template <uint32_t attribute> +static constexpr bool attributesMatch() +{ + bool match = (attribute == + ATTRIBUTES_WITH_COLOR[ATTRIBUTE_MAP_WITH_COLOR[attribute]]); + if (attribute == HWC_DISPLAY_COLOR_TRANSFORM) { + return match; + } + + return match && (attribute == + ATTRIBUTES_WITHOUT_COLOR[ATTRIBUTE_MAP_WITHOUT_COLOR[attribute]]); +} +static_assert(attributesMatch<HWC_DISPLAY_VSYNC_PERIOD>(), + "Tables out of sync"); +static_assert(attributesMatch<HWC_DISPLAY_WIDTH>(), "Tables out of sync"); +static_assert(attributesMatch<HWC_DISPLAY_HEIGHT>(), "Tables out of sync"); +static_assert(attributesMatch<HWC_DISPLAY_DPI_X>(), "Tables out of sync"); +static_assert(attributesMatch<HWC_DISPLAY_DPI_Y>(), "Tables out of sync"); +static_assert(attributesMatch<HWC_DISPLAY_COLOR_TRANSFORM>(), + "Tables out of sync"); + +void HWC2On1Adapter::Display::populateConfigs() { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + ALOGV("[%" PRIu64 "] populateConfigs", mId); + + if (mHwc1Id == -1) { + ALOGE("populateConfigs: HWC1 ID not set"); + return; + } + + const size_t MAX_NUM_CONFIGS = 128; + uint32_t configs[MAX_NUM_CONFIGS] = {}; + size_t numConfigs = MAX_NUM_CONFIGS; + mDevice.mHwc1Device->getDisplayConfigs(mDevice.mHwc1Device, mHwc1Id, + configs, &numConfigs); + + for (size_t c = 0; c < numConfigs; ++c) { + uint32_t hwc1ConfigId = configs[c]; + auto newConfig = std::make_shared<Config>(*this); + + int32_t values[NUM_ATTRIBUTES_WITH_COLOR] = {}; + bool hasColor = true; + auto result = mDevice.mHwc1Device->getDisplayAttributes( + mDevice.mHwc1Device, mHwc1Id, hwc1ConfigId, + ATTRIBUTES_WITH_COLOR, values); + if (result != 0) { + mDevice.mHwc1Device->getDisplayAttributes(mDevice.mHwc1Device, + mHwc1Id, hwc1ConfigId, ATTRIBUTES_WITHOUT_COLOR, values); + hasColor = false; + } + + auto attributeMap = hasColor ? + ATTRIBUTE_MAP_WITH_COLOR : ATTRIBUTE_MAP_WITHOUT_COLOR; + + newConfig->setAttribute(Attribute::VsyncPeriod, + values[attributeMap[HWC_DISPLAY_VSYNC_PERIOD]]); + newConfig->setAttribute(Attribute::Width, + values[attributeMap[HWC_DISPLAY_WIDTH]]); + newConfig->setAttribute(Attribute::Height, + values[attributeMap[HWC_DISPLAY_HEIGHT]]); + newConfig->setAttribute(Attribute::DpiX, + values[attributeMap[HWC_DISPLAY_DPI_X]]); + newConfig->setAttribute(Attribute::DpiY, + values[attributeMap[HWC_DISPLAY_DPI_Y]]); + if (hasColor) { + // In HWC1, color modes are referred to as color transforms. To avoid confusion with + // the HWC2 concept of color transforms, we internally refer to them as color modes for + // both HWC1 and 2. + newConfig->setAttribute(ColorMode, + values[attributeMap[HWC_DISPLAY_COLOR_TRANSFORM]]); + } + + // We can only do this after attempting to read the color mode + newConfig->setHwc1Id(hwc1ConfigId); + + for (auto& existingConfig : mConfigs) { + if (existingConfig->merge(*newConfig)) { + ALOGV("Merged config %d with existing config %u: %s", + hwc1ConfigId, existingConfig->getId(), + existingConfig->toString().c_str()); + newConfig.reset(); + break; + } + } + + // If it wasn't merged with any existing config, add it to the end + if (newConfig) { + newConfig->setId(static_cast<hwc2_config_t>(mConfigs.size())); + ALOGV("Found new config %u: %s", newConfig->getId(), + newConfig->toString().c_str()); + mConfigs.emplace_back(std::move(newConfig)); + } + } + + initializeActiveConfig(); + populateColorModes(); +} + +void HWC2On1Adapter::Display::populateConfigs(uint32_t width, uint32_t height) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + mConfigs.emplace_back(std::make_shared<Config>(*this)); + auto& config = mConfigs[0]; + + config->setAttribute(Attribute::Width, static_cast<int32_t>(width)); + config->setAttribute(Attribute::Height, static_cast<int32_t>(height)); + config->setHwc1Id(0); + config->setId(0); + mActiveConfig = config; +} + +bool HWC2On1Adapter::Display::prepare() { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + // Only prepare display contents for displays HWC1 knows about + if (mHwc1Id == -1) { + return true; + } + + // It doesn't make sense to prepare a display for which there is no active + // config, so return early + if (!mActiveConfig) { + ALOGE("[%" PRIu64 "] Attempted to prepare, but no config active", mId); + return false; + } + + allocateRequestedContents(); + assignHwc1LayerIds(); + + mHwc1RequestedContents->retireFenceFd = -1; + mHwc1RequestedContents->flags = 0; + if (mGeometryChanged) { + mHwc1RequestedContents->flags |= HWC_GEOMETRY_CHANGED; + } + mHwc1RequestedContents->outbuf = mOutputBuffer.getBuffer(); + mHwc1RequestedContents->outbufAcquireFenceFd = mOutputBuffer.getFence(); + + // +1 is for framebuffer target layer. + mHwc1RequestedContents->numHwLayers = mLayers.size() + 1; + for (auto& layer : mLayers) { + auto& hwc1Layer = mHwc1RequestedContents->hwLayers[layer->getHwc1Id()]; + hwc1Layer.releaseFenceFd = -1; + hwc1Layer.acquireFenceFd = -1; + ALOGV("Applying states for layer %" PRIu64 " ", layer->getId()); + layer->applyState(hwc1Layer); + } + + prepareFramebufferTarget(); + + resetGeometryMarker(); + + return true; +} + +void HWC2On1Adapter::Display::generateChanges() { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + mChanges.reset(new Changes); + + size_t numLayers = mHwc1RequestedContents->numHwLayers; + for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) { + const auto& receivedLayer = mHwc1RequestedContents->hwLayers[hwc1Id]; + if (mHwc1LayerMap.count(hwc1Id) == 0) { + ALOGE_IF(receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET, + "generateChanges: HWC1 layer %zd doesn't have a" + " matching HWC2 layer, and isn't the framebuffer target", + hwc1Id); + continue; + } + + Layer& layer = *mHwc1LayerMap[hwc1Id]; + updateTypeChanges(receivedLayer, layer); + updateLayerRequests(receivedLayer, layer); + } +} + +bool HWC2On1Adapter::Display::hasChanges() const { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + return mChanges != nullptr; +} + +Error HWC2On1Adapter::Display::set(hwc_display_contents_1& hwcContents) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!mChanges || (mChanges->getNumTypes() > 0)) { + ALOGE("[%" PRIu64 "] set failed: not validated", mId); + return Error::NotValidated; + } + + // Set up the client/framebuffer target + auto numLayers = hwcContents.numHwLayers; + + // Close acquire fences on FRAMEBUFFER layers, since they will not be used + // by HWC + for (size_t l = 0; l < numLayers - 1; ++l) { + auto& layer = hwcContents.hwLayers[l]; + if (layer.compositionType == HWC_FRAMEBUFFER) { + ALOGV("Closing fence %d for layer %zd", layer.acquireFenceFd, l); + close(layer.acquireFenceFd); + layer.acquireFenceFd = -1; + } + } + + auto& clientTargetLayer = hwcContents.hwLayers[numLayers - 1]; + if (clientTargetLayer.compositionType == HWC_FRAMEBUFFER_TARGET) { + clientTargetLayer.handle = mClientTarget.getBuffer(); + clientTargetLayer.acquireFenceFd = mClientTarget.getFence(); + } else { + ALOGE("[%" PRIu64 "] set: last HWC layer wasn't FRAMEBUFFER_TARGET", + mId); + } + + mChanges.reset(); + + return Error::None; +} + +void HWC2On1Adapter::Display::addRetireFence(int fenceFd) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + mRetireFence.add(fenceFd); +} + +void HWC2On1Adapter::Display::addReleaseFences( + const hwc_display_contents_1_t& hwcContents) { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + size_t numLayers = hwcContents.numHwLayers; + for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) { + const auto& receivedLayer = hwcContents.hwLayers[hwc1Id]; + if (mHwc1LayerMap.count(hwc1Id) == 0) { + if (receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET) { + ALOGE("addReleaseFences: HWC1 layer %zd doesn't have a" + " matching HWC2 layer, and isn't the framebuffer" + " target", hwc1Id); + } + // Close the framebuffer target release fence since we will use the + // display retire fence instead + if (receivedLayer.releaseFenceFd != -1) { + close(receivedLayer.releaseFenceFd); + } + continue; + } + + Layer& layer = *mHwc1LayerMap[hwc1Id]; + ALOGV("Adding release fence %d to layer %" PRIu64, + receivedLayer.releaseFenceFd, layer.getId()); + layer.addReleaseFence(receivedLayer.releaseFenceFd); + } +} + +bool HWC2On1Adapter::Display::hasColorTransform() const { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + return mHasColorTransform; +} + +static std::string hwc1CompositionString(int32_t type) { + switch (type) { + case HWC_FRAMEBUFFER: return "Framebuffer"; + case HWC_OVERLAY: return "Overlay"; + case HWC_BACKGROUND: return "Background"; + case HWC_FRAMEBUFFER_TARGET: return "FramebufferTarget"; + case HWC_SIDEBAND: return "Sideband"; + case HWC_CURSOR_OVERLAY: return "CursorOverlay"; + default: + return std::string("Unknown (") + std::to_string(type) + ")"; + } +} + +static std::string hwc1TransformString(int32_t transform) { + switch (transform) { + case 0: return "None"; + case HWC_TRANSFORM_FLIP_H: return "FlipH"; + case HWC_TRANSFORM_FLIP_V: return "FlipV"; + case HWC_TRANSFORM_ROT_90: return "Rotate90"; + case HWC_TRANSFORM_ROT_180: return "Rotate180"; + case HWC_TRANSFORM_ROT_270: return "Rotate270"; + case HWC_TRANSFORM_FLIP_H_ROT_90: return "FlipHRotate90"; + case HWC_TRANSFORM_FLIP_V_ROT_90: return "FlipVRotate90"; + default: + return std::string("Unknown (") + std::to_string(transform) + ")"; + } +} + +static std::string hwc1BlendModeString(int32_t mode) { + switch (mode) { + case HWC_BLENDING_NONE: return "None"; + case HWC_BLENDING_PREMULT: return "Premultiplied"; + case HWC_BLENDING_COVERAGE: return "Coverage"; + default: + return std::string("Unknown (") + std::to_string(mode) + ")"; + } +} + +static std::string rectString(hwc_rect_t rect) { + std::stringstream output; + output << "[" << rect.left << ", " << rect.top << ", "; + output << rect.right << ", " << rect.bottom << "]"; + return output.str(); +} + +static std::string approximateFloatString(float f) { + if (static_cast<int32_t>(f) == f) { + return std::to_string(static_cast<int32_t>(f)); + } + int32_t truncated = static_cast<int32_t>(f * 10); + bool approximate = (static_cast<float>(truncated) != f * 10); + const size_t BUFFER_SIZE = 32; + char buffer[BUFFER_SIZE] = {}; + auto bytesWritten = snprintf(buffer, BUFFER_SIZE, + "%s%.1f", approximate ? "~" : "", f); + return std::string(buffer, bytesWritten); +} + +static std::string frectString(hwc_frect_t frect) { + std::stringstream output; + output << "[" << approximateFloatString(frect.left) << ", "; + output << approximateFloatString(frect.top) << ", "; + output << approximateFloatString(frect.right) << ", "; + output << approximateFloatString(frect.bottom) << "]"; + return output.str(); +} + +static std::string colorString(hwc_color_t color) { + std::stringstream output; + output << "RGBA ["; + output << static_cast<int32_t>(color.r) << ", "; + output << static_cast<int32_t>(color.g) << ", "; + output << static_cast<int32_t>(color.b) << ", "; + output << static_cast<int32_t>(color.a) << "]"; + return output.str(); +} + +static std::string alphaString(float f) { + const size_t BUFFER_SIZE = 8; + char buffer[BUFFER_SIZE] = {}; + auto bytesWritten = snprintf(buffer, BUFFER_SIZE, "%.3f", f); + return std::string(buffer, bytesWritten); +} + +static std::string to_string(const hwc_layer_1_t& hwcLayer, + int32_t hwc1MinorVersion) { + const char* fill = " "; + + std::stringstream output; + + output << " Composition: " << + hwc1CompositionString(hwcLayer.compositionType); + + if (hwcLayer.compositionType == HWC_BACKGROUND) { + output << " Color: " << colorString(hwcLayer.backgroundColor) << '\n'; + } else if (hwcLayer.compositionType == HWC_SIDEBAND) { + output << " Stream: " << hwcLayer.sidebandStream << '\n'; + } else { + output << " Buffer: " << hwcLayer.handle << "/" << + hwcLayer.acquireFenceFd << '\n'; + } + + output << fill << "Display frame: " << rectString(hwcLayer.displayFrame) << + '\n'; + + output << fill << "Source crop: "; + if (hwc1MinorVersion >= 3) { + output << frectString(hwcLayer.sourceCropf) << '\n'; + } else { + output << rectString(hwcLayer.sourceCropi) << '\n'; + } + + output << fill << "Transform: " << hwc1TransformString(hwcLayer.transform); + output << " Blend mode: " << hwc1BlendModeString(hwcLayer.blending); + if (hwcLayer.planeAlpha != 0xFF) { + output << " Alpha: " << alphaString(hwcLayer.planeAlpha / 255.0f); + } + output << '\n'; + + if (hwcLayer.hints != 0) { + output << fill << "Hints:"; + if ((hwcLayer.hints & HWC_HINT_TRIPLE_BUFFER) != 0) { + output << " TripleBuffer"; + } + if ((hwcLayer.hints & HWC_HINT_CLEAR_FB) != 0) { + output << " ClearFB"; + } + output << '\n'; + } + + if (hwcLayer.flags != 0) { + output << fill << "Flags:"; + if ((hwcLayer.flags & HWC_SKIP_LAYER) != 0) { + output << " SkipLayer"; + } + if ((hwcLayer.flags & HWC_IS_CURSOR_LAYER) != 0) { + output << " IsCursorLayer"; + } + output << '\n'; + } + + return output.str(); +} + +static std::string to_string(const hwc_display_contents_1_t& hwcContents, + int32_t hwc1MinorVersion) { + const char* fill = " "; + + std::stringstream output; + output << fill << "Geometry changed: " << + ((hwcContents.flags & HWC_GEOMETRY_CHANGED) != 0 ? "Y\n" : "N\n"); + + output << fill << hwcContents.numHwLayers << " Layer" << + ((hwcContents.numHwLayers == 1) ? "\n" : "s\n"); + for (size_t layer = 0; layer < hwcContents.numHwLayers; ++layer) { + output << fill << " Layer " << layer; + output << to_string(hwcContents.hwLayers[layer], hwc1MinorVersion); + } + + if (hwcContents.outbuf != nullptr) { + output << fill << "Output buffer: " << hwcContents.outbuf << "/" << + hwcContents.outbufAcquireFenceFd << '\n'; + } + + return output.str(); +} + +std::string HWC2On1Adapter::Display::dump() const { + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + std::stringstream output; + + output << " Display " << mId << ": "; + output << to_string(mType) << " "; + output << "HWC1 ID: " << mHwc1Id << " "; + output << "Power mode: " << to_string(mPowerMode) << " "; + output << "Vsync: " << to_string(mVsyncEnabled) << '\n'; + + output << " Color modes [active]:"; + for (const auto& mode : mColorModes) { + if (mode == mActiveColorMode) { + output << " [" << mode << ']'; + } else { + output << " " << mode; + } + } + output << '\n'; + + output << " " << mConfigs.size() << " Config" << + (mConfigs.size() == 1 ? "" : "s") << " (* active)\n"; + for (const auto& config : mConfigs) { + output << (config == mActiveConfig ? " * " : " "); + output << config->toString(true) << '\n'; + } + + output << " " << mLayers.size() << " Layer" << + (mLayers.size() == 1 ? "" : "s") << '\n'; + for (const auto& layer : mLayers) { + output << layer->dump(); + } + + output << " Client target: " << mClientTarget.getBuffer() << '\n'; + + if (mOutputBuffer.getBuffer() != nullptr) { + output << " Output buffer: " << mOutputBuffer.getBuffer() << '\n'; + } + + if (mHwc1RequestedContents) { + output << " Last requested HWC1 state\n"; + output << to_string(*mHwc1RequestedContents, mDevice.mHwc1MinorVersion); + } + + return output.str(); +} + +hwc_rect_t* HWC2On1Adapter::Display::GetRects(size_t numRects) { + if (numRects == 0) { + return nullptr; + } + + if (numRects > mNumAvailableRects) { + // This should NEVER happen since we calculated how many rects the + // display would need. + ALOGE("Rect allocation failure! SF is likely to crash soon!"); + return nullptr; + + } + hwc_rect_t* rects = mNextAvailableRect; + mNextAvailableRect += numRects; + mNumAvailableRects -= numRects; + return rects; +} + +hwc_display_contents_1* HWC2On1Adapter::Display::getDisplayContents() { + return mHwc1RequestedContents.get(); +} + +void HWC2On1Adapter::Display::Config::setAttribute(HWC2::Attribute attribute, + int32_t value) { + mAttributes[attribute] = value; +} + +int32_t HWC2On1Adapter::Display::Config::getAttribute(Attribute attribute) const { + if (mAttributes.count(attribute) == 0) { + return -1; + } + return mAttributes.at(attribute); +} + +void HWC2On1Adapter::Display::Config::setHwc1Id(uint32_t id) { + android_color_mode_t colorMode = static_cast<android_color_mode_t>(getAttribute(ColorMode)); + mHwc1Ids.emplace(colorMode, id); +} + +bool HWC2On1Adapter::Display::Config::hasHwc1Id(uint32_t id) const { + for (const auto& idPair : mHwc1Ids) { + if (id == idPair.second) { + return true; + } + } + return false; +} + +Error HWC2On1Adapter::Display::Config::getColorModeForHwc1Id( + uint32_t id, android_color_mode_t* outMode) const { + for (const auto& idPair : mHwc1Ids) { + if (id == idPair.second) { + *outMode = idPair.first; + return Error::None; + } + } + ALOGE("Unable to find color mode for HWC ID %" PRIu32 " on config %u", id, mId); + return Error::BadParameter; +} + +Error HWC2On1Adapter::Display::Config::getHwc1IdForColorMode(android_color_mode_t mode, + uint32_t* outId) const { + for (const auto& idPair : mHwc1Ids) { + if (mode == idPair.first) { + *outId = idPair.second; + return Error::None; + } + } + ALOGE("Unable to find HWC1 ID for color mode %d on config %u", mode, mId); + return Error::BadParameter; +} + +bool HWC2On1Adapter::Display::Config::merge(const Config& other) { + auto attributes = {HWC2::Attribute::Width, HWC2::Attribute::Height, + HWC2::Attribute::VsyncPeriod, HWC2::Attribute::DpiX, + HWC2::Attribute::DpiY}; + for (auto attribute : attributes) { + if (getAttribute(attribute) != other.getAttribute(attribute)) { + return false; + } + } + android_color_mode_t otherColorMode = + static_cast<android_color_mode_t>(other.getAttribute(ColorMode)); + if (mHwc1Ids.count(otherColorMode) != 0) { + ALOGE("Attempted to merge two configs (%u and %u) which appear to be " + "identical", mHwc1Ids.at(otherColorMode), + other.mHwc1Ids.at(otherColorMode)); + return false; + } + mHwc1Ids.emplace(otherColorMode, + other.mHwc1Ids.at(otherColorMode)); + return true; +} + +std::set<android_color_mode_t> HWC2On1Adapter::Display::Config::getColorModes() const { + std::set<android_color_mode_t> colorModes; + for (const auto& idPair : mHwc1Ids) { + colorModes.emplace(idPair.first); + } + return colorModes; +} + +std::string HWC2On1Adapter::Display::Config::toString(bool splitLine) const { + std::string output; + + const size_t BUFFER_SIZE = 100; + char buffer[BUFFER_SIZE] = {}; + auto writtenBytes = snprintf(buffer, BUFFER_SIZE, + "%u x %u", mAttributes.at(HWC2::Attribute::Width), + mAttributes.at(HWC2::Attribute::Height)); + output.append(buffer, writtenBytes); + + if (mAttributes.count(HWC2::Attribute::VsyncPeriod) != 0) { + std::memset(buffer, 0, BUFFER_SIZE); + writtenBytes = snprintf(buffer, BUFFER_SIZE, " @ %.1f Hz", + 1e9 / mAttributes.at(HWC2::Attribute::VsyncPeriod)); + output.append(buffer, writtenBytes); + } + + if (mAttributes.count(HWC2::Attribute::DpiX) != 0 && + mAttributes.at(HWC2::Attribute::DpiX) != -1) { + std::memset(buffer, 0, BUFFER_SIZE); + writtenBytes = snprintf(buffer, BUFFER_SIZE, + ", DPI: %.1f x %.1f", + mAttributes.at(HWC2::Attribute::DpiX) / 1000.0f, + mAttributes.at(HWC2::Attribute::DpiY) / 1000.0f); + output.append(buffer, writtenBytes); + } + + std::memset(buffer, 0, BUFFER_SIZE); + if (splitLine) { + writtenBytes = snprintf(buffer, BUFFER_SIZE, + "\n HWC1 ID/Color transform:"); + } else { + writtenBytes = snprintf(buffer, BUFFER_SIZE, + ", HWC1 ID/Color transform:"); + } + output.append(buffer, writtenBytes); + + + for (const auto& id : mHwc1Ids) { + android_color_mode_t colorMode = id.first; + uint32_t hwc1Id = id.second; + std::memset(buffer, 0, BUFFER_SIZE); + if (colorMode == mDisplay.mActiveColorMode) { + writtenBytes = snprintf(buffer, BUFFER_SIZE, " [%u/%d]", hwc1Id, + colorMode); + } else { + writtenBytes = snprintf(buffer, BUFFER_SIZE, " %u/%d", hwc1Id, + colorMode); + } + output.append(buffer, writtenBytes); + } + + return output; +} + +std::shared_ptr<const HWC2On1Adapter::Display::Config> + HWC2On1Adapter::Display::getConfig(hwc2_config_t configId) const { + if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) { + return nullptr; + } + return mConfigs[configId]; +} + +void HWC2On1Adapter::Display::populateColorModes() { + mColorModes = mConfigs[0]->getColorModes(); + for (const auto& config : mConfigs) { + std::set<android_color_mode_t> intersection; + auto configModes = config->getColorModes(); + std::set_intersection(mColorModes.cbegin(), mColorModes.cend(), + configModes.cbegin(), configModes.cend(), + std::inserter(intersection, intersection.begin())); + std::swap(intersection, mColorModes); + } +} + +void HWC2On1Adapter::Display::initializeActiveConfig() { + if (mDevice.mHwc1Device->getActiveConfig == nullptr) { + ALOGV("getActiveConfig is null, choosing config 0"); + mActiveConfig = mConfigs[0]; + mActiveColorMode = HAL_COLOR_MODE_NATIVE; + return; + } + + auto activeConfig = mDevice.mHwc1Device->getActiveConfig( + mDevice.mHwc1Device, mHwc1Id); + + // Some devices startup without an activeConfig: + // We need to set one ourselves. + if (activeConfig == HWC_ERROR) { + ALOGV("There is no active configuration: Picking the first one: 0."); + const int defaultIndex = 0; + mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device, mHwc1Id, defaultIndex); + activeConfig = defaultIndex; + } + + for (const auto& config : mConfigs) { + if (config->hasHwc1Id(activeConfig)) { + ALOGE("Setting active config to %d for HWC1 config %u", config->getId(), activeConfig); + mActiveConfig = config; + if (config->getColorModeForHwc1Id(activeConfig, &mActiveColorMode) != Error::None) { + // This should never happen since we checked for the config's presence before + // setting it as active. + ALOGE("Unable to find color mode for active HWC1 config %d", config->getId()); + mActiveColorMode = HAL_COLOR_MODE_NATIVE; + } + break; + } + } + if (!mActiveConfig) { + ALOGV("Unable to find active HWC1 config %u, defaulting to " + "config 0", activeConfig); + mActiveConfig = mConfigs[0]; + mActiveColorMode = HAL_COLOR_MODE_NATIVE; + } + + + + +} + +void HWC2On1Adapter::Display::allocateRequestedContents() { + // What needs to be allocated: + // 1 hwc_display_contents_1_t + // 1 hwc_layer_1_t for each layer + // 1 hwc_rect_t for each layer's surfaceDamage + // 1 hwc_rect_t for each layer's visibleRegion + // 1 hwc_layer_1_t for the framebuffer + // 1 hwc_rect_t for the framebuffer's visibleRegion + + // Count # of surfaceDamage + size_t numSurfaceDamages = 0; + for (const auto& layer : mLayers) { + numSurfaceDamages += layer->getNumSurfaceDamages(); + } + + // Count # of visibleRegions (start at 1 for mandatory framebuffer target + // region) + size_t numVisibleRegion = 1; + for (const auto& layer : mLayers) { + numVisibleRegion += layer->getNumVisibleRegions(); + } + + size_t numRects = numVisibleRegion + numSurfaceDamages; + auto numLayers = mLayers.size() + 1; + size_t size = sizeof(hwc_display_contents_1_t) + + sizeof(hwc_layer_1_t) * numLayers + + sizeof(hwc_rect_t) * numRects; + auto contents = static_cast<hwc_display_contents_1_t*>(std::calloc(size, 1)); + mHwc1RequestedContents.reset(contents); + mNextAvailableRect = reinterpret_cast<hwc_rect_t*>(&contents->hwLayers[numLayers]); + mNumAvailableRects = numRects; +} + +void HWC2On1Adapter::Display::assignHwc1LayerIds() { + mHwc1LayerMap.clear(); + size_t nextHwc1Id = 0; + for (auto& layer : mLayers) { + mHwc1LayerMap[nextHwc1Id] = layer; + layer->setHwc1Id(nextHwc1Id++); + } +} + +void HWC2On1Adapter::Display::updateTypeChanges(const hwc_layer_1_t& hwc1Layer, + const Layer& layer) { + auto layerId = layer.getId(); + switch (hwc1Layer.compositionType) { + case HWC_FRAMEBUFFER: + if (layer.getCompositionType() != Composition::Client) { + mChanges->addTypeChange(layerId, Composition::Client); + } + break; + case HWC_OVERLAY: + if (layer.getCompositionType() != Composition::Device) { + mChanges->addTypeChange(layerId, Composition::Device); + } + break; + case HWC_BACKGROUND: + ALOGE_IF(layer.getCompositionType() != Composition::SolidColor, + "updateTypeChanges: HWC1 requested BACKGROUND, but HWC2" + " wasn't expecting SolidColor"); + break; + case HWC_FRAMEBUFFER_TARGET: + // Do nothing, since it shouldn't be modified by HWC1 + break; + case HWC_SIDEBAND: + ALOGE_IF(layer.getCompositionType() != Composition::Sideband, + "updateTypeChanges: HWC1 requested SIDEBAND, but HWC2" + " wasn't expecting Sideband"); + break; + case HWC_CURSOR_OVERLAY: + ALOGE_IF(layer.getCompositionType() != Composition::Cursor, + "updateTypeChanges: HWC1 requested CURSOR_OVERLAY, but" + " HWC2 wasn't expecting Cursor"); + break; + } +} + +void HWC2On1Adapter::Display::updateLayerRequests( + const hwc_layer_1_t& hwc1Layer, const Layer& layer) { + if ((hwc1Layer.hints & HWC_HINT_CLEAR_FB) != 0) { + mChanges->addLayerRequest(layer.getId(), + LayerRequest::ClearClientTarget); + } +} + +void HWC2On1Adapter::Display::prepareFramebufferTarget() { + // We check that mActiveConfig is valid in Display::prepare + int32_t width = mActiveConfig->getAttribute(Attribute::Width); + int32_t height = mActiveConfig->getAttribute(Attribute::Height); + + auto& hwc1Target = mHwc1RequestedContents->hwLayers[mLayers.size()]; + hwc1Target.compositionType = HWC_FRAMEBUFFER_TARGET; + hwc1Target.releaseFenceFd = -1; + hwc1Target.hints = 0; + hwc1Target.flags = 0; + hwc1Target.transform = 0; + hwc1Target.blending = HWC_BLENDING_PREMULT; + if (mDevice.getHwc1MinorVersion() < 3) { + hwc1Target.sourceCropi = {0, 0, width, height}; + } else { + hwc1Target.sourceCropf = {0.0f, 0.0f, static_cast<float>(width), + static_cast<float>(height)}; + } + hwc1Target.displayFrame = {0, 0, width, height}; + hwc1Target.planeAlpha = 255; + + hwc1Target.visibleRegionScreen.numRects = 1; + hwc_rect_t* rects = GetRects(1); + rects[0].left = 0; + rects[0].top = 0; + rects[0].right = width; + rects[0].bottom = height; + hwc1Target.visibleRegionScreen.rects = rects; + + // We will set this to the correct value in set + hwc1Target.acquireFenceFd = -1; +} + +// Layer functions + +std::atomic<hwc2_layer_t> HWC2On1Adapter::Layer::sNextId(1); + +HWC2On1Adapter::Layer::Layer(Display& display) + : mId(sNextId++), + mDisplay(display), + mBuffer(), + mSurfaceDamage(), + mBlendMode(BlendMode::None), + mColor({0, 0, 0, 0}), + mCompositionType(Composition::Invalid), + mDisplayFrame({0, 0, -1, -1}), + mPlaneAlpha(0.0f), + mSidebandStream(nullptr), + mSourceCrop({0.0f, 0.0f, -1.0f, -1.0f}), + mTransform(Transform::None), + mVisibleRegion(), + mZ(0), + mReleaseFence(), + mHwc1Id(0), + mHasUnsupportedPlaneAlpha(false) {} + +bool HWC2On1Adapter::SortLayersByZ::operator()( + const std::shared_ptr<Layer>& lhs, const std::shared_ptr<Layer>& rhs) { + return lhs->getZ() < rhs->getZ(); +} + +Error HWC2On1Adapter::Layer::setBuffer(buffer_handle_t buffer, + int32_t acquireFence) { + ALOGV("Setting acquireFence to %d for layer %" PRIu64, acquireFence, mId); + mBuffer.setBuffer(buffer); + mBuffer.setFence(acquireFence); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setCursorPosition(int32_t x, int32_t y) { + if (mCompositionType != Composition::Cursor) { + return Error::BadLayer; + } + + if (mDisplay.hasChanges()) { + return Error::NotValidated; + } + + auto displayId = mDisplay.getHwc1Id(); + auto hwc1Device = mDisplay.getDevice().getHwc1Device(); + hwc1Device->setCursorPositionAsync(hwc1Device, displayId, x, y); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setSurfaceDamage(hwc_region_t damage) { + // HWC1 supports surface damage starting only with version 1.5. + if (mDisplay.getDevice().mHwc1MinorVersion < 5) { + return Error::None; + } + mSurfaceDamage.resize(damage.numRects); + std::copy_n(damage.rects, damage.numRects, mSurfaceDamage.begin()); + return Error::None; +} + +// Layer state functions + +Error HWC2On1Adapter::Layer::setBlendMode(BlendMode mode) { + mBlendMode = mode; + mDisplay.markGeometryChanged(); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setColor(hwc_color_t color) { + mColor = color; + mDisplay.markGeometryChanged(); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setCompositionType(Composition type) { + mCompositionType = type; + mDisplay.markGeometryChanged(); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setDataspace(android_dataspace_t) { + return Error::None; +} + +Error HWC2On1Adapter::Layer::setDisplayFrame(hwc_rect_t frame) { + mDisplayFrame = frame; + mDisplay.markGeometryChanged(); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setPlaneAlpha(float alpha) { + mPlaneAlpha = alpha; + mDisplay.markGeometryChanged(); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setSidebandStream(const native_handle_t* stream) { + mSidebandStream = stream; + mDisplay.markGeometryChanged(); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setSourceCrop(hwc_frect_t crop) { + mSourceCrop = crop; + mDisplay.markGeometryChanged(); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setTransform(Transform transform) { + mTransform = transform; + mDisplay.markGeometryChanged(); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setVisibleRegion(hwc_region_t visible) { + mVisibleRegion.resize(visible.numRects); + std::copy_n(visible.rects, visible.numRects, mVisibleRegion.begin()); + mDisplay.markGeometryChanged(); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setZ(uint32_t z) { + mZ = z; + return Error::None; +} + +void HWC2On1Adapter::Layer::addReleaseFence(int fenceFd) { + ALOGV("addReleaseFence %d to layer %" PRIu64, fenceFd, mId); + mReleaseFence.add(fenceFd); +} + +const sp<MiniFence>& HWC2On1Adapter::Layer::getReleaseFence() const { + return mReleaseFence.get(); +} + +void HWC2On1Adapter::Layer::applyState(hwc_layer_1_t& hwc1Layer) { + applyCommonState(hwc1Layer); + applyCompositionType(hwc1Layer); + switch (mCompositionType) { + case Composition::SolidColor : applySolidColorState(hwc1Layer); break; + case Composition::Sideband : applySidebandState(hwc1Layer); break; + default: applyBufferState(hwc1Layer); break; + } +} + +static std::string regionStrings(const std::vector<hwc_rect_t>& visibleRegion, + const std::vector<hwc_rect_t>& surfaceDamage) { + std::string regions; + regions += " Visible Region"; + regions.resize(40, ' '); + regions += "Surface Damage\n"; + + size_t numPrinted = 0; + size_t maxSize = std::max(visibleRegion.size(), surfaceDamage.size()); + while (numPrinted < maxSize) { + std::string line(" "); + if (visibleRegion.empty() && numPrinted == 0) { + line += "None"; + } else if (numPrinted < visibleRegion.size()) { + line += rectString(visibleRegion[numPrinted]); + } + line.resize(40, ' '); + if (surfaceDamage.empty() && numPrinted == 0) { + line += "None"; + } else if (numPrinted < surfaceDamage.size()) { + line += rectString(surfaceDamage[numPrinted]); + } + line += '\n'; + regions += line; + ++numPrinted; + } + return regions; +} + +std::string HWC2On1Adapter::Layer::dump() const { + std::stringstream output; + const char* fill = " "; + + output << fill << to_string(mCompositionType); + output << " Layer HWC2/1: " << mId << "/" << mHwc1Id << " "; + output << "Z: " << mZ; + if (mCompositionType == HWC2::Composition::SolidColor) { + output << " " << colorString(mColor); + } else if (mCompositionType == HWC2::Composition::Sideband) { + output << " Handle: " << mSidebandStream << '\n'; + } else { + output << " Buffer: " << mBuffer.getBuffer() << "/" << + mBuffer.getFence() << '\n'; + output << fill << " Display frame [LTRB]: " << + rectString(mDisplayFrame) << '\n'; + output << fill << " Source crop: " << + frectString(mSourceCrop) << '\n'; + output << fill << " Transform: " << to_string(mTransform); + output << " Blend mode: " << to_string(mBlendMode); + if (mPlaneAlpha != 1.0f) { + output << " Alpha: " << + alphaString(mPlaneAlpha) << '\n'; + } else { + output << '\n'; + } + output << regionStrings(mVisibleRegion, mSurfaceDamage); + } + return output.str(); +} + +static int getHwc1Blending(HWC2::BlendMode blendMode) { + switch (blendMode) { + case BlendMode::Coverage: return HWC_BLENDING_COVERAGE; + case BlendMode::Premultiplied: return HWC_BLENDING_PREMULT; + default: return HWC_BLENDING_NONE; + } +} + +void HWC2On1Adapter::Layer::applyCommonState(hwc_layer_1_t& hwc1Layer) { + auto minorVersion = mDisplay.getDevice().getHwc1MinorVersion(); + hwc1Layer.blending = getHwc1Blending(mBlendMode); + hwc1Layer.displayFrame = mDisplayFrame; + + auto pendingAlpha = mPlaneAlpha; + if (minorVersion < 2) { + mHasUnsupportedPlaneAlpha = pendingAlpha < 1.0f; + } else { + hwc1Layer.planeAlpha = + static_cast<uint8_t>(255.0f * pendingAlpha + 0.5f); + } + + if (minorVersion < 3) { + auto pending = mSourceCrop; + hwc1Layer.sourceCropi.left = + static_cast<int32_t>(std::ceil(pending.left)); + hwc1Layer.sourceCropi.top = + static_cast<int32_t>(std::ceil(pending.top)); + hwc1Layer.sourceCropi.right = + static_cast<int32_t>(std::floor(pending.right)); + hwc1Layer.sourceCropi.bottom = + static_cast<int32_t>(std::floor(pending.bottom)); + } else { + hwc1Layer.sourceCropf = mSourceCrop; + } + + hwc1Layer.transform = static_cast<uint32_t>(mTransform); + + auto& hwc1VisibleRegion = hwc1Layer.visibleRegionScreen; + hwc1VisibleRegion.numRects = mVisibleRegion.size(); + hwc_rect_t* rects = mDisplay.GetRects(hwc1VisibleRegion.numRects); + hwc1VisibleRegion.rects = rects; + for (size_t i = 0; i < mVisibleRegion.size(); i++) { + rects[i] = mVisibleRegion[i]; + } +} + +void HWC2On1Adapter::Layer::applySolidColorState(hwc_layer_1_t& hwc1Layer) { + // If the device does not support background color it is likely to make + // assumption regarding backgroundColor and handle (both fields occupy + // the same location in hwc_layer_1_t union). + // To not confuse these devices we don't set background color and we + // make sure handle is a null pointer. + if (hasUnsupportedBackgroundColor()) { + hwc1Layer.handle = nullptr; + } else { + hwc1Layer.backgroundColor = mColor; + } +} + +void HWC2On1Adapter::Layer::applySidebandState(hwc_layer_1_t& hwc1Layer) { + hwc1Layer.sidebandStream = mSidebandStream; +} + +void HWC2On1Adapter::Layer::applyBufferState(hwc_layer_1_t& hwc1Layer) { + hwc1Layer.handle = mBuffer.getBuffer(); + hwc1Layer.acquireFenceFd = mBuffer.getFence(); +} + +void HWC2On1Adapter::Layer::applyCompositionType(hwc_layer_1_t& hwc1Layer) { + // HWC1 never supports color transforms or dataspaces and only sometimes + // supports plane alpha (depending on the version). These require us to drop + // some or all layers to client composition. + if (mHasUnsupportedPlaneAlpha || mDisplay.hasColorTransform() || + hasUnsupportedBackgroundColor()) { + hwc1Layer.compositionType = HWC_FRAMEBUFFER; + hwc1Layer.flags = HWC_SKIP_LAYER; + return; + } + + hwc1Layer.flags = 0; + switch (mCompositionType) { + case Composition::Client: + hwc1Layer.compositionType = HWC_FRAMEBUFFER; + hwc1Layer.flags |= HWC_SKIP_LAYER; + break; + case Composition::Device: + hwc1Layer.compositionType = HWC_FRAMEBUFFER; + break; + case Composition::SolidColor: + // In theory the following line should work, but since the HWC1 + // version of SurfaceFlinger never used HWC_BACKGROUND, HWC1 + // devices may not work correctly. To be on the safe side, we + // fall back to client composition. + // + // hwc1Layer.compositionType = HWC_BACKGROUND; + hwc1Layer.compositionType = HWC_FRAMEBUFFER; + hwc1Layer.flags |= HWC_SKIP_LAYER; + break; + case Composition::Cursor: + hwc1Layer.compositionType = HWC_FRAMEBUFFER; + if (mDisplay.getDevice().getHwc1MinorVersion() >= 4) { + hwc1Layer.hints |= HWC_IS_CURSOR_LAYER; + } + break; + case Composition::Sideband: + if (mDisplay.getDevice().getHwc1MinorVersion() < 4) { + hwc1Layer.compositionType = HWC_SIDEBAND; + } else { + hwc1Layer.compositionType = HWC_FRAMEBUFFER; + hwc1Layer.flags |= HWC_SKIP_LAYER; + } + break; + default: + hwc1Layer.compositionType = HWC_FRAMEBUFFER; + hwc1Layer.flags |= HWC_SKIP_LAYER; + break; + } + ALOGV("Layer %" PRIu64 " %s set to %d", mId, + to_string(mCompositionType).c_str(), + hwc1Layer.compositionType); + ALOGV_IF(hwc1Layer.flags & HWC_SKIP_LAYER, " and skipping"); +} + +// Adapter helpers + +void HWC2On1Adapter::populateCapabilities() { + if (mHwc1MinorVersion >= 3U) { + int supportedTypes = 0; + auto result = mHwc1Device->query(mHwc1Device, + HWC_DISPLAY_TYPES_SUPPORTED, &supportedTypes); + if ((result == 0) && ((supportedTypes & HWC_DISPLAY_VIRTUAL_BIT) != 0)) { + ALOGI("Found support for HWC virtual displays"); + mHwc1SupportsVirtualDisplays = true; + } + } + if (mHwc1MinorVersion >= 4U) { + mCapabilities.insert(Capability::SidebandStream); + } + + // Check for HWC background color layer support. + if (mHwc1MinorVersion >= 1U) { + int backgroundColorSupported = 0; + auto result = mHwc1Device->query(mHwc1Device, + HWC_BACKGROUND_LAYER_SUPPORTED, + &backgroundColorSupported); + if ((result == 0) && (backgroundColorSupported == 1)) { + ALOGV("Found support for HWC background color"); + mHwc1SupportsBackgroundColor = true; + } + } + + // Some devices might have HWC1 retire fences that accurately emulate + // HWC2 present fences when they are deferred, but it's not very reliable. + // To be safe, we indicate PresentFenceIsNotReliable for all HWC1 devices. + mCapabilities.insert(Capability::PresentFenceIsNotReliable); +} + +HWC2On1Adapter::Display* HWC2On1Adapter::getDisplay(hwc2_display_t id) { + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + auto display = mDisplays.find(id); + if (display == mDisplays.end()) { + return nullptr; + } + + return display->second.get(); +} + +std::tuple<HWC2On1Adapter::Layer*, Error> HWC2On1Adapter::getLayer( + hwc2_display_t displayId, hwc2_layer_t layerId) { + auto display = getDisplay(displayId); + if (!display) { + return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadDisplay); + } + + auto layerEntry = mLayers.find(layerId); + if (layerEntry == mLayers.end()) { + return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer); + } + + auto layer = layerEntry->second; + if (layer->getDisplay().getId() != displayId) { + return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer); + } + return std::make_tuple(layer.get(), Error::None); +} + +void HWC2On1Adapter::populatePrimary() { + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + auto display = std::make_shared<Display>(*this, HWC2::DisplayType::Physical); + mHwc1DisplayMap[HWC_DISPLAY_PRIMARY] = display->getId(); + display->setHwc1Id(HWC_DISPLAY_PRIMARY); + display->populateConfigs(); + mDisplays.emplace(display->getId(), std::move(display)); +} + +bool HWC2On1Adapter::prepareAllDisplays() { + ATRACE_CALL(); + + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + for (const auto& displayPair : mDisplays) { + auto& display = displayPair.second; + if (!display->prepare()) { + return false; + } + } + + if (mHwc1DisplayMap.count(HWC_DISPLAY_PRIMARY) == 0) { + ALOGE("prepareAllDisplays: Unable to find primary HWC1 display"); + return false; + } + + // Build an array of hwc_display_contents_1 to call prepare() on HWC1. + mHwc1Contents.clear(); + + // Always push the primary display + auto primaryDisplayId = mHwc1DisplayMap[HWC_DISPLAY_PRIMARY]; + auto& primaryDisplay = mDisplays[primaryDisplayId]; + mHwc1Contents.push_back(primaryDisplay->getDisplayContents()); + + // Push the external display, if present + if (mHwc1DisplayMap.count(HWC_DISPLAY_EXTERNAL) != 0) { + auto externalDisplayId = mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL]; + auto& externalDisplay = mDisplays[externalDisplayId]; + mHwc1Contents.push_back(externalDisplay->getDisplayContents()); + } else { + // Even if an external display isn't present, we still need to send + // at least two displays down to HWC1 + mHwc1Contents.push_back(nullptr); + } + + // Push the hardware virtual display, if supported and present + if (mHwc1MinorVersion >= 3) { + if (mHwc1DisplayMap.count(HWC_DISPLAY_VIRTUAL) != 0) { + auto virtualDisplayId = mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL]; + auto& virtualDisplay = mDisplays[virtualDisplayId]; + mHwc1Contents.push_back(virtualDisplay->getDisplayContents()); + } else { + mHwc1Contents.push_back(nullptr); + } + } + + for (auto& displayContents : mHwc1Contents) { + if (!displayContents) { + continue; + } + + ALOGV("Display %zd layers:", mHwc1Contents.size() - 1); + for (size_t l = 0; l < displayContents->numHwLayers; ++l) { + auto& layer = displayContents->hwLayers[l]; + ALOGV(" %zd: %d", l, layer.compositionType); + } + } + + ALOGV("Calling HWC1 prepare"); + { + ATRACE_NAME("HWC1 prepare"); + mHwc1Device->prepare(mHwc1Device, mHwc1Contents.size(), + mHwc1Contents.data()); + } + + for (size_t c = 0; c < mHwc1Contents.size(); ++c) { + auto& contents = mHwc1Contents[c]; + if (!contents) { + continue; + } + ALOGV("Display %zd layers:", c); + for (size_t l = 0; l < contents->numHwLayers; ++l) { + ALOGV(" %zd: %d", l, contents->hwLayers[l].compositionType); + } + } + + // Return the received contents to their respective displays + for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) { + if (mHwc1Contents[hwc1Id] == nullptr) { + continue; + } + + auto displayId = mHwc1DisplayMap[hwc1Id]; + auto& display = mDisplays[displayId]; + display->generateChanges(); + } + + return true; +} + +void dumpHWC1Message(hwc_composer_device_1* device, size_t numDisplays, + hwc_display_contents_1_t** displays) { + ALOGV("*****************************"); + size_t displayId = 0; + while (displayId < numDisplays) { + hwc_display_contents_1_t* display = displays[displayId]; + + ALOGV("hwc_display_contents_1_t[%zu] @0x%p", displayId, display); + if (display == nullptr) { + displayId++; + continue; + } + ALOGV(" retirefd:0x%08x", display->retireFenceFd); + ALOGV(" outbuf :0x%p", display->outbuf); + ALOGV(" outbuffd:0x%08x", display->outbufAcquireFenceFd); + ALOGV(" flags :0x%08x", display->flags); + for(size_t layerId=0 ; layerId < display->numHwLayers ; layerId++) { + hwc_layer_1_t& layer = display->hwLayers[layerId]; + ALOGV(" Layer[%zu]:", layerId); + ALOGV(" composition : 0x%08x", layer.compositionType); + ALOGV(" hints : 0x%08x", layer.hints); + ALOGV(" flags : 0x%08x", layer.flags); + ALOGV(" handle : 0x%p", layer.handle); + ALOGV(" transform : 0x%08x", layer.transform); + ALOGV(" blending : 0x%08x", layer.blending); + ALOGV(" sourceCropf : %f, %f, %f, %f", + layer.sourceCropf.left, + layer.sourceCropf.top, + layer.sourceCropf.right, + layer.sourceCropf.bottom); + ALOGV(" displayFrame : %d, %d, %d, %d", + layer.displayFrame.left, + layer.displayFrame.left, + layer.displayFrame.left, + layer.displayFrame.left); + hwc_region_t& visReg = layer.visibleRegionScreen; + ALOGV(" visibleRegionScreen: #0x%08zx[@0x%p]", + visReg.numRects, + visReg.rects); + for (size_t visRegId=0; visRegId < visReg.numRects ; visRegId++) { + if (layer.visibleRegionScreen.rects == nullptr) { + ALOGV(" null"); + } else { + ALOGV(" visibleRegionScreen[%zu] %d, %d, %d, %d", + visRegId, + visReg.rects[visRegId].left, + visReg.rects[visRegId].top, + visReg.rects[visRegId].right, + visReg.rects[visRegId].bottom); + } + } + ALOGV(" acquireFenceFd : 0x%08x", layer.acquireFenceFd); + ALOGV(" releaseFenceFd : 0x%08x", layer.releaseFenceFd); + ALOGV(" planeAlpha : 0x%08x", layer.planeAlpha); + if (getMinorVersion(device) < 5) + continue; + ALOGV(" surfaceDamage : #0x%08zx[@0x%p]", + layer.surfaceDamage.numRects, + layer.surfaceDamage.rects); + for (size_t sdId=0; sdId < layer.surfaceDamage.numRects ; sdId++) { + if (layer.surfaceDamage.rects == nullptr) { + ALOGV(" null"); + } else { + ALOGV(" surfaceDamage[%zu] %d, %d, %d, %d", + sdId, + layer.surfaceDamage.rects[sdId].left, + layer.surfaceDamage.rects[sdId].top, + layer.surfaceDamage.rects[sdId].right, + layer.surfaceDamage.rects[sdId].bottom); + } + } + } + displayId++; + } + ALOGV("-----------------------------"); +} + +Error HWC2On1Adapter::setAllDisplays() { + ATRACE_CALL(); + + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + // Make sure we're ready to validate + for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) { + if (mHwc1Contents[hwc1Id] == nullptr) { + continue; + } + + auto displayId = mHwc1DisplayMap[hwc1Id]; + auto& display = mDisplays[displayId]; + Error error = display->set(*mHwc1Contents[hwc1Id]); + if (error != Error::None) { + ALOGE("setAllDisplays: Failed to set display %zd: %s", hwc1Id, + to_string(error).c_str()); + return error; + } + } + + ALOGV("Calling HWC1 set"); + { + ATRACE_NAME("HWC1 set"); + //dumpHWC1Message(mHwc1Device, mHwc1Contents.size(), mHwc1Contents.data()); + mHwc1Device->set(mHwc1Device, mHwc1Contents.size(), + mHwc1Contents.data()); + } + + // Add retire and release fences + for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) { + if (mHwc1Contents[hwc1Id] == nullptr) { + continue; + } + + auto displayId = mHwc1DisplayMap[hwc1Id]; + auto& display = mDisplays[displayId]; + auto retireFenceFd = mHwc1Contents[hwc1Id]->retireFenceFd; + ALOGV("setAllDisplays: Adding retire fence %d to display %zd", + retireFenceFd, hwc1Id); + display->addRetireFence(mHwc1Contents[hwc1Id]->retireFenceFd); + display->addReleaseFences(*mHwc1Contents[hwc1Id]); + } + + return Error::None; +} + +void HWC2On1Adapter::hwc1Invalidate() { + ALOGV("Received hwc1Invalidate"); + + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + // If the HWC2-side callback hasn't been registered yet, buffer this until + // it is registered. + if (mCallbacks.count(Callback::Refresh) == 0) { + mHasPendingInvalidate = true; + return; + } + + const auto& callbackInfo = mCallbacks[Callback::Refresh]; + std::vector<hwc2_display_t> displays; + for (const auto& displayPair : mDisplays) { + displays.emplace_back(displayPair.first); + } + + // Call back without the state lock held. + lock.unlock(); + + auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(callbackInfo.pointer); + for (auto display : displays) { + refresh(callbackInfo.data, display); + } +} + +void HWC2On1Adapter::hwc1Vsync(int hwc1DisplayId, int64_t timestamp) { + ALOGV("Received hwc1Vsync(%d, %" PRId64 ")", hwc1DisplayId, timestamp); + + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + // If the HWC2-side callback hasn't been registered yet, buffer this until + // it is registered. + if (mCallbacks.count(Callback::Vsync) == 0) { + mPendingVsyncs.emplace_back(hwc1DisplayId, timestamp); + return; + } + + if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) { + ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d", hwc1DisplayId); + return; + } + + const auto& callbackInfo = mCallbacks[Callback::Vsync]; + auto displayId = mHwc1DisplayMap[hwc1DisplayId]; + + // Call back without the state lock held. + lock.unlock(); + + auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(callbackInfo.pointer); + vsync(callbackInfo.data, displayId, timestamp); +} + +void HWC2On1Adapter::hwc1Hotplug(int hwc1DisplayId, int connected) { + ALOGV("Received hwc1Hotplug(%d, %d)", hwc1DisplayId, connected); + + if (hwc1DisplayId != HWC_DISPLAY_EXTERNAL) { + ALOGE("hwc1Hotplug: Received hotplug for non-external display"); + return; + } + + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + // If the HWC2-side callback hasn't been registered yet, buffer this until + // it is registered + if (mCallbacks.count(Callback::Hotplug) == 0) { + mPendingHotplugs.emplace_back(hwc1DisplayId, connected); + return; + } + + hwc2_display_t displayId = UINT64_MAX; + if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) { + if (connected == 0) { + ALOGW("hwc1Hotplug: Received disconnect for unconnected display"); + return; + } + + // Create a new display on connect + auto display = std::make_shared<HWC2On1Adapter::Display>(*this, + HWC2::DisplayType::Physical); + display->setHwc1Id(HWC_DISPLAY_EXTERNAL); + display->populateConfigs(); + displayId = display->getId(); + mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL] = displayId; + mDisplays.emplace(displayId, std::move(display)); + } else { + if (connected != 0) { + ALOGW("hwc1Hotplug: Received connect for previously connected " + "display"); + return; + } + + // Disconnect an existing display + displayId = mHwc1DisplayMap[hwc1DisplayId]; + mHwc1DisplayMap.erase(HWC_DISPLAY_EXTERNAL); + mDisplays.erase(displayId); + } + + const auto& callbackInfo = mCallbacks[Callback::Hotplug]; + + // Call back without the state lock held + lock.unlock(); + + auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(callbackInfo.pointer); + auto hwc2Connected = (connected == 0) ? + HWC2::Connection::Disconnected : HWC2::Connection::Connected; + hotplug(callbackInfo.data, displayId, static_cast<int32_t>(hwc2Connected)); +} +} // namespace android diff --git a/libs/hwc2on1adapter/MiniFence.cpp b/libs/hwc2on1adapter/MiniFence.cpp new file mode 100644 index 0000000000..dfbe4d63cc --- /dev/null +++ b/libs/hwc2on1adapter/MiniFence.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "hwc2on1adapter/MiniFence.h" + +#include <unistd.h> + +namespace android { + +const sp<MiniFence> MiniFence::NO_FENCE = sp<MiniFence>(new MiniFence); + +MiniFence::MiniFence() : + mFenceFd(-1) { +} + +MiniFence::MiniFence(int fenceFd) : + mFenceFd(fenceFd) { +} + +MiniFence::~MiniFence() { + if (mFenceFd != -1) { + close(mFenceFd); + } +} + +int MiniFence::dup() const { + return ::dup(mFenceFd); +} +} diff --git a/libs/hwc2on1adapter/include/hwc2on1adapter/HWC2On1Adapter.h b/libs/hwc2on1adapter/include/hwc2on1adapter/HWC2On1Adapter.h new file mode 100644 index 0000000000..3badfce078 --- /dev/null +++ b/libs/hwc2on1adapter/include/hwc2on1adapter/HWC2On1Adapter.h @@ -0,0 +1,738 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_SF_HWC2_ON_1_ADAPTER_H +#define ANDROID_SF_HWC2_ON_1_ADAPTER_H + +#define HWC2_INCLUDE_STRINGIFICATION +#define HWC2_USE_CPP11 +#include <hardware/hwcomposer2.h> +#undef HWC2_INCLUDE_STRINGIFICATION +#undef HWC2_USE_CPP11 + +#include "MiniFence.h" + +#include <atomic> +#include <map> +#include <mutex> +#include <queue> +#include <set> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +struct hwc_composer_device_1; +struct hwc_display_contents_1; +struct hwc_layer_1; + +namespace android { + +// For devices unable to provide an implementation of HWC2 (see hwcomposer2.h), +// we provide an adapter able to talk to HWC1 (see hwcomposer.h). It translates +// streamed function calls ala HWC2 model to batched array of structs calls ala +// HWC1 model. +class HWC2On1Adapter : public hwc2_device_t +{ +public: + explicit HWC2On1Adapter(struct hwc_composer_device_1* hwc1Device); + ~HWC2On1Adapter(); + + struct hwc_composer_device_1* getHwc1Device() const { return mHwc1Device; } + uint8_t getHwc1MinorVersion() const { return mHwc1MinorVersion; } + +private: + static inline HWC2On1Adapter* getAdapter(hwc2_device_t* device) { + return static_cast<HWC2On1Adapter*>(device); + } + + // getCapabilities + + void doGetCapabilities(uint32_t* outCount, + int32_t* /*hwc2_capability_t*/ outCapabilities); + static void getCapabilitiesHook(hwc2_device_t* device, uint32_t* outCount, + int32_t* /*hwc2_capability_t*/ outCapabilities) { + getAdapter(device)->doGetCapabilities(outCount, outCapabilities); + } + + bool supportsBackgroundColor() { + return mHwc1SupportsBackgroundColor; + } + + // getFunction + + hwc2_function_pointer_t doGetFunction(HWC2::FunctionDescriptor descriptor); + static hwc2_function_pointer_t getFunctionHook(hwc2_device_t* device, + int32_t intDesc) { + auto descriptor = static_cast<HWC2::FunctionDescriptor>(intDesc); + return getAdapter(device)->doGetFunction(descriptor); + } + + // Device functions + + HWC2::Error createVirtualDisplay(uint32_t width, uint32_t height, + hwc2_display_t* outDisplay); + static int32_t createVirtualDisplayHook(hwc2_device_t* device, + uint32_t width, uint32_t height, int32_t* /*format*/, + hwc2_display_t* outDisplay) { + // HWC1 implementations cannot override the buffer format requested by + // the consumer + auto error = getAdapter(device)->createVirtualDisplay(width, height, + outDisplay); + return static_cast<int32_t>(error); + } + + HWC2::Error destroyVirtualDisplay(hwc2_display_t display); + static int32_t destroyVirtualDisplayHook(hwc2_device_t* device, + hwc2_display_t display) { + auto error = getAdapter(device)->destroyVirtualDisplay(display); + return static_cast<int32_t>(error); + } + + std::string mDumpString; + void dump(uint32_t* outSize, char* outBuffer); + static void dumpHook(hwc2_device_t* device, uint32_t* outSize, + char* outBuffer) { + getAdapter(device)->dump(outSize, outBuffer); + } + + uint32_t getMaxVirtualDisplayCount(); + static uint32_t getMaxVirtualDisplayCountHook(hwc2_device_t* device) { + return getAdapter(device)->getMaxVirtualDisplayCount(); + } + + HWC2::Error registerCallback(HWC2::Callback descriptor, + hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer); + static int32_t registerCallbackHook(hwc2_device_t* device, + int32_t intDesc, hwc2_callback_data_t callbackData, + hwc2_function_pointer_t pointer) { + auto descriptor = static_cast<HWC2::Callback>(intDesc); + auto error = getAdapter(device)->registerCallback(descriptor, + callbackData, pointer); + return static_cast<int32_t>(error); + } + + // Display functions + + class Layer; + + class SortLayersByZ { + public: + bool operator()(const std::shared_ptr<Layer>& lhs, + const std::shared_ptr<Layer>& rhs); + }; + + // The semantics of the fences returned by the device differ between + // hwc1.set() and hwc2.present(). Read hwcomposer.h and hwcomposer2.h + // for more information. + // + // Release fences in hwc1 are obtained on set() for a frame n and signaled + // when the layer buffer is not needed for read operations anymore + // (typically on frame n+1). In HWC2, release fences are obtained with a + // special call after present() for frame n. These fences signal + // on frame n: More specifically, the fence for a given buffer provided in + // frame n will signal when the prior buffer is no longer required. + // + // A retire fence (HWC1) is signaled when a composition is replaced + // on the panel whereas a present fence (HWC2) is signaled when a + // composition starts to be displayed on a panel. + // + // The HWC2to1Adapter emulates the new fence semantics for a frame + // n by returning the fence from frame n-1. For frame 0, the adapter + // returns NO_FENCE. + class DeferredFence { + public: + DeferredFence() + : mFences({MiniFence::NO_FENCE, MiniFence::NO_FENCE}) {} + + void add(int32_t fenceFd) { + mFences.emplace(new MiniFence(fenceFd)); + mFences.pop(); + } + + const sp<MiniFence>& get() const { + return mFences.front(); + } + + private: + // There are always two fences in this queue. + std::queue<sp<MiniFence>> mFences; + }; + + class FencedBuffer { + public: + FencedBuffer() : mBuffer(nullptr), mFence(MiniFence::NO_FENCE) {} + + void setBuffer(buffer_handle_t buffer) { mBuffer = buffer; } + void setFence(int fenceFd) { mFence = new MiniFence(fenceFd); } + + buffer_handle_t getBuffer() const { return mBuffer; } + int getFence() const { return mFence->dup(); } + + private: + buffer_handle_t mBuffer; + sp<MiniFence> mFence; + }; + + class Display { + public: + Display(HWC2On1Adapter& device, HWC2::DisplayType type); + + hwc2_display_t getId() const { return mId; } + HWC2On1Adapter& getDevice() const { return mDevice; } + + // Does not require locking because it is set before adding the + // Displays to the Adapter's list of displays + void setHwc1Id(int32_t id) { mHwc1Id = id; } + int32_t getHwc1Id() const { return mHwc1Id; } + + // HWC2 Display functions + HWC2::Error acceptChanges(); + HWC2::Error createLayer(hwc2_layer_t* outLayerId); + HWC2::Error destroyLayer(hwc2_layer_t layerId); + HWC2::Error getActiveConfig(hwc2_config_t* outConfigId); + HWC2::Error getAttribute(hwc2_config_t configId, + HWC2::Attribute attribute, int32_t* outValue); + HWC2::Error getChangedCompositionTypes(uint32_t* outNumElements, + hwc2_layer_t* outLayers, int32_t* outTypes); + HWC2::Error getColorModes(uint32_t* outNumModes, int32_t* outModes); + HWC2::Error getConfigs(uint32_t* outNumConfigs, + hwc2_config_t* outConfigIds); + HWC2::Error getDozeSupport(int32_t* outSupport); + HWC2::Error getHdrCapabilities(uint32_t* outNumTypes, + int32_t* outTypes, float* outMaxLuminance, + float* outMaxAverageLuminance, float* outMinLuminance); + HWC2::Error getName(uint32_t* outSize, char* outName); + HWC2::Error getReleaseFences(uint32_t* outNumElements, + hwc2_layer_t* outLayers, int32_t* outFences); + HWC2::Error getRequests(int32_t* outDisplayRequests, + uint32_t* outNumElements, hwc2_layer_t* outLayers, + int32_t* outLayerRequests); + HWC2::Error getType(int32_t* outType); + + // Since HWC1 "presents" (called "set" in HWC1) all Displays + // at once, the first call to any Display::present will trigger + // present() on all Displays in the Device. Subsequent calls without + // first calling validate() are noop (except for duping/returning + // the retire fence). + HWC2::Error present(int32_t* outRetireFence); + + HWC2::Error setActiveConfig(hwc2_config_t configId); + HWC2::Error setClientTarget(buffer_handle_t target, + int32_t acquireFence, int32_t dataspace, + hwc_region_t damage); + HWC2::Error setColorMode(android_color_mode_t mode); + HWC2::Error setColorTransform(android_color_transform_t hint); + HWC2::Error setOutputBuffer(buffer_handle_t buffer, + int32_t releaseFence); + HWC2::Error setPowerMode(HWC2::PowerMode mode); + HWC2::Error setVsyncEnabled(HWC2::Vsync enabled); + + // Since HWC1 "validates" (called "prepare" in HWC1) all Displays + // at once, the first call to any Display::validate() will trigger + // validate() on all other Displays in the Device. + HWC2::Error validate(uint32_t* outNumTypes, + uint32_t* outNumRequests); + + HWC2::Error updateLayerZ(hwc2_layer_t layerId, uint32_t z); + + HWC2::Error getClientTargetSupport(uint32_t width, uint32_t height, + int32_t format, int32_t dataspace); + + // Read configs from HWC1 device + void populateConfigs(); + + // Set configs for a virtual display + void populateConfigs(uint32_t width, uint32_t height); + + bool prepare(); + + // Called after hwc.prepare() with responses from the device. + void generateChanges(); + + bool hasChanges() const; + HWC2::Error set(hwc_display_contents_1& hwcContents); + void addRetireFence(int fenceFd); + void addReleaseFences(const hwc_display_contents_1& hwcContents); + + bool hasColorTransform() const; + + std::string dump() const; + + // Return a rect from the pool allocated during validate() + hwc_rect_t* GetRects(size_t numRects); + + hwc_display_contents_1* getDisplayContents(); + + void markGeometryChanged() { mGeometryChanged = true; } + void resetGeometryMarker() { mGeometryChanged = false;} + private: + class Config { + public: + Config(Display& display) + : mDisplay(display), + mId(0), + mAttributes() {} + + bool isOnDisplay(const Display& display) const { + return display.getId() == mDisplay.getId(); + } + + void setAttribute(HWC2::Attribute attribute, int32_t value); + int32_t getAttribute(HWC2::Attribute attribute) const; + + void setHwc1Id(uint32_t id); + bool hasHwc1Id(uint32_t id) const; + HWC2::Error getColorModeForHwc1Id(uint32_t id, + android_color_mode_t *outMode) const; + HWC2::Error getHwc1IdForColorMode(android_color_mode_t mode, + uint32_t* outId) const; + + void setId(hwc2_config_t id) { mId = id; } + hwc2_config_t getId() const { return mId; } + + // Attempts to merge two configs that differ only in color + // mode. Returns whether the merge was successful + bool merge(const Config& other); + + std::set<android_color_mode_t> getColorModes() const; + + // splitLine divides the output into two lines suitable for + // dumpsys SurfaceFlinger + std::string toString(bool splitLine = false) const; + + private: + Display& mDisplay; + hwc2_config_t mId; + std::unordered_map<HWC2::Attribute, int32_t> mAttributes; + + // Maps from color transform to HWC1 config ID + std::unordered_map<android_color_mode_t, uint32_t> mHwc1Ids; + }; + + // Stores changes requested from the device upon calling prepare(). + // Handles change request to: + // - Layer composition type. + // - Layer hints. + class Changes { + public: + uint32_t getNumTypes() const { + return static_cast<uint32_t>(mTypeChanges.size()); + } + + uint32_t getNumLayerRequests() const { + return static_cast<uint32_t>(mLayerRequests.size()); + } + + const std::unordered_map<hwc2_layer_t, HWC2::Composition>& + getTypeChanges() const { + return mTypeChanges; + } + + const std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>& + getLayerRequests() const { + return mLayerRequests; + } + + void addTypeChange(hwc2_layer_t layerId, + HWC2::Composition type) { + mTypeChanges.insert({layerId, type}); + } + + void clearTypeChanges() { mTypeChanges.clear(); } + + void addLayerRequest(hwc2_layer_t layerId, + HWC2::LayerRequest request) { + mLayerRequests.insert({layerId, request}); + } + + private: + std::unordered_map<hwc2_layer_t, HWC2::Composition> + mTypeChanges; + std::unordered_map<hwc2_layer_t, HWC2::LayerRequest> + mLayerRequests; + }; + + std::shared_ptr<const Config> + getConfig(hwc2_config_t configId) const; + + void populateColorModes(); + void initializeActiveConfig(); + + // Creates a bi-directional mapping between index in HWC1 + // prepare/set array and Layer object. Stores mapping in + // mHwc1LayerMap and also updates Layer's attribute mHwc1Id. + void assignHwc1LayerIds(); + + // Called after a response to prepare() has been received: + // Ingest composition type changes requested by the device. + void updateTypeChanges(const struct hwc_layer_1& hwc1Layer, + const Layer& layer); + + // Called after a response to prepare() has been received: + // Ingest layer hint changes requested by the device. + void updateLayerRequests(const struct hwc_layer_1& hwc1Layer, + const Layer& layer); + + // Set all fields in HWC1 comm array for layer containing the + // HWC_FRAMEBUFFER_TARGET (always the last layer). + void prepareFramebufferTarget(); + + // Display ID generator. + static std::atomic<hwc2_display_t> sNextId; + const hwc2_display_t mId; + + + HWC2On1Adapter& mDevice; + + // The state of this display should only be modified from + // SurfaceFlinger's main loop, with the exception of when dump is + // called. To prevent a bad state from crashing us during a dump + // call, all public calls into Display must acquire this mutex. + // + // It is recursive because we don't want to deadlock in validate + // (or present) when we call HWC2On1Adapter::prepareAllDisplays + // (or setAllDisplays), which calls back into Display functions + // which require locking. + mutable std::recursive_mutex mStateMutex; + + // Allocate RAM able to store all layers and rects used for + // communication with HWC1. Place allocated RAM in variable + // mHwc1RequestedContents. + void allocateRequestedContents(); + + // Array of structs exchanged between client and hwc1 device. + // Sent to device upon calling prepare(). + std::unique_ptr<hwc_display_contents_1> mHwc1RequestedContents; + private: + DeferredFence mRetireFence; + + // Will only be non-null after the Display has been validated and + // before it has been presented + std::unique_ptr<Changes> mChanges; + + int32_t mHwc1Id; + + std::vector<std::shared_ptr<Config>> mConfigs; + std::shared_ptr<const Config> mActiveConfig; + std::set<android_color_mode_t> mColorModes; + android_color_mode_t mActiveColorMode; + std::string mName; + HWC2::DisplayType mType; + HWC2::PowerMode mPowerMode; + HWC2::Vsync mVsyncEnabled; + + // Used to populate HWC1 HWC_FRAMEBUFFER_TARGET layer + FencedBuffer mClientTarget; + + + FencedBuffer mOutputBuffer; + + bool mHasColorTransform; + + // All layers this Display is aware of. + std::multiset<std::shared_ptr<Layer>, SortLayersByZ> mLayers; + + // Mapping between layer index in array of hwc_display_contents_1* + // passed to HWC1 during validate/set and Layer object. + std::unordered_map<size_t, std::shared_ptr<Layer>> mHwc1LayerMap; + + // All communication with HWC1 via prepare/set is done with one + // alloc. This pointer is pointing to a pool of hwc_rect_t. + size_t mNumAvailableRects; + hwc_rect_t* mNextAvailableRect; + + // True if any of the Layers contained in this Display have been + // updated with anything other than a buffer since last call to + // Display::set() + bool mGeometryChanged; + }; + + // Utility template calling a Display object method directly based on the + // hwc2_display_t displayId parameter. + template <typename ...Args> + static int32_t callDisplayFunction(hwc2_device_t* device, + hwc2_display_t displayId, HWC2::Error (Display::*member)(Args...), + Args... args) { + auto display = getAdapter(device)->getDisplay(displayId); + if (!display) { + return static_cast<int32_t>(HWC2::Error::BadDisplay); + } + auto error = ((*display).*member)(std::forward<Args>(args)...); + return static_cast<int32_t>(error); + } + + template <typename MF, MF memFunc, typename ...Args> + static int32_t displayHook(hwc2_device_t* device, hwc2_display_t displayId, + Args... args) { + return HWC2On1Adapter::callDisplayFunction(device, displayId, memFunc, + std::forward<Args>(args)...); + } + + static int32_t getDisplayAttributeHook(hwc2_device_t* device, + hwc2_display_t display, hwc2_config_t config, + int32_t intAttribute, int32_t* outValue) { + auto attribute = static_cast<HWC2::Attribute>(intAttribute); + return callDisplayFunction(device, display, &Display::getAttribute, + config, attribute, outValue); + } + + static int32_t setColorTransformHook(hwc2_device_t* device, + hwc2_display_t display, const float* /*matrix*/, + int32_t /*android_color_transform_t*/ intHint) { + // We intentionally throw away the matrix, because if the hint is + // anything other than IDENTITY, we have to fall back to client + // composition anyway + auto hint = static_cast<android_color_transform_t>(intHint); + return callDisplayFunction(device, display, &Display::setColorTransform, + hint); + } + + static int32_t setColorModeHook(hwc2_device_t* device, + hwc2_display_t display, int32_t /*android_color_mode_t*/ intMode) { + auto mode = static_cast<android_color_mode_t>(intMode); + return callDisplayFunction(device, display, &Display::setColorMode, + mode); + } + + static int32_t setPowerModeHook(hwc2_device_t* device, + hwc2_display_t display, int32_t intMode) { + auto mode = static_cast<HWC2::PowerMode>(intMode); + return callDisplayFunction(device, display, &Display::setPowerMode, + mode); + } + + static int32_t setVsyncEnabledHook(hwc2_device_t* device, + hwc2_display_t display, int32_t intEnabled) { + auto enabled = static_cast<HWC2::Vsync>(intEnabled); + return callDisplayFunction(device, display, &Display::setVsyncEnabled, + enabled); + } + + class Layer { + public: + explicit Layer(Display& display); + + bool operator==(const Layer& other) { return mId == other.mId; } + bool operator!=(const Layer& other) { return !(*this == other); } + + hwc2_layer_t getId() const { return mId; } + Display& getDisplay() const { return mDisplay; } + + // HWC2 Layer functions + HWC2::Error setBuffer(buffer_handle_t buffer, int32_t acquireFence); + HWC2::Error setCursorPosition(int32_t x, int32_t y); + HWC2::Error setSurfaceDamage(hwc_region_t damage); + + // HWC2 Layer state functions + HWC2::Error setBlendMode(HWC2::BlendMode mode); + HWC2::Error setColor(hwc_color_t color); + HWC2::Error setCompositionType(HWC2::Composition type); + HWC2::Error setDataspace(android_dataspace_t dataspace); + HWC2::Error setDisplayFrame(hwc_rect_t frame); + HWC2::Error setPlaneAlpha(float alpha); + HWC2::Error setSidebandStream(const native_handle_t* stream); + HWC2::Error setSourceCrop(hwc_frect_t crop); + HWC2::Error setTransform(HWC2::Transform transform); + HWC2::Error setVisibleRegion(hwc_region_t visible); + HWC2::Error setZ(uint32_t z); + + HWC2::Composition getCompositionType() const { + return mCompositionType; + } + uint32_t getZ() const { return mZ; } + + void addReleaseFence(int fenceFd); + const sp<MiniFence>& getReleaseFence() const; + + void setHwc1Id(size_t id) { mHwc1Id = id; } + size_t getHwc1Id() const { return mHwc1Id; } + + // Write state to HWC1 communication struct. + void applyState(struct hwc_layer_1& hwc1Layer); + + std::string dump() const; + + std::size_t getNumVisibleRegions() { return mVisibleRegion.size(); } + + std::size_t getNumSurfaceDamages() { return mSurfaceDamage.size(); } + + // True if a layer cannot be properly rendered by the device due + // to usage of SolidColor (a.k.a BackgroundColor in HWC1). + bool hasUnsupportedBackgroundColor() { + return (mCompositionType == HWC2::Composition::SolidColor && + !mDisplay.getDevice().supportsBackgroundColor()); + } + private: + void applyCommonState(struct hwc_layer_1& hwc1Layer); + void applySolidColorState(struct hwc_layer_1& hwc1Layer); + void applySidebandState(struct hwc_layer_1& hwc1Layer); + void applyBufferState(struct hwc_layer_1& hwc1Layer); + void applyCompositionType(struct hwc_layer_1& hwc1Layer); + + static std::atomic<hwc2_layer_t> sNextId; + const hwc2_layer_t mId; + Display& mDisplay; + + FencedBuffer mBuffer; + std::vector<hwc_rect_t> mSurfaceDamage; + + HWC2::BlendMode mBlendMode; + hwc_color_t mColor; + HWC2::Composition mCompositionType; + hwc_rect_t mDisplayFrame; + float mPlaneAlpha; + const native_handle_t* mSidebandStream; + hwc_frect_t mSourceCrop; + HWC2::Transform mTransform; + std::vector<hwc_rect_t> mVisibleRegion; + + uint32_t mZ; + + DeferredFence mReleaseFence; + + size_t mHwc1Id; + bool mHasUnsupportedPlaneAlpha; + }; + + // Utility tempate calling a Layer object method based on ID parameters: + // hwc2_display_t displayId + // and + // hwc2_layer_t layerId + template <typename ...Args> + static int32_t callLayerFunction(hwc2_device_t* device, + hwc2_display_t displayId, hwc2_layer_t layerId, + HWC2::Error (Layer::*member)(Args...), Args... args) { + auto result = getAdapter(device)->getLayer(displayId, layerId); + auto error = std::get<HWC2::Error>(result); + if (error == HWC2::Error::None) { + auto layer = std::get<Layer*>(result); + error = ((*layer).*member)(std::forward<Args>(args)...); + } + return static_cast<int32_t>(error); + } + + template <typename MF, MF memFunc, typename ...Args> + static int32_t layerHook(hwc2_device_t* device, hwc2_display_t displayId, + hwc2_layer_t layerId, Args... args) { + return HWC2On1Adapter::callLayerFunction(device, displayId, layerId, + memFunc, std::forward<Args>(args)...); + } + + // Layer state functions + + static int32_t setLayerBlendModeHook(hwc2_device_t* device, + hwc2_display_t display, hwc2_layer_t layer, int32_t intMode) { + auto mode = static_cast<HWC2::BlendMode>(intMode); + return callLayerFunction(device, display, layer, + &Layer::setBlendMode, mode); + } + + static int32_t setLayerCompositionTypeHook(hwc2_device_t* device, + hwc2_display_t display, hwc2_layer_t layer, int32_t intType) { + auto type = static_cast<HWC2::Composition>(intType); + return callLayerFunction(device, display, layer, + &Layer::setCompositionType, type); + } + + static int32_t setLayerDataspaceHook(hwc2_device_t* device, + hwc2_display_t display, hwc2_layer_t layer, int32_t intDataspace) { + auto dataspace = static_cast<android_dataspace_t>(intDataspace); + return callLayerFunction(device, display, layer, &Layer::setDataspace, + dataspace); + } + + static int32_t setLayerTransformHook(hwc2_device_t* device, + hwc2_display_t display, hwc2_layer_t layer, int32_t intTransform) { + auto transform = static_cast<HWC2::Transform>(intTransform); + return callLayerFunction(device, display, layer, &Layer::setTransform, + transform); + } + + static int32_t setLayerZOrderHook(hwc2_device_t* device, + hwc2_display_t display, hwc2_layer_t layer, uint32_t z) { + return callDisplayFunction(device, display, &Display::updateLayerZ, + layer, z); + } + + // Adapter internals + + void populateCapabilities(); + Display* getDisplay(hwc2_display_t id); + std::tuple<Layer*, HWC2::Error> getLayer(hwc2_display_t displayId, + hwc2_layer_t layerId); + void populatePrimary(); + + bool prepareAllDisplays(); + std::vector<struct hwc_display_contents_1*> mHwc1Contents; + HWC2::Error setAllDisplays(); + + // Callbacks + void hwc1Invalidate(); + void hwc1Vsync(int hwc1DisplayId, int64_t timestamp); + void hwc1Hotplug(int hwc1DisplayId, int connected); + + // These are set in the constructor and before any asynchronous events are + // possible + + struct hwc_composer_device_1* const mHwc1Device; + const uint8_t mHwc1MinorVersion; + bool mHwc1SupportsVirtualDisplays; + bool mHwc1SupportsBackgroundColor; + + class Callbacks; + const std::unique_ptr<Callbacks> mHwc1Callbacks; + + std::unordered_set<HWC2::Capability> mCapabilities; + + // These are only accessed from the main SurfaceFlinger thread (not from + // callbacks or dump + + std::map<hwc2_layer_t, std::shared_ptr<Layer>> mLayers; + + // A HWC1 supports only one virtual display. + std::shared_ptr<Display> mHwc1VirtualDisplay; + + // These are potentially accessed from multiple threads, and are protected + // by this mutex. This needs to be recursive, since the HWC1 implementation + // can call back into the invalidate callback on the same thread that is + // calling prepare. + std::recursive_timed_mutex mStateMutex; + + struct CallbackInfo { + hwc2_callback_data_t data; + hwc2_function_pointer_t pointer; + }; + std::unordered_map<HWC2::Callback, CallbackInfo> mCallbacks; + bool mHasPendingInvalidate; + + // There is a small gap between the time the HWC1 module is started and + // when the callbacks for vsync and hotplugs are registered by the + // HWC2on1Adapter. To prevent losing events they are stored in these arrays + // and fed to the callback as soon as possible. + std::vector<std::pair<int, int64_t>> mPendingVsyncs; + std::vector<std::pair<int, int>> mPendingHotplugs; + + // Mapping between HWC1 display id and Display objects. + std::map<hwc2_display_t, std::shared_ptr<Display>> mDisplays; + + // Map HWC1 display type (HWC_DISPLAY_PRIMARY, HWC_DISPLAY_EXTERNAL, + // HWC_DISPLAY_VIRTUAL) to Display IDs generated by HWC2on1Adapter objects. + std::unordered_map<int, hwc2_display_t> mHwc1DisplayMap; +}; + +} // namespace android + +#endif diff --git a/libs/hwc2on1adapter/include/hwc2on1adapter/MiniFence.h b/libs/hwc2on1adapter/include/hwc2on1adapter/MiniFence.h new file mode 100644 index 0000000000..75de764d2c --- /dev/null +++ b/libs/hwc2on1adapter/include/hwc2on1adapter/MiniFence.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 MINIFENCE_H +#define MINIFENCE_H + +#include <utils/RefBase.h> + +namespace android { + +/* MiniFence is a minimal re-implementation of Fence from libui. It exists to + * avoid linking the HWC2on1Adapter to libui and satisfy Treble requirements. + */ +class MiniFence : public LightRefBase<MiniFence> { +public: + static const sp<MiniFence> NO_FENCE; + + // Construct a new MiniFence object with an invalid file descriptor. + MiniFence(); + + // Construct a new MiniFence object to manage a given fence file descriptor. + // When the new MiniFence object is destructed the file descriptor will be + // closed. + explicit MiniFence(int fenceFd); + + // Not copyable or movable. + MiniFence(const MiniFence& rhs) = delete; + MiniFence& operator=(const MiniFence& rhs) = delete; + MiniFence(MiniFence&& rhs) = delete; + MiniFence& operator=(MiniFence&& rhs) = delete; + + // Return a duplicate of the fence file descriptor. The caller is + // responsible for closing the returned file descriptor. On error, -1 will + // be returned and errno will indicate the problem. + int dup() const; + +private: + // Only allow instantiation using ref counting. + friend class LightRefBase<MiniFence>; + ~MiniFence(); + + int mFenceFd; + +}; +} +#endif //MINIFENCE_H diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 9485d5b0e6..92944191c7 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -17,7 +17,11 @@ cc_library { name: "libinput", host_supported: true, - + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], srcs: [ "Input.cpp", "InputDevice.cpp", diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index d755ed30ac..4287abeb7d 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -21,6 +21,7 @@ #include <ctype.h> #include <input/InputDevice.h> +#include <input/InputEventLabels.h> namespace android { @@ -87,17 +88,23 @@ String8 getInputDeviceConfigurationFilePathByName( const String8& name, InputDeviceConfigurationFileType type) { // Search system repository. String8 path; - path.setTo(getenv("ANDROID_ROOT")); - path.append("/usr/"); - appendInputDeviceConfigurationFileRelativePath(path, name, type); + + // Treblized input device config files will be located /odm/usr or /vendor/usr. + const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")}; + for (size_t i = 0; i < size(rootsForPartition); i++) { + path.setTo(rootsForPartition[i]); + path.append("/usr/"); + appendInputDeviceConfigurationFileRelativePath(path, name, type); #if DEBUG_PROBE - ALOGD("Probing for system provided input device configuration file: path='%s'", path.string()); + ALOGD("Probing for system provided input device configuration file: path='%s'", + path.string()); #endif - if (!access(path.string(), R_OK)) { + if (!access(path.string(), R_OK)) { #if DEBUG_PROBE - ALOGD("Found"); + ALOGD("Found"); #endif - return path; + return path; + } } // Search user repository. diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp index 9a01395da1..07f2289785 100644 --- a/libs/input/Keyboard.cpp +++ b/libs/input/Keyboard.cpp @@ -208,7 +208,6 @@ static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaSta } int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) { - int32_t mask; switch (keyCode) { case AKEYCODE_ALT_LEFT: return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState); diff --git a/libs/math/Android.bp b/libs/math/Android.bp new file mode 100644 index 0000000000..3ef8b4aa05 --- /dev/null +++ b/libs/math/Android.bp @@ -0,0 +1,21 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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: "libmath", + host_supported: true, + export_include_dirs: ["include"], +} + +subdirs = ["tests"] diff --git a/libs/math/MODULE_LICENSE_APACHE2 b/libs/math/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/libs/math/MODULE_LICENSE_APACHE2 diff --git a/libs/math/NOTICE b/libs/math/NOTICE new file mode 100644 index 0000000000..c5b1efa7aa --- /dev/null +++ b/libs/math/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libs/math/include/math/TMatHelpers.h b/libs/math/include/math/TMatHelpers.h new file mode 100644 index 0000000000..5cb725d10a --- /dev/null +++ b/libs/math/include/math/TMatHelpers.h @@ -0,0 +1,644 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math.h> +#include <stdint.h> +#include <sys/types.h> + +#include <cmath> +#include <exception> +#include <iomanip> +#include <stdexcept> + +#include <math/quat.h> +#include <math/TVecHelpers.h> + +#include <utils/String8.h> + +#ifndef LIKELY +#define LIKELY_DEFINED_LOCAL +#ifdef __cplusplus +# define LIKELY( exp ) (__builtin_expect( !!(exp), true )) +# define UNLIKELY( exp ) (__builtin_expect( !!(exp), false )) +#else +# define LIKELY( exp ) (__builtin_expect( !!(exp), 1 )) +# define UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 )) +#endif +#endif + +#define PURE __attribute__((pure)) + +#if __cplusplus >= 201402L +#define CONSTEXPR constexpr +#else +#define CONSTEXPR +#endif + +namespace android { +namespace details { +// ------------------------------------------------------------------------------------- + +/* + * No user serviceable parts here. + * + * Don't use this file directly, instead include ui/mat*.h + */ + + +/* + * Matrix utilities + */ + +namespace matrix { + +inline constexpr int transpose(int v) { return v; } +inline constexpr float transpose(float v) { return v; } +inline constexpr double transpose(double v) { return v; } + +inline constexpr int trace(int v) { return v; } +inline constexpr float trace(float v) { return v; } +inline constexpr double trace(double v) { return v; } + +/* + * Matrix inversion + */ +template<typename MATRIX> +MATRIX PURE gaussJordanInverse(const MATRIX& src) { + typedef typename MATRIX::value_type T; + static constexpr unsigned int N = MATRIX::NUM_ROWS; + MATRIX tmp(src); + MATRIX inverted(1); + + for (size_t i = 0; i < N; ++i) { + // look for largest element in i'th column + size_t swap = i; + T t = std::abs(tmp[i][i]); + for (size_t j = i + 1; j < N; ++j) { + const T t2 = std::abs(tmp[j][i]); + if (t2 > t) { + swap = j; + t = t2; + } + } + + if (swap != i) { + // swap columns. + std::swap(tmp[i], tmp[swap]); + std::swap(inverted[i], inverted[swap]); + } + + const T denom(tmp[i][i]); + for (size_t k = 0; k < N; ++k) { + tmp[i][k] /= denom; + inverted[i][k] /= denom; + } + + // Factor out the lower triangle + for (size_t j = 0; j < N; ++j) { + if (j != i) { + const T d = tmp[j][i]; + for (size_t k = 0; k < N; ++k) { + tmp[j][k] -= tmp[i][k] * d; + inverted[j][k] -= inverted[i][k] * d; + } + } + } + } + + return inverted; +} + + +//------------------------------------------------------------------------------ +// 2x2 matrix inverse is easy. +template <typename MATRIX> +CONSTEXPR MATRIX PURE fastInverse2(const MATRIX& x) { + typedef typename MATRIX::value_type T; + + // Assuming the input matrix is: + // | a b | + // | c d | + // + // The analytic inverse is + // | d -b | + // | -c a | / (a d - b c) + // + // Importantly, our matrices are column-major! + + MATRIX inverted(MATRIX::NO_INIT); + + const T a = x[0][0]; + const T c = x[0][1]; + const T b = x[1][0]; + const T d = x[1][1]; + + const T det((a * d) - (b * c)); + inverted[0][0] = d / det; + inverted[0][1] = -c / det; + inverted[1][0] = -b / det; + inverted[1][1] = a / det; + return inverted; +} + + +//------------------------------------------------------------------------------ +// From the Wikipedia article on matrix inversion's section on fast 3x3 +// matrix inversion: +// http://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_3.C3.973_matrices +template <typename MATRIX> +CONSTEXPR MATRIX PURE fastInverse3(const MATRIX& x) { + typedef typename MATRIX::value_type T; + + // Assuming the input matrix is: + // | a b c | + // | d e f | + // | g h i | + // + // The analytic inverse is + // | A B C |^T + // | D E F | + // | G H I | / determinant + // + // Which is + // | A D G | + // | B E H | + // | C F I | / determinant + // + // Where: + // A = (ei - fh), B = (fg - di), C = (dh - eg) + // D = (ch - bi), E = (ai - cg), F = (bg - ah) + // G = (bf - ce), H = (cd - af), I = (ae - bd) + // + // and the determinant is a*A + b*B + c*C (The rule of Sarrus) + // + // Importantly, our matrices are column-major! + + MATRIX inverted(MATRIX::NO_INIT); + + const T a = x[0][0]; + const T b = x[1][0]; + const T c = x[2][0]; + const T d = x[0][1]; + const T e = x[1][1]; + const T f = x[2][1]; + const T g = x[0][2]; + const T h = x[1][2]; + const T i = x[2][2]; + + // Do the full analytic inverse + const T A = e * i - f * h; + const T B = f * g - d * i; + const T C = d * h - e * g; + inverted[0][0] = A; // A + inverted[0][1] = B; // B + inverted[0][2] = C; // C + inverted[1][0] = c * h - b * i; // D + inverted[1][1] = a * i - c * g; // E + inverted[1][2] = b * g - a * h; // F + inverted[2][0] = b * f - c * e; // G + inverted[2][1] = c * d - a * f; // H + inverted[2][2] = a * e - b * d; // I + + const T det(a * A + b * B + c * C); + for (size_t col = 0; col < 3; ++col) { + for (size_t row = 0; row < 3; ++row) { + inverted[col][row] /= det; + } + } + + return inverted; +} + +/** + * Inversion function which switches on the matrix size. + * @warning This function assumes the matrix is invertible. The result is + * undefined if it is not. It is the responsibility of the caller to + * make sure the matrix is not singular. + */ +template <typename MATRIX> +inline constexpr MATRIX PURE inverse(const MATRIX& matrix) { + static_assert(MATRIX::NUM_ROWS == MATRIX::NUM_COLS, "only square matrices can be inverted"); + return (MATRIX::NUM_ROWS == 2) ? fastInverse2<MATRIX>(matrix) : + ((MATRIX::NUM_ROWS == 3) ? fastInverse3<MATRIX>(matrix) : + gaussJordanInverse<MATRIX>(matrix)); +} + +template<typename MATRIX_R, typename MATRIX_A, typename MATRIX_B> +CONSTEXPR MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) { + // pre-requisite: + // lhs : D columns, R rows + // rhs : C columns, D rows + // res : C columns, R rows + + static_assert(MATRIX_A::NUM_COLS == MATRIX_B::NUM_ROWS, + "matrices can't be multiplied. invalid dimensions."); + static_assert(MATRIX_R::NUM_COLS == MATRIX_B::NUM_COLS, + "invalid dimension of matrix multiply result."); + static_assert(MATRIX_R::NUM_ROWS == MATRIX_A::NUM_ROWS, + "invalid dimension of matrix multiply result."); + + MATRIX_R res(MATRIX_R::NO_INIT); + for (size_t col = 0; col < MATRIX_R::NUM_COLS; ++col) { + res[col] = lhs * rhs[col]; + } + return res; +} + +// transpose. this handles matrices of matrices +template <typename MATRIX> +CONSTEXPR MATRIX PURE transpose(const MATRIX& m) { + // for now we only handle square matrix transpose + static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "transpose only supports square matrices"); + MATRIX result(MATRIX::NO_INIT); + for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) { + for (size_t row = 0; row < MATRIX::NUM_ROWS; ++row) { + result[col][row] = transpose(m[row][col]); + } + } + return result; +} + +// trace. this handles matrices of matrices +template <typename MATRIX> +CONSTEXPR typename MATRIX::value_type PURE trace(const MATRIX& m) { + static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "trace only defined for square matrices"); + typename MATRIX::value_type result(0); + for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) { + result += trace(m[col][col]); + } + return result; +} + +// diag. this handles matrices of matrices +template <typename MATRIX> +CONSTEXPR typename MATRIX::col_type PURE diag(const MATRIX& m) { + static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "diag only defined for square matrices"); + typename MATRIX::col_type result(MATRIX::col_type::NO_INIT); + for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) { + result[col] = m[col][col]; + } + return result; +} + +//------------------------------------------------------------------------------ +// This is taken from the Imath MatrixAlgo code, and is identical to Eigen. +template <typename MATRIX> +TQuaternion<typename MATRIX::value_type> extractQuat(const MATRIX& mat) { + typedef typename MATRIX::value_type T; + + TQuaternion<T> quat(TQuaternion<T>::NO_INIT); + + // Compute the trace to see if it is positive or not. + const T trace = mat[0][0] + mat[1][1] + mat[2][2]; + + // check the sign of the trace + if (LIKELY(trace > 0)) { + // trace is positive + T s = std::sqrt(trace + 1); + quat.w = T(0.5) * s; + s = T(0.5) / s; + quat.x = (mat[1][2] - mat[2][1]) * s; + quat.y = (mat[2][0] - mat[0][2]) * s; + quat.z = (mat[0][1] - mat[1][0]) * s; + } else { + // trace is negative + + // Find the index of the greatest diagonal + size_t i = 0; + if (mat[1][1] > mat[0][0]) { i = 1; } + if (mat[2][2] > mat[i][i]) { i = 2; } + + // Get the next indices: (n+1)%3 + static constexpr size_t next_ijk[3] = { 1, 2, 0 }; + size_t j = next_ijk[i]; + size_t k = next_ijk[j]; + T s = std::sqrt((mat[i][i] - (mat[j][j] + mat[k][k])) + 1); + quat[i] = T(0.5) * s; + if (s != 0) { + s = T(0.5) / s; + } + quat.w = (mat[j][k] - mat[k][j]) * s; + quat[j] = (mat[i][j] + mat[j][i]) * s; + quat[k] = (mat[i][k] + mat[k][i]) * s; + } + return quat; +} + +template <typename MATRIX> +String8 asString(const MATRIX& m) { + String8 s; + for (size_t c = 0; c < MATRIX::col_size(); c++) { + s.append("| "); + for (size_t r = 0; r < MATRIX::row_size(); r++) { + s.appendFormat("%7.2f ", m[r][c]); + } + s.append("|\n"); + } + return s; +} + +} // namespace matrix + +// ------------------------------------------------------------------------------------- + +/* + * TMatProductOperators implements basic arithmetic and basic compound assignments + * operators on a vector of type BASE<T>. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TMatProductOperators<BASE, T> BASE will automatically + * get all the functionality here. + */ + +template <template<typename T> class BASE, typename T> +class TMatProductOperators { +public: + // multiply by a scalar + BASE<T>& operator *= (T v) { + BASE<T>& lhs(static_cast< BASE<T>& >(*this)); + for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) { + lhs[col] *= v; + } + return lhs; + } + + // matrix *= matrix + template<typename U> + const BASE<T>& operator *= (const BASE<U>& rhs) { + BASE<T>& lhs(static_cast< BASE<T>& >(*this)); + lhs = matrix::multiply<BASE<T> >(lhs, rhs); + return lhs; + } + + // divide by a scalar + BASE<T>& operator /= (T v) { + BASE<T>& lhs(static_cast< BASE<T>& >(*this)); + for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) { + lhs[col] /= v; + } + return lhs; + } + + // matrix * matrix, result is a matrix of the same type than the lhs matrix + template<typename U> + friend CONSTEXPR BASE<T> PURE operator *(const BASE<T>& lhs, const BASE<U>& rhs) { + return matrix::multiply<BASE<T> >(lhs, rhs); + } +}; + +/* + * TMatSquareFunctions implements functions on a matrix of type BASE<T>. + * + * BASE only needs to implement: + * - operator[] + * - col_type + * - row_type + * - COL_SIZE + * - ROW_SIZE + * + * By simply inheriting from TMatSquareFunctions<BASE, T> BASE will automatically + * get all the functionality here. + */ + +template<template<typename U> class BASE, typename T> +class TMatSquareFunctions { +public: + + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE<T> being known). + */ + friend inline CONSTEXPR BASE<T> PURE inverse(const BASE<T>& matrix) { + return matrix::inverse(matrix); + } + friend inline constexpr BASE<T> PURE transpose(const BASE<T>& m) { + return matrix::transpose(m); + } + friend inline constexpr T PURE trace(const BASE<T>& m) { + return matrix::trace(m); + } +}; + +template<template<typename U> class BASE, typename T> +class TMatHelpers { +public: + constexpr inline size_t getColumnSize() const { return BASE<T>::COL_SIZE; } + constexpr inline size_t getRowSize() const { return BASE<T>::ROW_SIZE; } + constexpr inline size_t getColumnCount() const { return BASE<T>::NUM_COLS; } + constexpr inline size_t getRowCount() const { return BASE<T>::NUM_ROWS; } + constexpr inline size_t size() const { return BASE<T>::ROW_SIZE; } // for TVec*<> + + // array access + constexpr T const* asArray() const { + return &static_cast<BASE<T> const &>(*this)[0][0]; + } + + // element access + inline constexpr T const& operator()(size_t row, size_t col) const { + return static_cast<BASE<T> const &>(*this)[col][row]; + } + + inline T& operator()(size_t row, size_t col) { + return static_cast<BASE<T>&>(*this)[col][row]; + } + + template <typename VEC> + static CONSTEXPR BASE<T> translate(const VEC& t) { + BASE<T> r; + r[BASE<T>::NUM_COLS-1] = t; + return r; + } + + template <typename VEC> + static constexpr BASE<T> scale(const VEC& s) { + return BASE<T>(s); + } + + friend inline CONSTEXPR BASE<T> PURE abs(BASE<T> m) { + for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) { + m[col] = abs(m[col]); + } + return m; + } +}; + +// functions for 3x3 and 4x4 matrices +template<template<typename U> class BASE, typename T> +class TMatTransform { +public: + inline constexpr TMatTransform() { + static_assert(BASE<T>::NUM_ROWS == 3 || BASE<T>::NUM_ROWS == 4, "3x3 or 4x4 matrices only"); + } + + template <typename A, typename VEC> + static CONSTEXPR BASE<T> rotate(A radian, const VEC& about) { + BASE<T> r; + T c = std::cos(radian); + T s = std::sin(radian); + if (about.x == 1 && about.y == 0 && about.z == 0) { + r[1][1] = c; r[2][2] = c; + r[1][2] = s; r[2][1] = -s; + } else if (about.x == 0 && about.y == 1 && about.z == 0) { + r[0][0] = c; r[2][2] = c; + r[2][0] = s; r[0][2] = -s; + } else if (about.x == 0 && about.y == 0 && about.z == 1) { + r[0][0] = c; r[1][1] = c; + r[0][1] = s; r[1][0] = -s; + } else { + VEC nabout = normalize(about); + typename VEC::value_type x = nabout.x; + typename VEC::value_type y = nabout.y; + typename VEC::value_type z = nabout.z; + T nc = 1 - c; + T xy = x * y; + T yz = y * z; + T zx = z * x; + T xs = x * s; + T ys = y * s; + T zs = z * s; + r[0][0] = x*x*nc + c; r[1][0] = xy*nc - zs; r[2][0] = zx*nc + ys; + r[0][1] = xy*nc + zs; r[1][1] = y*y*nc + c; r[2][1] = yz*nc - xs; + r[0][2] = zx*nc - ys; r[1][2] = yz*nc + xs; r[2][2] = z*z*nc + c; + + // Clamp results to -1, 1. + for (size_t col = 0; col < 3; ++col) { + for (size_t row = 0; row < 3; ++row) { + r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1)); + } + } + } + return r; + } + + /** + * Create a matrix from euler angles using YPR around YXZ respectively + * @param yaw about Y axis + * @param pitch about X axis + * @param roll about Z axis + */ + template < + typename Y, typename P, typename R, + typename = typename std::enable_if<std::is_arithmetic<Y>::value >::type, + typename = typename std::enable_if<std::is_arithmetic<P>::value >::type, + typename = typename std::enable_if<std::is_arithmetic<R>::value >::type + > + static CONSTEXPR BASE<T> eulerYXZ(Y yaw, P pitch, R roll) { + return eulerZYX(roll, pitch, yaw); + } + + /** + * Create a matrix from euler angles using YPR around ZYX respectively + * @param roll about X axis + * @param pitch about Y axis + * @param yaw about Z axis + * + * The euler angles are applied in ZYX order. i.e: a vector is first rotated + * about X (roll) then Y (pitch) and then Z (yaw). + */ + template < + typename Y, typename P, typename R, + typename = typename std::enable_if<std::is_arithmetic<Y>::value >::type, + typename = typename std::enable_if<std::is_arithmetic<P>::value >::type, + typename = typename std::enable_if<std::is_arithmetic<R>::value >::type + > + static CONSTEXPR BASE<T> eulerZYX(Y yaw, P pitch, R roll) { + BASE<T> r; + T cy = std::cos(yaw); + T sy = std::sin(yaw); + T cp = std::cos(pitch); + T sp = std::sin(pitch); + T cr = std::cos(roll); + T sr = std::sin(roll); + T cc = cr * cy; + T cs = cr * sy; + T sc = sr * cy; + T ss = sr * sy; + r[0][0] = cp * cy; + r[0][1] = cp * sy; + r[0][2] = -sp; + r[1][0] = sp * sc - cs; + r[1][1] = sp * ss + cc; + r[1][2] = cp * sr; + r[2][0] = sp * cc + ss; + r[2][1] = sp * cs - sc; + r[2][2] = cp * cr; + + // Clamp results to -1, 1. + for (size_t col = 0; col < 3; ++col) { + for (size_t row = 0; row < 3; ++row) { + r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1)); + } + } + return r; + } + + TQuaternion<T> toQuaternion() const { + return matrix::extractQuat(static_cast<const BASE<T>&>(*this)); + } +}; + + +template <template<typename T> class BASE, typename T> +class TMatDebug { +public: + friend std::ostream& operator<<(std::ostream& stream, const BASE<T>& m) { + for (size_t row = 0; row < BASE<T>::NUM_ROWS; ++row) { + if (row != 0) { + stream << std::endl; + } + if (row == 0) { + stream << "/ "; + } else if (row == BASE<T>::NUM_ROWS-1) { + stream << "\\ "; + } else { + stream << "| "; + } + for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) { + stream << std::setw(10) << std::to_string(m[col][row]); + } + if (row == 0) { + stream << " \\"; + } else if (row == BASE<T>::NUM_ROWS-1) { + stream << " /"; + } else { + stream << " |"; + } + } + return stream; + } + + String8 asString() const { + return matrix::asString(static_cast<const BASE<T>&>(*this)); + } +}; + +// ------------------------------------------------------------------------------------- +} // namespace details +} // namespace android + +#ifdef LIKELY_DEFINED_LOCAL +#undef LIKELY_DEFINED_LOCAL +#undef LIKELY +#undef UNLIKELY +#endif //LIKELY_DEFINED_LOCAL + +#undef PURE +#undef CONSTEXPR diff --git a/libs/math/include/math/TQuatHelpers.h b/libs/math/include/math/TQuatHelpers.h new file mode 100644 index 0000000000..f0a71aebcd --- /dev/null +++ b/libs/math/include/math/TQuatHelpers.h @@ -0,0 +1,300 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#pragma once + +#include <math.h> +#include <stdint.h> +#include <sys/types.h> + +#include <iostream> + +#include <math/vec3.h> + +#define PURE __attribute__((pure)) + +namespace android { +namespace details { +// ------------------------------------------------------------------------------------- + +/* + * No user serviceable parts here. + * + * Don't use this file directly, instead include ui/quat.h + */ + + +/* + * TQuatProductOperators implements basic arithmetic and basic compound assignment + * operators on a quaternion of type BASE<T>. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TQuatProductOperators<BASE, T> BASE will automatically + * get all the functionality here. + */ + +template <template<typename T> class QUATERNION, typename T> +class TQuatProductOperators { +public: + /* compound assignment from a another quaternion of the same size but different + * element type. + */ + template <typename OTHER> + QUATERNION<T>& operator *= (const QUATERNION<OTHER>& r) { + QUATERNION<T>& q = static_cast<QUATERNION<T>&>(*this); + q = q * r; + return q; + } + + /* compound assignment products by a scalar + */ + QUATERNION<T>& operator *= (T v) { + QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this); + for (size_t i = 0; i < QUATERNION<T>::size(); i++) { + lhs[i] *= v; + } + return lhs; + } + QUATERNION<T>& operator /= (T v) { + QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this); + for (size_t i = 0; i < QUATERNION<T>::size(); i++) { + lhs[i] /= v; + } + return lhs; + } + + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE<T> being known). + */ + + /* The operators below handle operation between quaternion of the same size + * but of a different element type. + */ + template<typename RT> + friend inline + constexpr QUATERNION<T> PURE operator *(const QUATERNION<T>& q, const QUATERNION<RT>& r) { + // could be written as: + // return QUATERNION<T>( + // q.w*r.w - dot(q.xyz, r.xyz), + // q.w*r.xyz + r.w*q.xyz + cross(q.xyz, r.xyz)); + + return QUATERNION<T>( + q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z, + q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y, + q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x, + q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w); + } + + template<typename RT> + friend inline + constexpr TVec3<T> PURE operator *(const QUATERNION<T>& q, const TVec3<RT>& v) { + // note: if q is known to be a unit quaternion, then this simplifies to: + // TVec3<T> t = 2 * cross(q.xyz, v) + // return v + (q.w * t) + cross(q.xyz, t) + return imaginary(q * QUATERNION<T>(v, 0) * inverse(q)); + } + + + /* For quaternions, we use explicit "by a scalar" products because it's much faster + * than going (implicitly) through the quaternion multiplication. + * For reference: we could use the code below instead, but it would be a lot slower. + * friend inline + * constexpr BASE<T> PURE operator *(const BASE<T>& q, const BASE<T>& r) { + * return BASE<T>( + * q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z, + * q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y, + * q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x, + * q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w); + * + */ + friend inline + constexpr QUATERNION<T> PURE operator *(QUATERNION<T> q, T scalar) { + // don't pass q by reference because we need a copy anyways + return q *= scalar; + } + friend inline + constexpr QUATERNION<T> PURE operator *(T scalar, QUATERNION<T> q) { + // don't pass q by reference because we need a copy anyways + return q *= scalar; + } + + friend inline + constexpr QUATERNION<T> PURE operator /(QUATERNION<T> q, T scalar) { + // don't pass q by reference because we need a copy anyways + return q /= scalar; + } +}; + + +/* + * TQuatFunctions implements functions on a quaternion of type BASE<T>. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TQuatFunctions<BASE, T> BASE will automatically + * get all the functionality here. + */ +template <template<typename T> class QUATERNION, typename T> +class TQuatFunctions { +public: + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE<T> being known). + */ + + template<typename RT> + friend inline + constexpr T PURE dot(const QUATERNION<T>& p, const QUATERNION<RT>& q) { + return p.x * q.x + + p.y * q.y + + p.z * q.z + + p.w * q.w; + } + + friend inline + constexpr T PURE norm(const QUATERNION<T>& q) { + return std::sqrt( dot(q, q) ); + } + + friend inline + constexpr T PURE length(const QUATERNION<T>& q) { + return norm(q); + } + + friend inline + constexpr T PURE length2(const QUATERNION<T>& q) { + return dot(q, q); + } + + friend inline + constexpr QUATERNION<T> PURE normalize(const QUATERNION<T>& q) { + return length(q) ? q / length(q) : QUATERNION<T>(1); + } + + friend inline + constexpr QUATERNION<T> PURE conj(const QUATERNION<T>& q) { + return QUATERNION<T>(q.w, -q.x, -q.y, -q.z); + } + + friend inline + constexpr QUATERNION<T> PURE inverse(const QUATERNION<T>& q) { + return conj(q) * (1 / dot(q, q)); + } + + friend inline + constexpr T PURE real(const QUATERNION<T>& q) { + return q.w; + } + + friend inline + constexpr TVec3<T> PURE imaginary(const QUATERNION<T>& q) { + return q.xyz; + } + + friend inline + constexpr QUATERNION<T> PURE unreal(const QUATERNION<T>& q) { + return QUATERNION<T>(q.xyz, 0); + } + + friend inline + constexpr QUATERNION<T> PURE cross(const QUATERNION<T>& p, const QUATERNION<T>& q) { + return unreal(p*q); + } + + friend inline + QUATERNION<T> PURE exp(const QUATERNION<T>& q) { + const T nq(norm(q.xyz)); + return std::exp(q.w)*QUATERNION<T>((sin(nq)/nq)*q.xyz, cos(nq)); + } + + friend inline + QUATERNION<T> PURE log(const QUATERNION<T>& q) { + const T nq(norm(q)); + return QUATERNION<T>((std::acos(q.w/nq)/norm(q.xyz))*q.xyz, log(nq)); + } + + friend inline + QUATERNION<T> PURE pow(const QUATERNION<T>& q, T a) { + // could also be computed as: exp(a*log(q)); + const T nq(norm(q)); + const T theta(a*std::acos(q.w / nq)); + return std::pow(nq, a) * QUATERNION<T>(normalize(q.xyz) * std::sin(theta), std::cos(theta)); + } + + friend inline + QUATERNION<T> PURE slerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) { + // could also be computed as: pow(q * inverse(p), t) * p; + const T d = dot(p, q); + const T npq = sqrt(dot(p, p) * dot(q, q)); // ||p|| * ||q|| + const T a = std::acos(std::abs(d) / npq); + const T a0 = a * (1 - t); + const T a1 = a * t; + const T isina = 1 / sin(a); + const T s0 = std::sin(a0) * isina; + const T s1 = std::sin(a1) * isina; + // ensure we're taking the "short" side + return normalize(s0 * p + ((d < 0) ? (-s1) : (s1)) * q); + } + + friend inline + constexpr QUATERNION<T> PURE lerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) { + return ((1 - t) * p) + (t * q); + } + + friend inline + constexpr QUATERNION<T> PURE nlerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) { + return normalize(lerp(p, q, t)); + } + + friend inline + constexpr QUATERNION<T> PURE positive(const QUATERNION<T>& q) { + return q.w < 0 ? -q : q; + } +}; + +/* + * TQuatDebug implements functions on a vector of type BASE<T>. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TQuatDebug<BASE, T> BASE will automatically + * get all the functionality here. + */ +template <template<typename T> class QUATERNION, typename T> +class TQuatDebug { +public: + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE<T> being known). + */ + friend std::ostream& operator<< (std::ostream& stream, const QUATERNION<T>& q) { + return stream << "< " << q.w << " + " << q.x << "i + " << q.y << "j + " << q.z << "k >"; + } +}; +#undef PURE + +// ------------------------------------------------------------------------------------- +} // namespace details +} // namespace android diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h new file mode 100644 index 0000000000..20f852fd9f --- /dev/null +++ b/libs/math/include/math/TVecHelpers.h @@ -0,0 +1,608 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#pragma once + +#include <math.h> +#include <stdint.h> +#include <sys/types.h> + +#include <cmath> +#include <limits> +#include <iostream> + +#define PURE __attribute__((pure)) + +#if __cplusplus >= 201402L +#define CONSTEXPR constexpr +#else +#define CONSTEXPR +#endif + +namespace android { +namespace details { +// ------------------------------------------------------------------------------------- + +/* + * No user serviceable parts here. + * + * Don't use this file directly, instead include ui/vec{2|3|4}.h + */ + +/* + * TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments + * operators on a vector of type BASE<T>. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TVec{Add|Product}Operators<BASE, T> BASE will automatically + * get all the functionality here. + */ + +template <template<typename T> class VECTOR, typename T> +class TVecAddOperators { +public: + /* compound assignment from a another vector of the same size but different + * element type. + */ + template<typename OTHER> + VECTOR<T>& operator +=(const VECTOR<OTHER>& v) { + VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this); + for (size_t i = 0; i < lhs.size(); i++) { + lhs[i] += v[i]; + } + return lhs; + } + template<typename OTHER> + VECTOR<T>& operator -=(const VECTOR<OTHER>& v) { + VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this); + for (size_t i = 0; i < lhs.size(); i++) { + lhs[i] -= v[i]; + } + return lhs; + } + + /* compound assignment from a another vector of the same type. + * These operators can be used for implicit conversion and handle operations + * like "vector *= scalar" by letting the compiler implicitly convert a scalar + * to a vector (assuming the BASE<T> allows it). + */ + VECTOR<T>& operator +=(const VECTOR<T>& v) { + VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this); + for (size_t i = 0; i < lhs.size(); i++) { + lhs[i] += v[i]; + } + return lhs; + } + VECTOR<T>& operator -=(const VECTOR<T>& v) { + VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this); + for (size_t i = 0; i < lhs.size(); i++) { + lhs[i] -= v[i]; + } + return lhs; + } + + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE<T> being known). + */ + + /* The operators below handle operation between vectors of the same size + * but of a different element type. + */ + template<typename RT> + friend inline constexpr VECTOR<T> PURE operator +(VECTOR<T> lv, const VECTOR<RT>& rv) { + // don't pass lv by reference because we need a copy anyways + return lv += rv; + } + template<typename RT> + friend inline constexpr VECTOR<T> PURE operator -(VECTOR<T> lv, const VECTOR<RT>& rv) { + // don't pass lv by reference because we need a copy anyways + return lv -= rv; + } + + /* The operators below (which are not templates once this class is instanced, + * i.e.: BASE<T> is known) can be used for implicit conversion on both sides. + * These handle operations like "vector + scalar" and "scalar + vector" by + * letting the compiler implicitly convert a scalar to a vector (assuming + * the BASE<T> allows it). + */ + friend inline constexpr VECTOR<T> PURE operator +(VECTOR<T> lv, const VECTOR<T>& rv) { + // don't pass lv by reference because we need a copy anyways + return lv += rv; + } + friend inline constexpr VECTOR<T> PURE operator -(VECTOR<T> lv, const VECTOR<T>& rv) { + // don't pass lv by reference because we need a copy anyways + return lv -= rv; + } +}; + +template<template<typename T> class VECTOR, typename T> +class TVecProductOperators { +public: + /* compound assignment from a another vector of the same size but different + * element type. + */ + template<typename OTHER> + VECTOR<T>& operator *=(const VECTOR<OTHER>& v) { + VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this); + for (size_t i = 0; i < lhs.size(); i++) { + lhs[i] *= v[i]; + } + return lhs; + } + template<typename OTHER> + VECTOR<T>& operator /=(const VECTOR<OTHER>& v) { + VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this); + for (size_t i = 0; i < lhs.size(); i++) { + lhs[i] /= v[i]; + } + return lhs; + } + + /* compound assignment from a another vector of the same type. + * These operators can be used for implicit conversion and handle operations + * like "vector *= scalar" by letting the compiler implicitly convert a scalar + * to a vector (assuming the BASE<T> allows it). + */ + VECTOR<T>& operator *=(const VECTOR<T>& v) { + VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this); + for (size_t i = 0; i < lhs.size(); i++) { + lhs[i] *= v[i]; + } + return lhs; + } + VECTOR<T>& operator /=(const VECTOR<T>& v) { + VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this); + for (size_t i = 0; i < lhs.size(); i++) { + lhs[i] /= v[i]; + } + return lhs; + } + + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE<T> being known). + */ + + /* The operators below handle operation between vectors of the same size + * but of a different element type. + */ + template<typename RT> + friend inline constexpr VECTOR<T> PURE operator *(VECTOR<T> lv, const VECTOR<RT>& rv) { + // don't pass lv by reference because we need a copy anyways + return lv *= rv; + } + template<typename RT> + friend inline constexpr VECTOR<T> PURE operator /(VECTOR<T> lv, const VECTOR<RT>& rv) { + // don't pass lv by reference because we need a copy anyways + return lv /= rv; + } + + /* The operators below (which are not templates once this class is instanced, + * i.e.: BASE<T> is known) can be used for implicit conversion on both sides. + * These handle operations like "vector * scalar" and "scalar * vector" by + * letting the compiler implicitly convert a scalar to a vector (assuming + * the BASE<T> allows it). + */ + friend inline constexpr VECTOR<T> PURE operator *(VECTOR<T> lv, const VECTOR<T>& rv) { + // don't pass lv by reference because we need a copy anyways + return lv *= rv; + } + friend inline constexpr VECTOR<T> PURE operator /(VECTOR<T> lv, const VECTOR<T>& rv) { + // don't pass lv by reference because we need a copy anyways + return lv /= rv; + } +}; + +/* + * TVecUnaryOperators implements unary operators on a vector of type BASE<T>. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TVecUnaryOperators<BASE, T> BASE will automatically + * get all the functionality here. + * + * These operators are implemented as friend functions of TVecUnaryOperators<BASE, T> + */ +template<template<typename T> class VECTOR, typename T> +class TVecUnaryOperators { +public: + VECTOR<T>& operator ++() { + VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this); + for (size_t i = 0; i < rhs.size(); i++) { + ++rhs[i]; + } + return rhs; + } + + VECTOR<T>& operator --() { + VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this); + for (size_t i = 0; i < rhs.size(); i++) { + --rhs[i]; + } + return rhs; + } + + CONSTEXPR VECTOR<T> operator -() const { + VECTOR<T> r(VECTOR<T>::NO_INIT); + VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this)); + for (size_t i = 0; i < r.size(); i++) { + r[i] = -rv[i]; + } + return r; + } +}; + +/* + * TVecComparisonOperators implements relational/comparison operators + * on a vector of type BASE<T>. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TVecComparisonOperators<BASE, T> BASE will automatically + * get all the functionality here. + */ +template<template<typename T> class VECTOR, typename T> +class TVecComparisonOperators { +public: + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE<T> being known). + */ + template<typename RT> + friend inline + bool PURE operator ==(const VECTOR<T>& lv, const VECTOR<RT>& rv) { + for (size_t i = 0; i < lv.size(); i++) + if (lv[i] != rv[i]) + return false; + return true; + } + + template<typename RT> + friend inline + bool PURE operator !=(const VECTOR<T>& lv, const VECTOR<RT>& rv) { + return !operator ==(lv, rv); + } + + template<typename RT> + friend inline + bool PURE operator >(const VECTOR<T>& lv, const VECTOR<RT>& rv) { + for (size_t i = 0; i < lv.size(); i++) { + if (lv[i] == rv[i]) { + continue; + } + return lv[i] > rv[i]; + } + return false; + } + + template<typename RT> + friend inline + constexpr bool PURE operator <=(const VECTOR<T>& lv, const VECTOR<RT>& rv) { + return !(lv > rv); + } + + template<typename RT> + friend inline + bool PURE operator <(const VECTOR<T>& lv, const VECTOR<RT>& rv) { + for (size_t i = 0; i < lv.size(); i++) { + if (lv[i] == rv[i]) { + continue; + } + return lv[i] < rv[i]; + } + return false; + } + + template<typename RT> + friend inline + constexpr bool PURE operator >=(const VECTOR<T>& lv, const VECTOR<RT>& rv) { + return !(lv < rv); + } + + template<typename RT> + friend inline + CONSTEXPR VECTOR<bool> PURE equal(const VECTOR<T>& lv, const VECTOR<RT>& rv) { + VECTOR<bool> r; + for (size_t i = 0; i < lv.size(); i++) { + r[i] = lv[i] == rv[i]; + } + return r; + } + + template<typename RT> + friend inline + CONSTEXPR VECTOR<bool> PURE notEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) { + VECTOR<bool> r; + for (size_t i = 0; i < lv.size(); i++) { + r[i] = lv[i] != rv[i]; + } + return r; + } + + template<typename RT> + friend inline + CONSTEXPR VECTOR<bool> PURE lessThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) { + VECTOR<bool> r; + for (size_t i = 0; i < lv.size(); i++) { + r[i] = lv[i] < rv[i]; + } + return r; + } + + template<typename RT> + friend inline + CONSTEXPR VECTOR<bool> PURE lessThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) { + VECTOR<bool> r; + for (size_t i = 0; i < lv.size(); i++) { + r[i] = lv[i] <= rv[i]; + } + return r; + } + + template<typename RT> + friend inline + CONSTEXPR VECTOR<bool> PURE greaterThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) { + VECTOR<bool> r; + for (size_t i = 0; i < lv.size(); i++) { + r[i] = lv[i] > rv[i]; + } + return r; + } + + template<typename RT> + friend inline + CONSTEXPR VECTOR<bool> PURE greaterThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) { + VECTOR<bool> r; + for (size_t i = 0; i < lv.size(); i++) { + r[i] = lv[i] >= rv[i]; + } + return r; + } +}; + +/* + * TVecFunctions implements functions on a vector of type BASE<T>. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TVecFunctions<BASE, T> BASE will automatically + * get all the functionality here. + */ +template<template<typename T> class VECTOR, typename T> +class TVecFunctions { +public: + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE<T> being known). + */ + template<typename RT> + friend inline CONSTEXPR T PURE dot(const VECTOR<T>& lv, const VECTOR<RT>& rv) { + T r(0); + for (size_t i = 0; i < lv.size(); i++) { + //r = std::fma(lv[i], rv[i], r); + r += lv[i] * rv[i]; + } + return r; + } + + friend inline constexpr T PURE norm(const VECTOR<T>& lv) { + return std::sqrt(dot(lv, lv)); + } + + friend inline constexpr T PURE length(const VECTOR<T>& lv) { + return norm(lv); + } + + friend inline constexpr T PURE norm2(const VECTOR<T>& lv) { + return dot(lv, lv); + } + + friend inline constexpr T PURE length2(const VECTOR<T>& lv) { + return norm2(lv); + } + + template<typename RT> + friend inline constexpr T PURE distance(const VECTOR<T>& lv, const VECTOR<RT>& rv) { + return length(rv - lv); + } + + template<typename RT> + friend inline constexpr T PURE distance2(const VECTOR<T>& lv, const VECTOR<RT>& rv) { + return length2(rv - lv); + } + + friend inline constexpr VECTOR<T> PURE normalize(const VECTOR<T>& lv) { + return lv * (T(1) / length(lv)); + } + + friend inline constexpr VECTOR<T> PURE rcp(VECTOR<T> v) { + return T(1) / v; + } + + friend inline CONSTEXPR VECTOR<T> PURE abs(VECTOR<T> v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::abs(v[i]); + } + return v; + } + + friend inline CONSTEXPR VECTOR<T> PURE floor(VECTOR<T> v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::floor(v[i]); + } + return v; + } + + friend inline CONSTEXPR VECTOR<T> PURE ceil(VECTOR<T> v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::ceil(v[i]); + } + return v; + } + + friend inline CONSTEXPR VECTOR<T> PURE round(VECTOR<T> v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::round(v[i]); + } + return v; + } + + friend inline CONSTEXPR VECTOR<T> PURE inversesqrt(VECTOR<T> v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = T(1) / std::sqrt(v[i]); + } + return v; + } + + friend inline CONSTEXPR VECTOR<T> PURE sqrt(VECTOR<T> v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::sqrt(v[i]); + } + return v; + } + + friend inline CONSTEXPR VECTOR<T> PURE pow(VECTOR<T> v, T p) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::pow(v[i], p); + } + return v; + } + + friend inline CONSTEXPR VECTOR<T> PURE saturate(const VECTOR<T>& lv) { + return clamp(lv, T(0), T(1)); + } + + friend inline CONSTEXPR VECTOR<T> PURE clamp(VECTOR<T> v, T min, T max) { + for (size_t i = 0; i< v.size(); i++) { + v[i] = std::min(max, std::max(min, v[i])); + } + return v; + } + + friend inline CONSTEXPR VECTOR<T> PURE fma(const VECTOR<T>& lv, const VECTOR<T>& rv, VECTOR<T> a) { + for (size_t i = 0; i<lv.size(); i++) { + //a[i] = std::fma(lv[i], rv[i], a[i]); + a[i] += (lv[i] * rv[i]); + } + return a; + } + + friend inline CONSTEXPR VECTOR<T> PURE min(const VECTOR<T>& u, VECTOR<T> v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::min(u[i], v[i]); + } + return v; + } + + friend inline CONSTEXPR VECTOR<T> PURE max(const VECTOR<T>& u, VECTOR<T> v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::max(u[i], v[i]); + } + return v; + } + + friend inline CONSTEXPR T PURE max(const VECTOR<T>& v) { + T r(std::numeric_limits<T>::lowest()); + for (size_t i = 0; i < v.size(); i++) { + r = std::max(r, v[i]); + } + return r; + } + + friend inline CONSTEXPR T PURE min(const VECTOR<T>& v) { + T r(std::numeric_limits<T>::max()); + for (size_t i = 0; i < v.size(); i++) { + r = std::min(r, v[i]); + } + return r; + } + + friend inline CONSTEXPR VECTOR<T> PURE apply(VECTOR<T> v, const std::function<T(T)>& f) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = f(v[i]); + } + return v; + } + + friend inline CONSTEXPR bool PURE any(const VECTOR<T>& v) { + for (size_t i = 0; i < v.size(); i++) { + if (v[i] != T(0)) return true; + } + return false; + } + + friend inline CONSTEXPR bool PURE all(const VECTOR<T>& v) { + bool result = true; + for (size_t i = 0; i < v.size(); i++) { + result &= (v[i] != T(0)); + } + return result; + } + + template<typename R> + friend inline CONSTEXPR VECTOR<R> PURE map(VECTOR<T> v, const std::function<R(T)>& f) { + VECTOR<R> result; + for (size_t i = 0; i < v.size(); i++) { + result[i] = f(v[i]); + } + return result; + } +}; + +/* + * TVecDebug implements functions on a vector of type BASE<T>. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TVecDebug<BASE, T> BASE will automatically + * get all the functionality here. + */ +template<template<typename T> class VECTOR, typename T> +class TVecDebug { +public: + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE<T> being known). + */ + friend std::ostream& operator<<(std::ostream& stream, const VECTOR<T>& v) { + stream << "< "; + for (size_t i = 0; i < v.size() - 1; i++) { + stream << T(v[i]) << ", "; + } + stream << T(v[v.size() - 1]) << " >"; + return stream; + } +}; + +#undef CONSTEXPR +#undef PURE + +// ------------------------------------------------------------------------------------- +} // namespace details +} // namespace android diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h new file mode 100644 index 0000000000..76829734a4 --- /dev/null +++ b/libs/math/include/math/half.h @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <iosfwd> +#include <limits> +#include <type_traits> + +#ifndef LIKELY +#define LIKELY_DEFINED_LOCAL +#ifdef __cplusplus +# define LIKELY( exp ) (__builtin_expect( !!(exp), true )) +# define UNLIKELY( exp ) (__builtin_expect( !!(exp), false )) +#else +# define LIKELY( exp ) (__builtin_expect( !!(exp), 1 )) +# define UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 )) +#endif +#endif + +#if __cplusplus >= 201402L +#define CONSTEXPR constexpr +#else +#define CONSTEXPR +#endif + +namespace android { + +/* + * half-float + * + * 1 5 10 + * +-+------+------------+ + * |s|eee.ee|mm.mmmm.mmmm| + * +-+------+------------+ + * + * minimum (denormal) value: 2^-24 = 5.96e-8 + * minimum (normal) value: 2^-14 = 6.10e-5 + * maximum value: 2-2^-10 = 65504 + * + * Integers between 0 and 2048 can be represented exactly + */ +class half { + struct fp16 { + uint16_t bits; + explicit constexpr fp16() noexcept : bits(0) { } + explicit constexpr fp16(uint16_t b) noexcept : bits(b) { } + void setS(unsigned int s) noexcept { bits = uint16_t((bits & 0x7FFF) | (s<<15)); } + void setE(unsigned int s) noexcept { bits = uint16_t((bits & 0xE3FF) | (s<<10)); } + void setM(unsigned int s) noexcept { bits = uint16_t((bits & 0xFC00) | (s<< 0)); } + constexpr unsigned int getS() const noexcept { return bits >> 15u; } + constexpr unsigned int getE() const noexcept { return (bits >> 10u) & 0x1Fu; } + constexpr unsigned int getM() const noexcept { return bits & 0x3FFu; } + }; + struct fp32 { + union { + uint32_t bits; + float fp; + }; + explicit constexpr fp32() noexcept : bits(0) { } + explicit constexpr fp32(float f) noexcept : fp(f) { } + void setS(unsigned int s) noexcept { bits = uint32_t((bits & 0x7FFFFFFF) | (s<<31)); } + void setE(unsigned int s) noexcept { bits = uint32_t((bits & 0x807FFFFF) | (s<<23)); } + void setM(unsigned int s) noexcept { bits = uint32_t((bits & 0xFF800000) | (s<< 0)); } + constexpr unsigned int getS() const noexcept { return bits >> 31u; } + constexpr unsigned int getE() const noexcept { return (bits >> 23u) & 0xFFu; } + constexpr unsigned int getM() const noexcept { return bits & 0x7FFFFFu; } + }; + +public: + CONSTEXPR half(float v) noexcept : mBits(ftoh(v)) { } + CONSTEXPR operator float() const noexcept { return htof(mBits); } + + uint16_t getBits() const noexcept { return mBits.bits; } + unsigned int getExponent() const noexcept { return mBits.getE(); } + unsigned int getMantissa() const noexcept { return mBits.getM(); } + +private: + friend class std::numeric_limits<half>; + friend CONSTEXPR half operator"" _hf(long double v); + + enum Binary { binary }; + explicit constexpr half(Binary, uint16_t bits) noexcept : mBits(bits) { } + static CONSTEXPR fp16 ftoh(float v) noexcept; + static CONSTEXPR float htof(fp16 v) noexcept; + fp16 mBits; +}; + +inline CONSTEXPR half::fp16 half::ftoh(float v) noexcept { + fp16 out; + fp32 in(v); + if (UNLIKELY(in.getE() == 0xFF)) { // inf or nan + out.setE(0x1F); + out.setM(in.getM() ? 0x200 : 0); + } else { + int e = static_cast<int>(in.getE()) - 127 + 15; + if (e >= 0x1F) { + // overflow + out.setE(0x31); // +/- inf + } else if (e <= 0) { + // underflow + // flush to +/- 0 + } else { + unsigned int m = in.getM(); + out.setE(uint16_t(e)); + out.setM(m >> 13); + if (m & 0x1000) { + // rounding + out.bits++; + } + } + } + out.setS(in.getS()); + return out; +} + +inline CONSTEXPR float half::htof(half::fp16 in) noexcept { + fp32 out; + if (UNLIKELY(in.getE() == 0x1F)) { // inf or nan + out.setE(0xFF); + out.setM(in.getM() ? 0x400000 : 0); + } else { + if (in.getE() == 0) { + if (in.getM()) { + // TODO: denormal half float, treat as zero for now + // (it's stupid because they can be represented as regular float) + } + } else { + int e = static_cast<int>(in.getE()) - 15 + 127; + unsigned int m = in.getM(); + out.setE(uint32_t(e)); + out.setM(m << 13); + } + } + out.setS(in.getS()); + return out.fp; +} + +inline CONSTEXPR android::half operator"" _hf(long double v) { + return android::half(android::half::binary, android::half::ftoh(static_cast<float>(v)).bits); +} + +} // namespace android + +namespace std { + +template<> struct is_floating_point<android::half> : public std::true_type {}; + +template<> +class numeric_limits<android::half> { +public: + typedef android::half type; + + static constexpr const bool is_specialized = true; + static constexpr const bool is_signed = true; + static constexpr const bool is_integer = false; + static constexpr const bool is_exact = false; + static constexpr const bool has_infinity = true; + static constexpr const bool has_quiet_NaN = true; + static constexpr const bool has_signaling_NaN = false; + static constexpr const float_denorm_style has_denorm = denorm_absent; + static constexpr const bool has_denorm_loss = true; + static constexpr const bool is_iec559 = false; + static constexpr const bool is_bounded = true; + static constexpr const bool is_modulo = false; + static constexpr const bool traps = false; + static constexpr const bool tinyness_before = false; + static constexpr const float_round_style round_style = round_indeterminate; + + static constexpr const int digits = 11; + static constexpr const int digits10 = 3; + static constexpr const int max_digits10 = 5; + static constexpr const int radix = 2; + static constexpr const int min_exponent = -13; + static constexpr const int min_exponent10 = -4; + static constexpr const int max_exponent = 16; + static constexpr const int max_exponent10 = 4; + + inline static constexpr type round_error() noexcept { return android::half(android::half::binary, 0x3800); } + inline static constexpr type min() noexcept { return android::half(android::half::binary, 0x0400); } + inline static constexpr type max() noexcept { return android::half(android::half::binary, 0x7bff); } + inline static constexpr type lowest() noexcept { return android::half(android::half::binary, 0xfbff); } + inline static constexpr type epsilon() noexcept { return android::half(android::half::binary, 0x1400); } + inline static constexpr type infinity() noexcept { return android::half(android::half::binary, 0x7c00); } + inline static constexpr type quiet_NaN() noexcept { return android::half(android::half::binary, 0x7fff); } + inline static constexpr type denorm_min() noexcept { return android::half(android::half::binary, 0x0001); } + inline static constexpr type signaling_NaN() noexcept { return android::half(android::half::binary, 0x7dff); } +}; + +} // namespace std + +#ifdef LIKELY_DEFINED_LOCAL +#undef LIKELY_DEFINED_LOCAL +#undef LIKELY +#undef UNLIKELY +#endif // LIKELY_DEFINED_LOCAL + +#undef CONSTEXPR diff --git a/libs/math/include/math/mat2.h b/libs/math/include/math/mat2.h new file mode 100644 index 0000000000..3e6cd4c794 --- /dev/null +++ b/libs/math/include/math/mat2.h @@ -0,0 +1,377 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/TMatHelpers.h> +#include <math/vec2.h> +#include <stdint.h> +#include <sys/types.h> + +#define PURE __attribute__((pure)) + +#if __cplusplus >= 201402L +#define CONSTEXPR constexpr +#else +#define CONSTEXPR +#endif + +namespace android { +// ------------------------------------------------------------------------------------- +namespace details { + +/** + * A 2x2 column-major matrix class. + * + * Conceptually a 2x2 matrix is a an array of 2 column vec2: + * + * mat2 m = + * \f$ + * \left( + * \begin{array}{cc} + * m[0] & m[1] \\ + * \end{array} + * \right) + * \f$ + * = + * \f$ + * \left( + * \begin{array}{cc} + * m[0][0] & m[1][0] \\ + * m[0][1] & m[1][1] \\ + * \end{array} + * \right) + * \f$ + * = + * \f$ + * \left( + * \begin{array}{cc} + * m(0,0) & m(0,1) \\ + * m(1,0) & m(1,1) \\ + * \end{array} + * \right) + * \f$ + * + * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec2. + * + */ +template <typename T> +class TMat22 : public TVecUnaryOperators<TMat22, T>, + public TVecComparisonOperators<TMat22, T>, + public TVecAddOperators<TMat22, T>, + public TMatProductOperators<TMat22, T>, + public TMatSquareFunctions<TMat22, T>, + public TMatHelpers<TMat22, T>, + public TMatDebug<TMat22, T> { +public: + enum no_init { NO_INIT }; + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + typedef TVec2<T> col_type; + typedef TVec2<T> row_type; + + static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows) + static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns) + static constexpr size_t NUM_ROWS = COL_SIZE; + static constexpr size_t NUM_COLS = ROW_SIZE; + +private: + /* + * <-- N columns --> + * + * a[0][0] a[1][0] a[2][0] ... a[N][0] ^ + * a[0][1] a[1][1] a[2][1] ... a[N][1] | + * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows + * ... | + * a[0][M] a[1][M] a[2][M] ... a[N][M] v + * + * COL_SIZE = M + * ROW_SIZE = N + * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ] + */ + + col_type m_value[NUM_COLS]; + +public: + // array access + inline constexpr col_type const& operator[](size_t column) const { +#if __cplusplus >= 201402L + // only possible in C++0x14 with constexpr + assert(column < NUM_COLS); +#endif + return m_value[column]; + } + + inline col_type& operator[](size_t column) { + assert(column < NUM_COLS); + return m_value[column]; + } + + // ----------------------------------------------------------------------- + // we want the compiler generated versions for these... + TMat22(const TMat22&) = default; + ~TMat22() = default; + TMat22& operator = (const TMat22&) = default; + + /** + * constructors + */ + + /** + * leaves object uninitialized. use with caution. + */ + explicit constexpr TMat22(no_init) + : m_value{ col_type(col_type::NO_INIT), + col_type(col_type::NO_INIT) } {} + + + /** + * initialize to identity. + * + * \f$ + * \left( + * \begin{array}{cc} + * 1 & 0 \\ + * 0 & 1 \\ + * \end{array} + * \right) + * \f$ + */ + CONSTEXPR TMat22(); + + /** + * initialize to Identity*scalar. + * + * \f$ + * \left( + * \begin{array}{cc} + * v & 0 \\ + * 0 & v \\ + * \end{array} + * \right) + * \f$ + */ + template<typename U> + explicit CONSTEXPR TMat22(U v); + + /** + * sets the diagonal to a vector. + * + * \f$ + * \left( + * \begin{array}{cc} + * v[0] & 0 \\ + * 0 & v[1] \\ + * \end{array} + * \right) + * \f$ + */ + template <typename U> + explicit CONSTEXPR TMat22(const TVec2<U>& v); + + /** + * construct from another matrix of the same size + */ + template <typename U> + explicit CONSTEXPR TMat22(const TMat22<U>& rhs); + + /** + * construct from 2 column vectors. + * + * \f$ + * \left( + * \begin{array}{cc} + * v0 & v1 \\ + * \end{array} + * \right) + * \f$ + */ + template <typename A, typename B> + CONSTEXPR TMat22(const TVec2<A>& v0, const TVec2<B>& v1); + + /** construct from 4 elements in column-major form. + * + * \f$ + * \left( + * \begin{array}{cc} + * m[0][0] & m[1][0] \\ + * m[0][1] & m[1][1] \\ + * \end{array} + * \right) + * \f$ + */ + template < + typename A, typename B, + typename C, typename D> + CONSTEXPR TMat22(A m00, B m01, C m10, D m11); + + /** + * construct from a C array in column major form. + */ + template <typename U> + explicit CONSTEXPR TMat22(U const* rawArray); + + /** + * Rotate by radians in the 2D plane + */ + static CONSTEXPR TMat22<T> rotate(T radian) { + TMat22<T> r(TMat22<T>::NO_INIT); + T c = std::cos(radian); + T s = std::sin(radian); + r[0][0] = c; r[1][1] = c; + r[0][1] = s; r[1][0] = -s; + return r; + } +}; + +// ---------------------------------------------------------------------------------------- +// Constructors +// ---------------------------------------------------------------------------------------- + +// Since the matrix code could become pretty big quickly, we don't inline most +// operations. + +template <typename T> +CONSTEXPR TMat22<T>::TMat22() { + m_value[0] = col_type(1, 0); + m_value[1] = col_type(0, 1); +} + +template <typename T> +template <typename U> +CONSTEXPR TMat22<T>::TMat22(U v) { + m_value[0] = col_type(v, 0); + m_value[1] = col_type(0, v); +} + +template<typename T> +template<typename U> +CONSTEXPR TMat22<T>::TMat22(const TVec2<U>& v) { + m_value[0] = col_type(v.x, 0); + m_value[1] = col_type(0, v.y); +} + +// construct from 4 scalars. Note that the arrangement +// of values in the constructor is the transpose of the matrix +// notation. +template<typename T> +template < + typename A, typename B, + typename C, typename D> +CONSTEXPR TMat22<T>::TMat22( A m00, B m01, C m10, D m11) { + m_value[0] = col_type(m00, m01); + m_value[1] = col_type(m10, m11); +} + +template <typename T> +template <typename U> +CONSTEXPR TMat22<T>::TMat22(const TMat22<U>& rhs) { + for (size_t col = 0; col < NUM_COLS; ++col) { + m_value[col] = col_type(rhs[col]); + } +} + +// Construct from 2 column vectors. +template <typename T> +template <typename A, typename B> +CONSTEXPR TMat22<T>::TMat22(const TVec2<A>& v0, const TVec2<B>& v1) { + m_value[0] = v0; + m_value[1] = v1; +} + +// Construct from raw array, in column-major form. +template <typename T> +template <typename U> +CONSTEXPR TMat22<T>::TMat22(U const* rawArray) { + for (size_t col = 0; col < NUM_COLS; ++col) { + for (size_t row = 0; row < NUM_ROWS; ++row) { + m_value[col][row] = *rawArray++; + } + } +} + +// ---------------------------------------------------------------------------------------- +// Arithmetic operators outside of class +// ---------------------------------------------------------------------------------------- + +/* We use non-friend functions here to prevent the compiler from using + * implicit conversions, for instance of a scalar to a vector. The result would + * not be what the caller expects. + * + * Also note that the order of the arguments in the inner loop is important since + * it determines the output type (only relevant when T != U). + */ + +// matrix * column-vector, result is a vector of the same type than the input vector +template <typename T, typename U> +CONSTEXPR typename TMat22<U>::col_type PURE operator *(const TMat22<T>& lhs, const TVec2<U>& rhs) { + // Result is initialized to zero. + typename TMat22<U>::col_type result; + for (size_t col = 0; col < TMat22<T>::NUM_COLS; ++col) { + result += lhs[col] * rhs[col]; + } + return result; +} + +// row-vector * matrix, result is a vector of the same type than the input vector +template <typename T, typename U> +CONSTEXPR typename TMat22<U>::row_type PURE operator *(const TVec2<U>& lhs, const TMat22<T>& rhs) { + typename TMat22<U>::row_type result(TMat22<U>::row_type::NO_INIT); + for (size_t col = 0; col < TMat22<T>::NUM_COLS; ++col) { + result[col] = dot(lhs, rhs[col]); + } + return result; +} + +// matrix * scalar, result is a matrix of the same type than the input matrix +template<typename T, typename U> +constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat22<T>>::type PURE +operator*(TMat22<T> lhs, U rhs) { + return lhs *= rhs; +} + +// scalar * matrix, result is a matrix of the same type than the input matrix +template<typename T, typename U> +constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat22<T>>::type PURE +operator*(U lhs, const TMat22<T>& rhs) { + return rhs * lhs; +} + +// ---------------------------------------------------------------------------------------- + +/* FIXME: this should go into TMatSquareFunctions<> but for some reason + * BASE<T>::col_type is not accessible from there (???) + */ +template<typename T> +CONSTEXPR typename TMat22<T>::col_type PURE diag(const TMat22<T>& m) { + return matrix::diag(m); +} + +} // namespace details + +// ---------------------------------------------------------------------------------------- + +typedef details::TMat22<double> mat2d; +typedef details::TMat22<float> mat2; +typedef details::TMat22<float> mat2f; + +// ---------------------------------------------------------------------------------------- +} // namespace android + +#undef PURE +#undef CONSTEXPR diff --git a/libs/math/include/math/mat3.h b/libs/math/include/math/mat3.h new file mode 100644 index 0000000000..5c8a9b2573 --- /dev/null +++ b/libs/math/include/math/mat3.h @@ -0,0 +1,440 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/quat.h> +#include <math/TMatHelpers.h> +#include <math/vec3.h> +#include <stdint.h> +#include <sys/types.h> + +#define PURE __attribute__((pure)) + +#if __cplusplus >= 201402L +#define CONSTEXPR constexpr +#else +#define CONSTEXPR +#endif + +namespace android { +// ------------------------------------------------------------------------------------- +namespace details { + +template<typename T> +class TQuaternion; + +/** + * A 3x3 column-major matrix class. + * + * Conceptually a 3x3 matrix is a an array of 3 column vec3: + * + * mat3 m = + * \f$ + * \left( + * \begin{array}{ccc} + * m[0] & m[1] & m[2] \\ + * \end{array} + * \right) + * \f$ + * = + * \f$ + * \left( + * \begin{array}{ccc} + * m[0][0] & m[1][0] & m[2][0] \\ + * m[0][1] & m[1][1] & m[2][1] \\ + * m[0][2] & m[1][2] & m[2][2] \\ + * \end{array} + * \right) + * \f$ + * = + * \f$ + * \left( + * \begin{array}{ccc} + * m(0,0) & m(0,1) & m(0,2) \\ + * m(1,0) & m(1,1) & m(1,2) \\ + * m(2,0) & m(2,1) & m(2,2) \\ + * \end{array} + * \right) + * \f$ + * + * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec3. + * + */ +template <typename T> +class TMat33 : public TVecUnaryOperators<TMat33, T>, + public TVecComparisonOperators<TMat33, T>, + public TVecAddOperators<TMat33, T>, + public TMatProductOperators<TMat33, T>, + public TMatSquareFunctions<TMat33, T>, + public TMatTransform<TMat33, T>, + public TMatHelpers<TMat33, T>, + public TMatDebug<TMat33, T> { +public: + enum no_init { NO_INIT }; + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + typedef TVec3<T> col_type; + typedef TVec3<T> row_type; + + static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows) + static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns) + static constexpr size_t NUM_ROWS = COL_SIZE; + static constexpr size_t NUM_COLS = ROW_SIZE; + +private: + /* + * <-- N columns --> + * + * a[0][0] a[1][0] a[2][0] ... a[N][0] ^ + * a[0][1] a[1][1] a[2][1] ... a[N][1] | + * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows + * ... | + * a[0][M] a[1][M] a[2][M] ... a[N][M] v + * + * COL_SIZE = M + * ROW_SIZE = N + * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ] + */ + + col_type m_value[NUM_COLS]; + +public: + // array access + inline constexpr col_type const& operator[](size_t column) const { +#if __cplusplus >= 201402L + // only possible in C++0x14 with constexpr + assert(column < NUM_COLS); +#endif + return m_value[column]; + } + + inline col_type& operator[](size_t column) { + assert(column < NUM_COLS); + return m_value[column]; + } + + // ----------------------------------------------------------------------- + // we want the compiler generated versions for these... + TMat33(const TMat33&) = default; + ~TMat33() = default; + TMat33& operator = (const TMat33&) = default; + + /** + * constructors + */ + + /** + * leaves object uninitialized. use with caution. + */ + explicit constexpr TMat33(no_init) + : m_value{ col_type(col_type::NO_INIT), + col_type(col_type::NO_INIT), + col_type(col_type::NO_INIT) } {} + + + /** + * initialize to identity. + * + * \f$ + * \left( + * \begin{array}{ccc} + * 1 & 0 & 0 \\ + * 0 & 1 & 0 \\ + * 0 & 0 & 1 \\ + * \end{array} + * \right) + * \f$ + */ + CONSTEXPR TMat33(); + + /** + * initialize to Identity*scalar. + * + * \f$ + * \left( + * \begin{array}{ccc} + * v & 0 & 0 \\ + * 0 & v & 0 \\ + * 0 & 0 & v \\ + * \end{array} + * \right) + * \f$ + */ + template<typename U> + explicit CONSTEXPR TMat33(U v); + + /** + * sets the diagonal to a vector. + * + * \f$ + * \left( + * \begin{array}{ccc} + * v[0] & 0 & 0 \\ + * 0 & v[1] & 0 \\ + * 0 & 0 & v[2] \\ + * \end{array} + * \right) + * \f$ + */ + template <typename U> + explicit CONSTEXPR TMat33(const TVec3<U>& v); + + /** + * construct from another matrix of the same size + */ + template <typename U> + explicit CONSTEXPR TMat33(const TMat33<U>& rhs); + + /** + * construct from 3 column vectors. + * + * \f$ + * \left( + * \begin{array}{ccc} + * v0 & v1 & v2 \\ + * \end{array} + * \right) + * \f$ + */ + template <typename A, typename B, typename C> + CONSTEXPR TMat33(const TVec3<A>& v0, const TVec3<B>& v1, const TVec3<C>& v2); + + /** construct from 9 elements in column-major form. + * + * \f$ + * \left( + * \begin{array}{ccc} + * m[0][0] & m[1][0] & m[2][0] \\ + * m[0][1] & m[1][1] & m[2][1] \\ + * m[0][2] & m[1][2] & m[2][2] \\ + * \end{array} + * \right) + * \f$ + */ + template < + typename A, typename B, typename C, + typename D, typename E, typename F, + typename G, typename H, typename I> + CONSTEXPR TMat33( + A m00, B m01, C m02, + D m10, E m11, F m12, + G m20, H m21, I m22); + + /** + * construct from a quaternion + */ + template <typename U> + explicit CONSTEXPR TMat33(const TQuaternion<U>& q); + + /** + * construct from a C array in column major form. + */ + template <typename U> + explicit CONSTEXPR TMat33(U const* rawArray); + + /** + * orthogonalize only works on matrices of size 3x3 + */ + friend inline + CONSTEXPR TMat33 orthogonalize(const TMat33& m) { + TMat33 ret(TMat33::NO_INIT); + ret[0] = normalize(m[0]); + ret[2] = normalize(cross(ret[0], m[1])); + ret[1] = normalize(cross(ret[2], ret[0])); + return ret; + } +}; + +// ---------------------------------------------------------------------------------------- +// Constructors +// ---------------------------------------------------------------------------------------- + +// Since the matrix code could become pretty big quickly, we don't inline most +// operations. + +template <typename T> +CONSTEXPR TMat33<T>::TMat33() { + m_value[0] = col_type(1, 0, 0); + m_value[1] = col_type(0, 1, 0); + m_value[2] = col_type(0, 0, 1); +} + +template <typename T> +template <typename U> +CONSTEXPR TMat33<T>::TMat33(U v) { + m_value[0] = col_type(v, 0, 0); + m_value[1] = col_type(0, v, 0); + m_value[2] = col_type(0, 0, v); +} + +template<typename T> +template<typename U> +CONSTEXPR TMat33<T>::TMat33(const TVec3<U>& v) { + m_value[0] = col_type(v.x, 0, 0); + m_value[1] = col_type(0, v.y, 0); + m_value[2] = col_type(0, 0, v.z); +} + +// construct from 9 scalars. Note that the arrangement +// of values in the constructor is the transpose of the matrix +// notation. +template<typename T> +template < + typename A, typename B, typename C, + typename D, typename E, typename F, + typename G, typename H, typename I> +CONSTEXPR TMat33<T>::TMat33( + A m00, B m01, C m02, + D m10, E m11, F m12, + G m20, H m21, I m22) { + m_value[0] = col_type(m00, m01, m02); + m_value[1] = col_type(m10, m11, m12); + m_value[2] = col_type(m20, m21, m22); +} + +template <typename T> +template <typename U> +CONSTEXPR TMat33<T>::TMat33(const TMat33<U>& rhs) { + for (size_t col = 0; col < NUM_COLS; ++col) { + m_value[col] = col_type(rhs[col]); + } +} + +// Construct from 3 column vectors. +template <typename T> +template <typename A, typename B, typename C> +CONSTEXPR TMat33<T>::TMat33(const TVec3<A>& v0, const TVec3<B>& v1, const TVec3<C>& v2) { + m_value[0] = v0; + m_value[1] = v1; + m_value[2] = v2; +} + +// Construct from raw array, in column-major form. +template <typename T> +template <typename U> +CONSTEXPR TMat33<T>::TMat33(U const* rawArray) { + for (size_t col = 0; col < NUM_COLS; ++col) { + for (size_t row = 0; row < NUM_ROWS; ++row) { + m_value[col][row] = *rawArray++; + } + } +} + +template <typename T> +template <typename U> +CONSTEXPR TMat33<T>::TMat33(const TQuaternion<U>& q) { + const U n = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w; + const U s = n > 0 ? 2/n : 0; + const U x = s*q.x; + const U y = s*q.y; + const U z = s*q.z; + const U xx = x*q.x; + const U xy = x*q.y; + const U xz = x*q.z; + const U xw = x*q.w; + const U yy = y*q.y; + const U yz = y*q.z; + const U yw = y*q.w; + const U zz = z*q.z; + const U zw = z*q.w; + m_value[0] = col_type(1-yy-zz, xy+zw, xz-yw); // NOLINT + m_value[1] = col_type( xy-zw, 1-xx-zz, yz+xw); // NOLINT + m_value[2] = col_type( xz+yw, yz-xw, 1-xx-yy); // NOLINT +} + +// ---------------------------------------------------------------------------------------- +// Arithmetic operators outside of class +// ---------------------------------------------------------------------------------------- + +/* We use non-friend functions here to prevent the compiler from using + * implicit conversions, for instance of a scalar to a vector. The result would + * not be what the caller expects. + * + * Also note that the order of the arguments in the inner loop is important since + * it determines the output type (only relevant when T != U). + */ + +// matrix * column-vector, result is a vector of the same type than the input vector +template <typename T, typename U> +CONSTEXPR typename TMat33<U>::col_type PURE operator *(const TMat33<T>& lhs, const TVec3<U>& rhs) { + // Result is initialized to zero. + typename TMat33<U>::col_type result; + for (size_t col = 0; col < TMat33<T>::NUM_COLS; ++col) { + result += lhs[col] * rhs[col]; + } + return result; +} + +// row-vector * matrix, result is a vector of the same type than the input vector +template <typename T, typename U> +CONSTEXPR typename TMat33<U>::row_type PURE operator *(const TVec3<U>& lhs, const TMat33<T>& rhs) { + typename TMat33<U>::row_type result(TMat33<U>::row_type::NO_INIT); + for (size_t col = 0; col < TMat33<T>::NUM_COLS; ++col) { + result[col] = dot(lhs, rhs[col]); + } + return result; +} + +// matrix * scalar, result is a matrix of the same type than the input matrix +template<typename T, typename U> +constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat33<T>>::type PURE +operator*(TMat33<T> lhs, U rhs) { + return lhs *= rhs; +} + +// scalar * matrix, result is a matrix of the same type than the input matrix +template<typename T, typename U> +constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat33<T>>::type PURE +operator*(U lhs, const TMat33<T>& rhs) { + return rhs * lhs; +} + +//------------------------------------------------------------------------------ +template <typename T> +CONSTEXPR TMat33<T> orthogonalize(const TMat33<T>& m) { + TMat33<T> ret(TMat33<T>::NO_INIT); + ret[0] = normalize(m[0]); + ret[2] = normalize(cross(ret[0], m[1])); + ret[1] = normalize(cross(ret[2], ret[0])); + return ret; +} + +// ---------------------------------------------------------------------------------------- + +/* FIXME: this should go into TMatSquareFunctions<> but for some reason + * BASE<T>::col_type is not accessible from there (???) + */ +template<typename T> +CONSTEXPR typename TMat33<T>::col_type PURE diag(const TMat33<T>& m) { + return matrix::diag(m); +} + +} // namespace details + +// ---------------------------------------------------------------------------------------- + +typedef details::TMat33<double> mat3d; +typedef details::TMat33<float> mat3; +typedef details::TMat33<float> mat3f; + +// ---------------------------------------------------------------------------------------- +} // namespace android + +#undef PURE +#undef CONSTEXPR diff --git a/libs/math/include/math/mat4.h b/libs/math/include/math/mat4.h new file mode 100644 index 0000000000..6119ba7f68 --- /dev/null +++ b/libs/math/include/math/mat4.h @@ -0,0 +1,586 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/mat3.h> +#include <math/quat.h> +#include <math/TMatHelpers.h> +#include <math/vec3.h> +#include <math/vec4.h> + +#include <stdint.h> +#include <sys/types.h> +#include <limits> + +#define PURE __attribute__((pure)) + +#if __cplusplus >= 201402L +#define CONSTEXPR constexpr +#else +#define CONSTEXPR +#endif + +namespace android { +// ------------------------------------------------------------------------------------- +namespace details { + +template<typename T> +class TQuaternion; + +/** + * A 4x4 column-major matrix class. + * + * Conceptually a 4x4 matrix is a an array of 4 column double4: + * + * mat4 m = + * \f$ + * \left( + * \begin{array}{cccc} + * m[0] & m[1] & m[2] & m[3] \\ + * \end{array} + * \right) + * \f$ + * = + * \f$ + * \left( + * \begin{array}{cccc} + * m[0][0] & m[1][0] & m[2][0] & m[3][0] \\ + * m[0][1] & m[1][1] & m[2][1] & m[3][1] \\ + * m[0][2] & m[1][2] & m[2][2] & m[3][2] \\ + * m[0][3] & m[1][3] & m[2][3] & m[3][3] \\ + * \end{array} + * \right) + * \f$ + * = + * \f$ + * \left( + * \begin{array}{cccc} + * m(0,0) & m(0,1) & m(0,2) & m(0,3) \\ + * m(1,0) & m(1,1) & m(1,2) & m(1,3) \\ + * m(2,0) & m(2,1) & m(2,2) & m(2,3) \\ + * m(3,0) & m(3,1) & m(3,2) & m(3,3) \\ + * \end{array} + * \right) + * \f$ + * + * m[n] is the \f$ n^{th} \f$ column of the matrix and is a double4. + * + */ +template <typename T> +class TMat44 : public TVecUnaryOperators<TMat44, T>, + public TVecComparisonOperators<TMat44, T>, + public TVecAddOperators<TMat44, T>, + public TMatProductOperators<TMat44, T>, + public TMatSquareFunctions<TMat44, T>, + public TMatTransform<TMat44, T>, + public TMatHelpers<TMat44, T>, + public TMatDebug<TMat44, T> { +public: + enum no_init { NO_INIT }; + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + typedef TVec4<T> col_type; + typedef TVec4<T> row_type; + + static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows) + static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns) + static constexpr size_t NUM_ROWS = COL_SIZE; + static constexpr size_t NUM_COLS = ROW_SIZE; + +private: + /* + * <-- N columns --> + * + * a[0][0] a[1][0] a[2][0] ... a[N][0] ^ + * a[0][1] a[1][1] a[2][1] ... a[N][1] | + * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows + * ... | + * a[0][M] a[1][M] a[2][M] ... a[N][M] v + * + * COL_SIZE = M + * ROW_SIZE = N + * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ] + */ + + col_type m_value[NUM_COLS]; + +public: + // array access + inline constexpr col_type const& operator[](size_t column) const { +#if __cplusplus >= 201402L + // only possible in C++0x14 with constexpr + assert(column < NUM_COLS); +#endif + return m_value[column]; + } + + inline col_type& operator[](size_t column) { + assert(column < NUM_COLS); + return m_value[column]; + } + + // ----------------------------------------------------------------------- + // we want the compiler generated versions for these... + TMat44(const TMat44&) = default; + ~TMat44() = default; + TMat44& operator = (const TMat44&) = default; + + /* + * constructors + */ + + // leaves object uninitialized. use with caution. + explicit constexpr TMat44(no_init) + : m_value{ col_type(col_type::NO_INIT), + col_type(col_type::NO_INIT), + col_type(col_type::NO_INIT), + col_type(col_type::NO_INIT) } {} + + /** initialize to identity. + * + * \f$ + * \left( + * \begin{array}{cccc} + * 1 & 0 & 0 & 0 \\ + * 0 & 1 & 0 & 0 \\ + * 0 & 0 & 1 & 0 \\ + * 0 & 0 & 0 & 1 \\ + * \end{array} + * \right) + * \f$ + */ + CONSTEXPR TMat44(); + + /** initialize to Identity*scalar. + * + * \f$ + * \left( + * \begin{array}{cccc} + * v & 0 & 0 & 0 \\ + * 0 & v & 0 & 0 \\ + * 0 & 0 & v & 0 \\ + * 0 & 0 & 0 & v \\ + * \end{array} + * \right) + * \f$ + */ + template<typename U> + explicit CONSTEXPR TMat44(U v); + + /** sets the diagonal to a vector. + * + * \f$ + * \left( + * \begin{array}{cccc} + * v[0] & 0 & 0 & 0 \\ + * 0 & v[1] & 0 & 0 \\ + * 0 & 0 & v[2] & 0 \\ + * 0 & 0 & 0 & v[3] \\ + * \end{array} + * \right) + * \f$ + */ + template <typename U> + explicit CONSTEXPR TMat44(const TVec4<U>& v); + + // construct from another matrix of the same size + template <typename U> + explicit CONSTEXPR TMat44(const TMat44<U>& rhs); + + /** construct from 4 column vectors. + * + * \f$ + * \left( + * \begin{array}{cccc} + * v0 & v1 & v2 & v3 \\ + * \end{array} + * \right) + * \f$ + */ + template <typename A, typename B, typename C, typename D> + CONSTEXPR TMat44(const TVec4<A>& v0, const TVec4<B>& v1, const TVec4<C>& v2, const TVec4<D>& v3); + + /** construct from 16 elements in column-major form. + * + * \f$ + * \left( + * \begin{array}{cccc} + * m[0][0] & m[1][0] & m[2][0] & m[3][0] \\ + * m[0][1] & m[1][1] & m[2][1] & m[3][1] \\ + * m[0][2] & m[1][2] & m[2][2] & m[3][2] \\ + * m[0][3] & m[1][3] & m[2][3] & m[3][3] \\ + * \end{array} + * \right) + * \f$ + */ + template < + typename A, typename B, typename C, typename D, + typename E, typename F, typename G, typename H, + typename I, typename J, typename K, typename L, + typename M, typename N, typename O, typename P> + CONSTEXPR TMat44( + A m00, B m01, C m02, D m03, + E m10, F m11, G m12, H m13, + I m20, J m21, K m22, L m23, + M m30, N m31, O m32, P m33); + + /** + * construct from a quaternion + */ + template <typename U> + explicit CONSTEXPR TMat44(const TQuaternion<U>& q); + + /** + * construct from a C array in column major form. + */ + template <typename U> + explicit CONSTEXPR TMat44(U const* rawArray); + + /** + * construct from a 3x3 matrix + */ + template <typename U> + explicit CONSTEXPR TMat44(const TMat33<U>& matrix); + + /** + * construct from a 3x3 matrix and 3d translation + */ + template <typename U, typename V> + CONSTEXPR TMat44(const TMat33<U>& matrix, const TVec3<V>& translation); + + /** + * construct from a 3x3 matrix and 4d last column. + */ + template <typename U, typename V> + CONSTEXPR TMat44(const TMat33<U>& matrix, const TVec4<V>& column3); + + /* + * helpers + */ + + static CONSTEXPR TMat44 ortho(T left, T right, T bottom, T top, T near, T far); + + static CONSTEXPR TMat44 frustum(T left, T right, T bottom, T top, T near, T far); + + enum class Fov { + HORIZONTAL, + VERTICAL + }; + static CONSTEXPR TMat44 perspective(T fov, T aspect, T near, T far, Fov direction = Fov::VERTICAL); + + template <typename A, typename B, typename C> + static CONSTEXPR TMat44 lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up); + + template <typename A> + static CONSTEXPR TVec3<A> project(const TMat44& projectionMatrix, TVec3<A> vertice) { + TVec4<A> r = projectionMatrix * TVec4<A>{ vertice, 1 }; + return r.xyz / r.w; + } + + template <typename A> + static CONSTEXPR TVec4<A> project(const TMat44& projectionMatrix, TVec4<A> vertice) { + vertice = projectionMatrix * vertice; + return { vertice.xyz / vertice.w, 1 }; + } + + /** + * Constructs a 3x3 matrix from the upper-left corner of this 4x4 matrix + */ + inline constexpr TMat33<T> upperLeft() const { + return TMat33<T>(m_value[0].xyz, m_value[1].xyz, m_value[2].xyz); + } +}; + +// ---------------------------------------------------------------------------------------- +// Constructors +// ---------------------------------------------------------------------------------------- + +// Since the matrix code could become pretty big quickly, we don't inline most +// operations. + +template <typename T> +CONSTEXPR TMat44<T>::TMat44() { + m_value[0] = col_type(1, 0, 0, 0); + m_value[1] = col_type(0, 1, 0, 0); + m_value[2] = col_type(0, 0, 1, 0); + m_value[3] = col_type(0, 0, 0, 1); +} + +template <typename T> +template <typename U> +CONSTEXPR TMat44<T>::TMat44(U v) { + m_value[0] = col_type(v, 0, 0, 0); + m_value[1] = col_type(0, v, 0, 0); + m_value[2] = col_type(0, 0, v, 0); + m_value[3] = col_type(0, 0, 0, v); +} + +template<typename T> +template<typename U> +CONSTEXPR TMat44<T>::TMat44(const TVec4<U>& v) { + m_value[0] = col_type(v.x, 0, 0, 0); + m_value[1] = col_type(0, v.y, 0, 0); + m_value[2] = col_type(0, 0, v.z, 0); + m_value[3] = col_type(0, 0, 0, v.w); +} + +// construct from 16 scalars +template<typename T> +template < + typename A, typename B, typename C, typename D, + typename E, typename F, typename G, typename H, + typename I, typename J, typename K, typename L, + typename M, typename N, typename O, typename P> +CONSTEXPR TMat44<T>::TMat44( + A m00, B m01, C m02, D m03, + E m10, F m11, G m12, H m13, + I m20, J m21, K m22, L m23, + M m30, N m31, O m32, P m33) { + m_value[0] = col_type(m00, m01, m02, m03); + m_value[1] = col_type(m10, m11, m12, m13); + m_value[2] = col_type(m20, m21, m22, m23); + m_value[3] = col_type(m30, m31, m32, m33); +} + +template <typename T> +template <typename U> +CONSTEXPR TMat44<T>::TMat44(const TMat44<U>& rhs) { + for (size_t col = 0; col < NUM_COLS; ++col) { + m_value[col] = col_type(rhs[col]); + } +} + +// Construct from 4 column vectors. +template <typename T> +template <typename A, typename B, typename C, typename D> +CONSTEXPR TMat44<T>::TMat44( + const TVec4<A>& v0, const TVec4<B>& v1, + const TVec4<C>& v2, const TVec4<D>& v3) { + m_value[0] = col_type(v0); + m_value[1] = col_type(v1); + m_value[2] = col_type(v2); + m_value[3] = col_type(v3); +} + +// Construct from raw array, in column-major form. +template <typename T> +template <typename U> +CONSTEXPR TMat44<T>::TMat44(U const* rawArray) { + for (size_t col = 0; col < NUM_COLS; ++col) { + for (size_t row = 0; row < NUM_ROWS; ++row) { + m_value[col][row] = *rawArray++; + } + } +} + +template <typename T> +template <typename U> +CONSTEXPR TMat44<T>::TMat44(const TQuaternion<U>& q) { + const U n = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w; + const U s = n > 0 ? 2/n : 0; + const U x = s*q.x; + const U y = s*q.y; + const U z = s*q.z; + const U xx = x*q.x; + const U xy = x*q.y; + const U xz = x*q.z; + const U xw = x*q.w; + const U yy = y*q.y; + const U yz = y*q.z; + const U yw = y*q.w; + const U zz = z*q.z; + const U zw = z*q.w; + m_value[0] = col_type(1-yy-zz, xy+zw, xz-yw, 0); + m_value[1] = col_type( xy-zw, 1-xx-zz, yz+xw, 0); // NOLINT + m_value[2] = col_type( xz+yw, yz-xw, 1-xx-yy, 0); // NOLINT + m_value[3] = col_type( 0, 0, 0, 1); // NOLINT +} + +template <typename T> +template <typename U> +CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m) { + m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0); + m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0); + m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0); + m_value[3] = col_type( 0, 0, 0, 1); // NOLINT +} + +template <typename T> +template <typename U, typename V> +CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m, const TVec3<V>& v) { + m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0); + m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0); + m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0); + m_value[3] = col_type( v[0], v[1], v[2], 1); // NOLINT +} + +template <typename T> +template <typename U, typename V> +CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m, const TVec4<V>& v) { + m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0); // NOLINT + m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0); // NOLINT + m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0); // NOLINT + m_value[3] = col_type( v[0], v[1], v[2], v[3]); // NOLINT +} + +// ---------------------------------------------------------------------------------------- +// Helpers +// ---------------------------------------------------------------------------------------- + +template <typename T> +CONSTEXPR TMat44<T> TMat44<T>::ortho(T left, T right, T bottom, T top, T near, T far) { + TMat44<T> m; + m[0][0] = 2 / (right - left); + m[1][1] = 2 / (top - bottom); + m[2][2] = -2 / (far - near); + m[3][0] = -(right + left) / (right - left); + m[3][1] = -(top + bottom) / (top - bottom); + m[3][2] = -(far + near) / (far - near); + return m; +} + +template <typename T> +CONSTEXPR TMat44<T> TMat44<T>::frustum(T left, T right, T bottom, T top, T near, T far) { + TMat44<T> m; + m[0][0] = (2 * near) / (right - left); + m[1][1] = (2 * near) / (top - bottom); + m[2][0] = (right + left) / (right - left); + m[2][1] = (top + bottom) / (top - bottom); + m[2][2] = -(far + near) / (far - near); + m[2][3] = -1; + m[3][2] = -(2 * far * near) / (far - near); + m[3][3] = 0; + return m; +} + +template <typename T> +CONSTEXPR TMat44<T> TMat44<T>::perspective(T fov, T aspect, T near, T far, TMat44::Fov direction) { + T h; + T w; + + if (direction == TMat44::Fov::VERTICAL) { + h = std::tan(fov * M_PI / 360.0f) * near; + w = h * aspect; + } else { + w = std::tan(fov * M_PI / 360.0f) * near; + h = w / aspect; + } + return frustum(-w, w, -h, h, near, far); +} + +/* + * Returns a matrix representing the pose of a virtual camera looking towards -Z in its + * local Y-up coordinate system. "eye" is where the camera is located, "center" is the points its + * looking at and "up" defines where the Y axis of the camera's local coordinate system is. + */ +template <typename T> +template <typename A, typename B, typename C> +CONSTEXPR TMat44<T> TMat44<T>::lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up) { + TVec3<T> z_axis(normalize(center - eye)); + TVec3<T> norm_up(normalize(up)); + if (std::abs(dot(z_axis, norm_up)) > 0.999) { + // Fix up vector if we're degenerate (looking straight up, basically) + norm_up = { norm_up.z, norm_up.x, norm_up.y }; + } + TVec3<T> x_axis(normalize(cross(z_axis, norm_up))); + TVec3<T> y_axis(cross(x_axis, z_axis)); + return TMat44<T>( + TVec4<T>(x_axis, 0), + TVec4<T>(y_axis, 0), + TVec4<T>(-z_axis, 0), + TVec4<T>(eye, 1)); +} + +// ---------------------------------------------------------------------------------------- +// Arithmetic operators outside of class +// ---------------------------------------------------------------------------------------- + +/* We use non-friend functions here to prevent the compiler from using + * implicit conversions, for instance of a scalar to a vector. The result would + * not be what the caller expects. + * + * Also note that the order of the arguments in the inner loop is important since + * it determines the output type (only relevant when T != U). + */ + +// matrix * column-vector, result is a vector of the same type than the input vector +template <typename T, typename U> +CONSTEXPR typename TMat44<T>::col_type PURE operator *(const TMat44<T>& lhs, const TVec4<U>& rhs) { + // Result is initialized to zero. + typename TMat44<T>::col_type result; + for (size_t col = 0; col < TMat44<T>::NUM_COLS; ++col) { + result += lhs[col] * rhs[col]; + } + return result; +} + +// mat44 * vec3, result is vec3( mat44 * {vec3, 1} ) +template <typename T, typename U> +CONSTEXPR typename TMat44<T>::col_type PURE operator *(const TMat44<T>& lhs, const TVec3<U>& rhs) { + return lhs * TVec4<U>{ rhs, 1 }; +} + + +// row-vector * matrix, result is a vector of the same type than the input vector +template <typename T, typename U> +CONSTEXPR typename TMat44<U>::row_type PURE operator *(const TVec4<U>& lhs, const TMat44<T>& rhs) { + typename TMat44<U>::row_type result(TMat44<U>::row_type::NO_INIT); + for (size_t col = 0; col < TMat44<T>::NUM_COLS; ++col) { + result[col] = dot(lhs, rhs[col]); + } + return result; +} + +// matrix * scalar, result is a matrix of the same type than the input matrix +template <typename T, typename U> +constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat44<T>>::type PURE +operator *(TMat44<T> lhs, U rhs) { + return lhs *= rhs; +} + +// scalar * matrix, result is a matrix of the same type than the input matrix +template <typename T, typename U> +constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat44<T>>::type PURE +operator *(U lhs, const TMat44<T>& rhs) { + return rhs * lhs; +} + +// ---------------------------------------------------------------------------------------- + +/* FIXME: this should go into TMatSquareFunctions<> but for some reason + * BASE<T>::col_type is not accessible from there (???) + */ +template<typename T> +typename TMat44<T>::col_type PURE diag(const TMat44<T>& m) { + return matrix::diag(m); +} + +} // namespace details + +// ---------------------------------------------------------------------------------------- + +typedef details::TMat44<double> mat4d; +typedef details::TMat44<float> mat4; +typedef details::TMat44<float> mat4f; + +// ---------------------------------------------------------------------------------------- +} // namespace android + +#undef PURE +#undef CONSTEXPR diff --git a/libs/math/include/math/quat.h b/libs/math/include/math/quat.h new file mode 100644 index 0000000000..1936a2baec --- /dev/null +++ b/libs/math/include/math/quat.h @@ -0,0 +1,192 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/half.h> +#include <math/TQuatHelpers.h> +#include <math/vec3.h> +#include <math/vec4.h> + +#include <stdint.h> +#include <sys/types.h> + +#ifndef PURE +#define PURE __attribute__((pure)) +#endif + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +#pragma clang diagnostic ignored "-Wnested-anon-types" + +namespace android { +// ------------------------------------------------------------------------------------- + +namespace details { + +template <typename T> +class TQuaternion : public TVecAddOperators<TQuaternion, T>, + public TVecUnaryOperators<TQuaternion, T>, + public TVecComparisonOperators<TQuaternion, T>, + public TQuatProductOperators<TQuaternion, T>, + public TQuatFunctions<TQuaternion, T>, + public TQuatDebug<TQuaternion, T> { +public: + enum no_init { NO_INIT }; + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + + /* + * quaternion internals stored as: + * + * q = w + xi + yj + zk + * + * q[0] = x; + * q[1] = y; + * q[2] = z; + * q[3] = w; + * + */ + union { + struct { T x, y, z, w; }; + TVec4<T> xyzw; + TVec3<T> xyz; + TVec2<T> xy; + }; + + enum { SIZE = 4 }; + inline constexpr static size_type size() { return SIZE; } + + // array access + inline constexpr T const& operator[](size_t i) const { +#if __cplusplus >= 201402L + // only possible in C++0x14 with constexpr + assert(i < SIZE); +#endif + return (&x)[i]; + } + + inline T& operator[](size_t i) { + assert(i < SIZE); + return (&x)[i]; + } + + // ----------------------------------------------------------------------- + // we want the compiler generated versions for these... + TQuaternion(const TQuaternion&) = default; + ~TQuaternion() = default; + TQuaternion& operator = (const TQuaternion&) = default; + + // constructors + + // leaves object uninitialized. use with caution. + explicit + constexpr TQuaternion(no_init) : xyzw(TVec4<T>::NO_INIT) {} + + // default constructor. sets all values to zero. + constexpr TQuaternion() : x(0), y(0), z(0), w(0) { } + + // handles implicit conversion to a tvec4. must not be explicit. + template<typename A> + constexpr TQuaternion(A w) : x(0), y(0), z(0), w(w) { + static_assert(std::is_arithmetic<A>::value, "requires arithmetic type"); + } + + // initialize from 4 values to w + xi + yj + zk + template<typename A, typename B, typename C, typename D> + constexpr TQuaternion(A w, B x, C y, D z) : x(x), y(y), z(z), w(w) { } + + // initialize from a vec3 + a value to : v.xi + v.yj + v.zk + w + template<typename A, typename B> + constexpr TQuaternion(const TVec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { } + + // initialize from a double4 + template<typename A> + constexpr explicit TQuaternion(const TVec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { } + + // initialize from a quaternion of a different type + template<typename A> + constexpr explicit TQuaternion(const TQuaternion<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { } + + // conjugate operator + constexpr TQuaternion operator~() const { + return conj(*this); + } + + // constructs a quaternion from an axis and angle + template <typename A, typename B> + constexpr static TQuaternion PURE fromAxisAngle(const TVec3<A>& axis, B angle) { + return TQuaternion(std::sin(angle*0.5) * normalize(axis), std::cos(angle*0.5)); + } +}; + +} // namespace details + +// ---------------------------------------------------------------------------------------- + +typedef details::TQuaternion<double> quatd; +typedef details::TQuaternion<float> quat; +typedef details::TQuaternion<float> quatf; +typedef details::TQuaternion<half> quath; + +constexpr inline quat operator"" _i(long double v) { + return quat(0, static_cast<float>(v), 0, 0); +} +constexpr inline quat operator"" _j(long double v) { + return quat(0, 0, static_cast<float>(v), 0); +} +constexpr inline quat operator"" _k(long double v) { + return quat(0, 0, 0, static_cast<float>(v)); +} + +constexpr inline quat operator"" _i(unsigned long long v) { // NOLINT + return quat(0, static_cast<float>(v), 0, 0); +} +constexpr inline quat operator"" _j(unsigned long long v) { // NOLINT + return quat(0, 0, static_cast<float>(v), 0); +} +constexpr inline quat operator"" _k(unsigned long long v) { // NOLINT + return quat(0, 0, 0, static_cast<float>(v)); +} + +constexpr inline quatd operator"" _id(long double v) { + return quatd(0, static_cast<double>(v), 0, 0); +} +constexpr inline quatd operator"" _jd(long double v) { + return quatd(0, 0, static_cast<double>(v), 0); +} +constexpr inline quatd operator"" _kd(long double v) { + return quatd(0, 0, 0, static_cast<double>(v)); +} + +constexpr inline quatd operator"" _id(unsigned long long v) { // NOLINT + return quatd(0, static_cast<double>(v), 0, 0); +} +constexpr inline quatd operator"" _jd(unsigned long long v) { // NOLINT + return quatd(0, 0, static_cast<double>(v), 0); +} +constexpr inline quatd operator"" _kd(unsigned long long v) { // NOLINT + return quatd(0, 0, 0, static_cast<double>(v)); +} + +// ---------------------------------------------------------------------------------------- +} // namespace android + +#pragma clang diagnostic pop + +#undef PURE diff --git a/libs/math/include/math/scalar.h b/libs/math/include/math/scalar.h new file mode 100644 index 0000000000..2eced92e76 --- /dev/null +++ b/libs/math/include/math/scalar.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <algorithm> +#include <cmath> + +namespace android { + +template<typename T> +static constexpr T saturate(T v) noexcept { + return T(std::min(T(1), std::max(T(0), v))); +} + +template<typename T> +static constexpr T clamp(T v, T min, T max) noexcept { + return T(std::min(max, std::max(min, v))); +} + +template<typename T> +static constexpr T mix(T x, T y, T a) noexcept { + return x * (T(1) - a) + y * a; +} + +template<typename T> +static constexpr T lerp(T x, T y, T a) noexcept { + return mix(x, y, a); +} + +} // namespace std diff --git a/libs/math/include/math/vec2.h b/libs/math/include/math/vec2.h new file mode 100644 index 0000000000..a34763347c --- /dev/null +++ b/libs/math/include/math/vec2.h @@ -0,0 +1,125 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/TVecHelpers.h> +#include <math/half.h> +#include <assert.h> +#include <stdint.h> +#include <sys/types.h> +#include <type_traits> + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +#pragma clang diagnostic ignored "-Wnested-anon-types" + +namespace android { +// ------------------------------------------------------------------------------------- + +namespace details { + +template <typename T> +class TVec2 : public TVecProductOperators<TVec2, T>, + public TVecAddOperators<TVec2, T>, + public TVecUnaryOperators<TVec2, T>, + public TVecComparisonOperators<TVec2, T>, + public TVecFunctions<TVec2, T>, + public TVecDebug<TVec2, T> { +public: + enum no_init { NO_INIT }; + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + + union { + struct { T x, y; }; + struct { T s, t; }; + struct { T r, g; }; + }; + + static constexpr size_t SIZE = 2; + inline constexpr size_type size() const { return SIZE; } + + // array access + inline constexpr T const& operator[](size_t i) const { +#if __cplusplus >= 201402L + // only possible in C++0x14 with constexpr + assert(i < SIZE); +#endif + return (&x)[i]; + } + + inline T& operator[](size_t i) { + assert(i < SIZE); + return (&x)[i]; + } + + // ----------------------------------------------------------------------- + // we want the compiler generated versions for these... + TVec2(const TVec2&) = default; + ~TVec2() = default; + TVec2& operator = (const TVec2&) = default; + + // constructors + + // leaves object uninitialized. use with caution. + explicit + constexpr TVec2(no_init) { } + + // default constructor + constexpr TVec2() : x(0), y(0) { } + + // handles implicit conversion to a tvec4. must not be explicit. + template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type> + constexpr TVec2(A v) : x(v), y(v) { } + + template<typename A, typename B> + constexpr TVec2(A x, B y) : x(x), y(y) { } + + template<typename A> + explicit + constexpr TVec2(const TVec2<A>& v) : x(v.x), y(v.y) { } + + // cross product works only on vectors of size 2 or 3 + template<typename RT> + friend inline + constexpr value_type cross(const TVec2& u, const TVec2<RT>& v) { + return value_type(u.x*v.y - u.y*v.x); + } +}; + +} // namespace details + +// ---------------------------------------------------------------------------------------- + +typedef details::TVec2<double> double2; +typedef details::TVec2<float> float2; +typedef details::TVec2<float> vec2; +typedef details::TVec2<half> half2; +typedef details::TVec2<int32_t> int2; +typedef details::TVec2<uint32_t> uint2; +typedef details::TVec2<int16_t> short2; +typedef details::TVec2<uint16_t> ushort2; +typedef details::TVec2<int8_t> byte2; +typedef details::TVec2<uint8_t> ubyte2; +typedef details::TVec2<bool> bool2; + +// ---------------------------------------------------------------------------------------- +} // namespace android + +#pragma clang diagnostic pop diff --git a/libs/math/include/math/vec3.h b/libs/math/include/math/vec3.h new file mode 100644 index 0000000000..009fd84e3b --- /dev/null +++ b/libs/math/include/math/vec3.h @@ -0,0 +1,131 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/vec2.h> +#include <math/half.h> +#include <stdint.h> +#include <sys/types.h> + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +#pragma clang diagnostic ignored "-Wnested-anon-types" + +namespace android { +// ------------------------------------------------------------------------------------- + +namespace details { + +template <typename T> +class TVec3 : public TVecProductOperators<TVec3, T>, + public TVecAddOperators<TVec3, T>, + public TVecUnaryOperators<TVec3, T>, + public TVecComparisonOperators<TVec3, T>, + public TVecFunctions<TVec3, T>, + public TVecDebug<TVec3, T> { +public: + enum no_init { NO_INIT }; + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + + union { + struct { T x, y, z; }; + struct { T s, t, p; }; + struct { T r, g, b; }; + TVec2<T> xy; + TVec2<T> st; + TVec2<T> rg; + }; + + static constexpr size_t SIZE = 3; + inline constexpr size_type size() const { return SIZE; } + + // array access + inline constexpr T const& operator[](size_t i) const { +#if __cplusplus >= 201402L + // only possible in C++0x14 with constexpr + assert(i < SIZE); +#endif + return (&x)[i]; + } + + inline T& operator[](size_t i) { + assert(i < SIZE); + return (&x)[i]; + } + + // ----------------------------------------------------------------------- + // we want the compiler generated versions for these... + TVec3(const TVec3&) = default; + ~TVec3() = default; + TVec3& operator = (const TVec3&) = default; + + // constructors + // leaves object uninitialized. use with caution. + explicit + constexpr TVec3(no_init) { } + + // default constructor + constexpr TVec3() : x(0), y(0), z(0) { } + + // handles implicit conversion to a tvec4. must not be explicit. + template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type> + constexpr TVec3(A v) : x(v), y(v), z(v) { } + + template<typename A, typename B, typename C> + constexpr TVec3(A x, B y, C z) : x(x), y(y), z(z) { } + + template<typename A, typename B> + constexpr TVec3(const TVec2<A>& v, B z) : x(v.x), y(v.y), z(z) { } + + template<typename A> + explicit + constexpr TVec3(const TVec3<A>& v) : x(v.x), y(v.y), z(v.z) { } + + // cross product works only on vectors of size 3 + template <typename RT> + friend inline + constexpr TVec3 cross(const TVec3& u, const TVec3<RT>& v) { + return TVec3( + u.y*v.z - u.z*v.y, + u.z*v.x - u.x*v.z, + u.x*v.y - u.y*v.x); + } +}; + +} // namespace details + +// ---------------------------------------------------------------------------------------- + +typedef details::TVec3<double> double3; +typedef details::TVec3<float> float3; +typedef details::TVec3<float> vec3; +typedef details::TVec3<half> half3; +typedef details::TVec3<int32_t> int3; +typedef details::TVec3<uint32_t> uint3; +typedef details::TVec3<int16_t> short3; +typedef details::TVec3<uint16_t> ushort3; +typedef details::TVec3<int8_t> byte3; +typedef details::TVec3<uint8_t> ubyte3; +typedef details::TVec3<bool> bool3; + +// ---------------------------------------------------------------------------------------- +} // namespace android + +#pragma clang diagnostic pop diff --git a/libs/math/include/math/vec4.h b/libs/math/include/math/vec4.h new file mode 100644 index 0000000000..1e279fe3f2 --- /dev/null +++ b/libs/math/include/math/vec4.h @@ -0,0 +1,128 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/vec3.h> +#include <math/half.h> +#include <stdint.h> +#include <sys/types.h> + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +#pragma clang diagnostic ignored "-Wnested-anon-types" + +namespace android { +// ------------------------------------------------------------------------------------- + +namespace details { + +template <typename T> +class TVec4 : public TVecProductOperators<TVec4, T>, + public TVecAddOperators<TVec4, T>, + public TVecUnaryOperators<TVec4, T>, + public TVecComparisonOperators<TVec4, T>, + public TVecFunctions<TVec4, T>, + public TVecDebug<TVec4, T> { +public: + enum no_init { NO_INIT }; + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + + union { + struct { T x, y, z, w; }; + struct { T s, t, p, q; }; + struct { T r, g, b, a; }; + TVec2<T> xy; + TVec2<T> st; + TVec2<T> rg; + TVec3<T> xyz; + TVec3<T> stp; + TVec3<T> rgb; + }; + + static constexpr size_t SIZE = 4; + inline constexpr size_type size() const { return SIZE; } + + // array access + inline constexpr T const& operator[](size_t i) const { +#if __cplusplus >= 201402L + // only possible in C++0x14 with constexpr + assert(i < SIZE); +#endif + return (&x)[i]; + } + + inline T& operator[](size_t i) { + assert(i < SIZE); + return (&x)[i]; + } + + // ----------------------------------------------------------------------- + // we want the compiler generated versions for these... + TVec4(const TVec4&) = default; + ~TVec4() = default; + TVec4& operator = (const TVec4&) = default; + + // constructors + + // leaves object uninitialized. use with caution. + explicit + constexpr TVec4(no_init) { } + + // default constructor + constexpr TVec4() : x(0), y(0), z(0), w(0) { } + + // handles implicit conversion to a tvec4. must not be explicit. + template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type> + constexpr TVec4(A v) : x(v), y(v), z(v), w(v) { } + + template<typename A, typename B, typename C, typename D> + constexpr TVec4(A x, B y, C z, D w) : x(x), y(y), z(z), w(w) { } + + template<typename A, typename B, typename C> + constexpr TVec4(const TVec2<A>& v, B z, C w) : x(v.x), y(v.y), z(z), w(w) { } + + template<typename A, typename B> + constexpr TVec4(const TVec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { } + + template<typename A> + explicit + constexpr TVec4(const TVec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { } +}; + +} // namespace details + +// ---------------------------------------------------------------------------------------- + +typedef details::TVec4<double> double4; +typedef details::TVec4<float> float4; +typedef details::TVec4<float> vec4; +typedef details::TVec4<half> half4; +typedef details::TVec4<int32_t> int4; +typedef details::TVec4<uint32_t> uint4; +typedef details::TVec4<int16_t> short4; +typedef details::TVec4<uint16_t> ushort4; +typedef details::TVec4<int8_t> byte4; +typedef details::TVec4<uint8_t> ubyte4; +typedef details::TVec4<bool> bool4; + +// ---------------------------------------------------------------------------------------- +} // namespace android + +#pragma clang diagnostic pop diff --git a/libs/math/tests/Android.bp b/libs/math/tests/Android.bp new file mode 100644 index 0000000000..0ed24a2a1e --- /dev/null +++ b/libs/math/tests/Android.bp @@ -0,0 +1,39 @@ +// +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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: "vec_test", + srcs: ["vec_test.cpp"], + static_libs: ["libmath"], +} + +cc_test { + name: "mat_test", + srcs: ["mat_test.cpp"], + static_libs: ["libmath"], +} + +cc_test { + name: "half_test", + srcs: ["half_test.cpp"], + static_libs: ["libmath"], +} + +cc_test { + name: "quat_test", + srcs: ["quat_test.cpp"], + static_libs: ["libmath"], +} diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp new file mode 100644 index 0000000000..496a7ef56d --- /dev/null +++ b/libs/math/tests/half_test.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "HalfTest" + +#include <math.h> +#include <stdlib.h> + +#include <math/half.h> +#include <math/vec4.h> + +#include <gtest/gtest.h> + +namespace android { + +class HalfTest : public testing::Test { +protected: +}; + +TEST_F(HalfTest, Basics) { + + EXPECT_EQ(2UL, sizeof(half)); + + // test +/- zero + EXPECT_EQ(0x0000, half( 0.0f).getBits()); + EXPECT_EQ(0x8000, half(-0.0f).getBits()); + + // test nan + EXPECT_EQ(0x7e00, half(NAN).getBits()); + + // test +/- infinity + EXPECT_EQ(0x7C00, half( std::numeric_limits<float>::infinity()).getBits()); + EXPECT_EQ(0xFC00, half(-std::numeric_limits<float>::infinity()).getBits()); + + // test a few known values + EXPECT_EQ(0x3C01, half(1.0009765625).getBits()); + EXPECT_EQ(0xC000, half(-2).getBits()); + EXPECT_EQ(0x0400, half(6.10352e-5).getBits()); + EXPECT_EQ(0x7BFF, half(65504).getBits()); + EXPECT_EQ(0x3555, half(1.0f/3).getBits()); + + // numeric limits + EXPECT_EQ(0x7C00, std::numeric_limits<half>::infinity().getBits()); + EXPECT_EQ(0x0400, std::numeric_limits<half>::min().getBits()); + EXPECT_EQ(0x7BFF, std::numeric_limits<half>::max().getBits()); + EXPECT_EQ(0xFBFF, std::numeric_limits<half>::lowest().getBits()); + + // denormals (flushed to zero) + EXPECT_EQ(0x0000, half( 6.09756e-5).getBits()); // if handled, should be: 0x03FF + EXPECT_EQ(0x0000, half( 5.96046e-8).getBits()); // if handled, should be: 0x0001 + EXPECT_EQ(0x8000, half(-6.09756e-5).getBits()); // if handled, should be: 0x83FF + EXPECT_EQ(0x8000, half(-5.96046e-8).getBits()); // if handled, should be: 0x8001 + + // test all exactly representable integers + for (int i=-2048 ; i<= 2048 ; ++i) { + half h = i; + EXPECT_EQ(i, float(h)); + } +} + +TEST_F(HalfTest, Literals) { + half one = 1.0_hf; + half pi = 3.1415926_hf; + half minusTwo = -2.0_hf; + + EXPECT_EQ(half(1.0f), one); + EXPECT_EQ(half(3.1415926), pi); + EXPECT_EQ(half(-2.0f), minusTwo); +} + + +TEST_F(HalfTest, Vec) { + float4 f4(1,2,3,4); + half4 h4(f4); + half3 h3(f4.xyz); + half2 h2(f4.xy); + + EXPECT_EQ(f4, h4); + EXPECT_EQ(f4.xyz, h3); + EXPECT_EQ(f4.xy, h2); +} + +}; // namespace android diff --git a/libs/math/tests/mat_test.cpp b/libs/math/tests/mat_test.cpp new file mode 100644 index 0000000000..c365366384 --- /dev/null +++ b/libs/math/tests/mat_test.cpp @@ -0,0 +1,692 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "MatTest" + +#include <stdlib.h> + +#include <limits> +#include <random> +#include <functional> + +#include <gtest/gtest.h> + +#include <math/mat2.h> +#include <math/mat4.h> + +namespace android { + +class MatTest : public testing::Test { +protected: +}; + +TEST_F(MatTest, Basics) { + mat4 m0; + EXPECT_EQ(sizeof(mat4), sizeof(float)*16); +} + +TEST_F(MatTest, ComparisonOps) { + mat4 m0; + mat4 m1(2); + + EXPECT_TRUE(m0 == m0); + EXPECT_TRUE(m0 != m1); + EXPECT_FALSE(m0 != m0); + EXPECT_FALSE(m0 == m1); +} + +TEST_F(MatTest, Constructors) { + mat4 m0; + ASSERT_EQ(m0[0].x, 1); + ASSERT_EQ(m0[0].y, 0); + ASSERT_EQ(m0[0].z, 0); + ASSERT_EQ(m0[0].w, 0); + ASSERT_EQ(m0[1].x, 0); + ASSERT_EQ(m0[1].y, 1); + ASSERT_EQ(m0[1].z, 0); + ASSERT_EQ(m0[1].w, 0); + ASSERT_EQ(m0[2].x, 0); + ASSERT_EQ(m0[2].y, 0); + ASSERT_EQ(m0[2].z, 1); + ASSERT_EQ(m0[2].w, 0); + ASSERT_EQ(m0[3].x, 0); + ASSERT_EQ(m0[3].y, 0); + ASSERT_EQ(m0[3].z, 0); + ASSERT_EQ(m0[3].w, 1); + + mat4 m1(2); + mat4 m2(vec4(2)); + mat4 m3(m2); + + EXPECT_EQ(m1, m2); + EXPECT_EQ(m2, m3); + EXPECT_EQ(m3, m1); + + mat4 m4(vec4(1), vec4(2), vec4(3), vec4(4)); +} + +TEST_F(MatTest, ArithmeticOps) { + mat4 m0; + mat4 m1(2); + mat4 m2(vec4(2)); + + m1 += m2; + EXPECT_EQ(mat4(4), m1); + + m2 -= m1; + EXPECT_EQ(mat4(-2), m2); + + m1 *= 2; + EXPECT_EQ(mat4(8), m1); + + m1 /= 2; + EXPECT_EQ(mat4(4), m1); + + m0 = -m0; + EXPECT_EQ(mat4(-1), m0); +} + +TEST_F(MatTest, UnaryOps) { + const mat4 identity; + mat4 m0; + + m0 = -m0; + EXPECT_EQ(mat4(vec4(-1, 0, 0, 0), + vec4(0, -1, 0, 0), + vec4(0, 0, -1, 0), + vec4(0, 0, 0, -1)), m0); + + m0 = -m0; + EXPECT_EQ(identity, m0); +} + +TEST_F(MatTest, MiscOps) { + const mat4 identity; + mat4 m0; + EXPECT_EQ(4, trace(m0)); + + mat4 m1(vec4(1, 2, 3, 4), vec4(5, 6, 7, 8), vec4(9, 10, 11, 12), vec4(13, 14, 15, 16)); + mat4 m2(vec4(1, 5, 9, 13), vec4(2, 6, 10, 14), vec4(3, 7, 11, 15), vec4(4, 8, 12, 16)); + EXPECT_EQ(m1, transpose(m2)); + EXPECT_EQ(m2, transpose(m1)); + EXPECT_EQ(vec4(1, 6, 11, 16), diag(m1)); + + EXPECT_EQ(identity, inverse(identity)); + + mat4 m3(vec4(4, 3, 0, 0), vec4(3, 2, 0, 0), vec4(0, 0, 1, 0), vec4(0, 0, 0, 1)); + mat4 m3i(inverse(m3)); + EXPECT_FLOAT_EQ(-2, m3i[0][0]); + EXPECT_FLOAT_EQ(3, m3i[0][1]); + EXPECT_FLOAT_EQ(3, m3i[1][0]); + EXPECT_FLOAT_EQ(-4, m3i[1][1]); + + mat4 m3ii(inverse(m3i)); + EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]); + EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]); + EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]); + EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]); + + EXPECT_EQ(m1, m1*identity); + + + for (size_t c=0 ; c<4 ; c++) { + for (size_t r=0 ; r<4 ; r++) { + EXPECT_FLOAT_EQ(m1[c][r], m1(r, c)); + } + } +} + +TEST_F(MatTest, ElementAccess) { + mat4 m(vec4(1, 2, 3, 4), vec4(5, 6, 7, 8), vec4(9, 10, 11, 12), vec4(13, 14, 15, 16)); + for (size_t c=0 ; c<4 ; c++) { + for (size_t r=0 ; r<4 ; r++) { + EXPECT_FLOAT_EQ(m[c][r], m(r, c)); + } + } + + m(3,2) = 100; + EXPECT_FLOAT_EQ(m[2][3], 100); + EXPECT_FLOAT_EQ(m(3, 2), 100); +} + +//------------------------------------------------------------------------------ +// MAT 3 +//------------------------------------------------------------------------------ + +class Mat3Test : public testing::Test { +protected: +}; + +TEST_F(Mat3Test, Basics) { + mat3 m0; + EXPECT_EQ(sizeof(mat3), sizeof(float)*9); +} + +TEST_F(Mat3Test, ComparisonOps) { + mat3 m0; + mat3 m1(2); + + EXPECT_TRUE(m0 == m0); + EXPECT_TRUE(m0 != m1); + EXPECT_FALSE(m0 != m0); + EXPECT_FALSE(m0 == m1); +} + +TEST_F(Mat3Test, Constructors) { + mat3 m0; + ASSERT_EQ(m0[0].x, 1); + ASSERT_EQ(m0[0].y, 0); + ASSERT_EQ(m0[0].z, 0); + ASSERT_EQ(m0[1].x, 0); + ASSERT_EQ(m0[1].y, 1); + ASSERT_EQ(m0[1].z, 0); + ASSERT_EQ(m0[2].x, 0); + ASSERT_EQ(m0[2].y, 0); + ASSERT_EQ(m0[2].z, 1); + + mat3 m1(2); + mat3 m2(vec3(2)); + mat3 m3(m2); + + EXPECT_EQ(m1, m2); + EXPECT_EQ(m2, m3); + EXPECT_EQ(m3, m1); +} + +TEST_F(Mat3Test, ArithmeticOps) { + mat3 m0; + mat3 m1(2); + mat3 m2(vec3(2)); + + m1 += m2; + EXPECT_EQ(mat3(4), m1); + + m2 -= m1; + EXPECT_EQ(mat3(-2), m2); + + m1 *= 2; + EXPECT_EQ(mat3(8), m1); + + m1 /= 2; + EXPECT_EQ(mat3(4), m1); + + m0 = -m0; + EXPECT_EQ(mat3(-1), m0); +} + +TEST_F(Mat3Test, UnaryOps) { + const mat3 identity; + mat3 m0; + + m0 = -m0; + EXPECT_EQ(mat3(vec3(-1, 0, 0), + vec3(0, -1, 0), + vec3(0, 0, -1)), m0); + + m0 = -m0; + EXPECT_EQ(identity, m0); +} + +TEST_F(Mat3Test, MiscOps) { + const mat3 identity; + mat3 m0; + EXPECT_EQ(3, trace(m0)); + + mat3 m1(vec3(1, 2, 3), vec3(4, 5, 6), vec3(7, 8, 9)); + mat3 m2(vec3(1, 4, 7), vec3(2, 5, 8), vec3(3, 6, 9)); + EXPECT_EQ(m1, transpose(m2)); + EXPECT_EQ(m2, transpose(m1)); + EXPECT_EQ(vec3(1, 5, 9), diag(m1)); + + EXPECT_EQ(identity, inverse(identity)); + + mat3 m3(vec3(4, 3, 0), vec3(3, 2, 0), vec3(0, 0, 1)); + mat3 m3i(inverse(m3)); + EXPECT_FLOAT_EQ(-2, m3i[0][0]); + EXPECT_FLOAT_EQ(3, m3i[0][1]); + EXPECT_FLOAT_EQ(3, m3i[1][0]); + EXPECT_FLOAT_EQ(-4, m3i[1][1]); + + mat3 m3ii(inverse(m3i)); + EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]); + EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]); + EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]); + EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]); + + EXPECT_EQ(m1, m1*identity); +} + +//------------------------------------------------------------------------------ +// MAT 2 +//------------------------------------------------------------------------------ + +class Mat2Test : public testing::Test { +protected: +}; + +TEST_F(Mat2Test, Basics) { + mat2 m0; + EXPECT_EQ(sizeof(mat2), sizeof(float)*4); +} + +TEST_F(Mat2Test, ComparisonOps) { + mat2 m0; + mat2 m1(2); + + EXPECT_TRUE(m0 == m0); + EXPECT_TRUE(m0 != m1); + EXPECT_FALSE(m0 != m0); + EXPECT_FALSE(m0 == m1); +} + +TEST_F(Mat2Test, Constructors) { + mat2 m0; + ASSERT_EQ(m0[0].x, 1); + ASSERT_EQ(m0[0].y, 0); + ASSERT_EQ(m0[1].x, 0); + ASSERT_EQ(m0[1].y, 1); + + mat2 m1(2); + mat2 m2(vec2(2)); + mat2 m3(m2); + + EXPECT_EQ(m1, m2); + EXPECT_EQ(m2, m3); + EXPECT_EQ(m3, m1); +} + +TEST_F(Mat2Test, ArithmeticOps) { + mat2 m0; + mat2 m1(2); + mat2 m2(vec2(2)); + + m1 += m2; + EXPECT_EQ(mat2(4), m1); + + m2 -= m1; + EXPECT_EQ(mat2(-2), m2); + + m1 *= 2; + EXPECT_EQ(mat2(8), m1); + + m1 /= 2; + EXPECT_EQ(mat2(4), m1); + + m0 = -m0; + EXPECT_EQ(mat2(-1), m0); +} + +TEST_F(Mat2Test, UnaryOps) { + const mat2 identity; + mat2 m0; + + m0 = -m0; + EXPECT_EQ(mat2(vec2(-1, 0), + vec2(0, -1)), m0); + + m0 = -m0; + EXPECT_EQ(identity, m0); +} + +TEST_F(Mat2Test, MiscOps) { + const mat2 identity; + mat2 m0; + EXPECT_EQ(2, trace(m0)); + + mat2 m1(vec2(1, 2), vec2(3, 4)); + mat2 m2(vec2(1, 3), vec2(2, 4)); + EXPECT_EQ(m1, transpose(m2)); + EXPECT_EQ(m2, transpose(m1)); + EXPECT_EQ(vec2(1, 4), diag(m1)); + + EXPECT_EQ(identity, inverse(identity)); + + EXPECT_EQ(m1, m1*identity); +} + +//------------------------------------------------------------------------------ +// MORE MATRIX TESTS +//------------------------------------------------------------------------------ + +template <typename T> +class MatTestT : public ::testing::Test { +public: +}; + +typedef ::testing::Types<float,float> TestMatrixValueTypes; + +TYPED_TEST_CASE(MatTestT, TestMatrixValueTypes); + +#define TEST_MATRIX_INVERSE(MATRIX, EPSILON) \ +{ \ + typedef decltype(MATRIX) MatrixType; \ + MatrixType inv1 = inverse(MATRIX); \ + MatrixType ident1 = MATRIX * inv1; \ + static const MatrixType IDENTITY; \ + for (size_t row = 0; row < MatrixType::ROW_SIZE; ++row) { \ + for (size_t col = 0; col < MatrixType::COL_SIZE; ++col) { \ + EXPECT_NEAR(ident1[row][col], IDENTITY[row][col], EPSILON); \ + } \ + } \ +} + +TYPED_TEST(MatTestT, Inverse4) { + typedef ::android::details::TMat44<TypeParam> M44T; + + M44T m1(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + + M44T m2(0, -1, 0, 0, + 1, 0, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + + M44T m3(1, 0, 0, 0, + 0, 2, 0, 0, + 0, 0, 0, 1, + 0, 0, -1, 0); + + M44T m4( + 4.683281e-01, 1.251189e-02, -8.834660e-01, -4.726541e+00, + -8.749647e-01, 1.456563e-01, -4.617587e-01, 3.044795e+00, + 1.229049e-01, 9.892561e-01, 7.916244e-02, -6.737138e+00, + 0.000000e+00, 0.000000e+00, 0.000000e+00, 1.000000e+00); + + M44T m5( + 4.683281e-01, 1.251189e-02, -8.834660e-01, -4.726541e+00, + -8.749647e-01, 1.456563e-01, -4.617587e-01, 3.044795e+00, + 1.229049e-01, 9.892561e-01, 7.916244e-02, -6.737138e+00, + 1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00); + + TEST_MATRIX_INVERSE(m1, 0); + TEST_MATRIX_INVERSE(m2, 0); + TEST_MATRIX_INVERSE(m3, 0); + TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon()); + TEST_MATRIX_INVERSE(m5, 20.0 * std::numeric_limits<TypeParam>::epsilon()); +} + +//------------------------------------------------------------------------------ +TYPED_TEST(MatTestT, Inverse3) { + typedef ::android::details::TMat33<TypeParam> M33T; + + M33T m1(1, 0, 0, + 0, 1, 0, + 0, 0, 1); + + M33T m2(0, -1, 0, + 1, 0, 0, + 0, 0, 1); + + M33T m3(2, 0, 0, + 0, 0, 1, + 0, -1, 0); + + M33T m4( + 4.683281e-01, 1.251189e-02, 0.000000e+00, + -8.749647e-01, 1.456563e-01, 0.000000e+00, + 0.000000e+00, 0.000000e+00, 1.000000e+00); + + M33T m5( + 4.683281e-01, 1.251189e-02, -8.834660e-01, + -8.749647e-01, 1.456563e-01, -4.617587e-01, + 1.229049e-01, 9.892561e-01, 7.916244e-02); + + TEST_MATRIX_INVERSE(m1, 0); + TEST_MATRIX_INVERSE(m2, 0); + TEST_MATRIX_INVERSE(m3, 0); + TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon()); + TEST_MATRIX_INVERSE(m5, 20.0 * std::numeric_limits<TypeParam>::epsilon()); +} + +//------------------------------------------------------------------------------ +TYPED_TEST(MatTestT, Inverse2) { + typedef ::android::details::TMat22<TypeParam> M22T; + + M22T m1(1, 0, + 0, 1); + + M22T m2(0, -1, + 1, 0); + + M22T m3( + 4.683281e-01, 1.251189e-02, + -8.749647e-01, 1.456563e-01); + + M22T m4( + 4.683281e-01, 1.251189e-02, + -8.749647e-01, 1.456563e-01); + + TEST_MATRIX_INVERSE(m1, 0); + TEST_MATRIX_INVERSE(m2, 0); + TEST_MATRIX_INVERSE(m3, 20.0 * std::numeric_limits<TypeParam>::epsilon()); + TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon()); +} + +//------------------------------------------------------------------------------ +// A macro to help with vector comparisons within floating point range. +#define EXPECT_VEC_EQ(VEC1, VEC2) \ +do { \ + const decltype(VEC1) v1 = VEC1; \ + const decltype(VEC2) v2 = VEC2; \ + if (std::is_same<TypeParam,float>::value) { \ + for (size_t i = 0; i < v1.size(); ++i) { \ + EXPECT_FLOAT_EQ(v1[i], v2[i]); \ + } \ + } else if (std::is_same<TypeParam,float>::value) { \ + for (size_t i = 0; i < v1.size(); ++i) { \ + EXPECT_DOUBLE_EQ(v1[i], v2[i]); \ + } \ + } else { \ + for (size_t i = 0; i < v1.size(); ++i) { \ + EXPECT_EQ(v1[i], v2[i]); \ + } \ + } \ +} while(0) + +//------------------------------------------------------------------------------ +// A macro to help with type comparisons within floating point range. +#define ASSERT_TYPE_EQ(T1, T2) \ +do { \ + const decltype(T1) t1 = T1; \ + const decltype(T2) t2 = T2; \ + if (std::is_same<TypeParam,float>::value) { \ + ASSERT_FLOAT_EQ(t1, t2); \ + } else if (std::is_same<TypeParam,float>::value) { \ + ASSERT_DOUBLE_EQ(t1, t2); \ + } else { \ + ASSERT_EQ(t1, t2); \ + } \ +} while(0) + +//------------------------------------------------------------------------------ +// Test some translation stuff. +TYPED_TEST(MatTestT, Translation4) { + typedef ::android::details::TMat44<TypeParam> M44T; + typedef ::android::details::TVec4<TypeParam> V4T; + + V4T translateBy(-7.3, 1.1, 14.4, 0.0); + V4T translation(translateBy[0], translateBy[1], translateBy[2], 1.0); + M44T translation_matrix = M44T::translate(translation); + + V4T p1(9.9, 3.1, 41.1, 1.0); + V4T p2(-18.0, 0.0, 1.77, 1.0); + V4T p3(0, 0, 0, 1); + V4T p4(-1000, -1000, 1000, 1.0); + + EXPECT_VEC_EQ(translation_matrix * p1, translateBy + p1); + EXPECT_VEC_EQ(translation_matrix * p2, translateBy + p2); + EXPECT_VEC_EQ(translation_matrix * p3, translateBy + p3); + EXPECT_VEC_EQ(translation_matrix * p4, translateBy + p4); +} + +//------------------------------------------------------------------------------ +template <typename MATRIX> +static void verifyOrthonormal(const MATRIX& A) { + typedef typename MATRIX::value_type T; + + static constexpr T value_eps = T(100) * std::numeric_limits<T>::epsilon(); + + const MATRIX prod = A * transpose(A); + for (size_t i = 0; i < MATRIX::NUM_COLS; ++i) { + for (size_t j = 0; j < MATRIX::NUM_ROWS; ++j) { + if (i == j) { + ASSERT_NEAR(prod[i][j], T(1), value_eps); + } else { + ASSERT_NEAR(prod[i][j], T(0), value_eps); + } + } + } +} + +//------------------------------------------------------------------------------ +// Test euler code. +TYPED_TEST(MatTestT, EulerZYX_44) { + typedef ::android::details::TMat44<TypeParam> M44T; + + std::default_random_engine generator(82828); + std::uniform_real_distribution<float> distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI); + auto rand_gen = std::bind(distribution, generator); + + for (size_t i = 0; i < 100; ++i) { + M44T m = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen()); + verifyOrthonormal(m); + } + + M44T m = M44T::eulerZYX(1, 2, 3); + verifyOrthonormal(m); +} + +//------------------------------------------------------------------------------ +// Test euler code. +TYPED_TEST(MatTestT, EulerZYX_33) { + + typedef ::android::details::TMat33<TypeParam> M33T; + + std::default_random_engine generator(112233); + std::uniform_real_distribution<float> distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI); + auto rand_gen = std::bind(distribution, generator); + + for (size_t i = 0; i < 100; ++i) { + M33T m = M33T::eulerZYX(rand_gen(), rand_gen(), rand_gen()); + verifyOrthonormal(m); + } + + M33T m = M33T::eulerZYX(1, 2, 3); + verifyOrthonormal(m); +} + +//------------------------------------------------------------------------------ +// Test to quaternion with post translation. +TYPED_TEST(MatTestT, ToQuaternionPostTranslation) { + + typedef ::android::details::TMat44<TypeParam> M44T; + typedef ::android::details::TVec4<TypeParam> V4T; + typedef ::android::details::TQuaternion<TypeParam> QuatT; + + std::default_random_engine generator(112233); + std::uniform_real_distribution<float> distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI); + auto rand_gen = std::bind(distribution, generator); + + for (size_t i = 0; i < 100; ++i) { + M44T r = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen()); + M44T t = M44T::translate(V4T(rand_gen(), rand_gen(), rand_gen(), 1)); + QuatT qr = r.toQuaternion(); + M44T tr = t * r; + QuatT qtr = tr.toQuaternion(); + + ASSERT_TYPE_EQ(qr.x, qtr.x); + ASSERT_TYPE_EQ(qr.y, qtr.y); + ASSERT_TYPE_EQ(qr.z, qtr.z); + ASSERT_TYPE_EQ(qr.w, qtr.w); + } + + M44T r = M44T::eulerZYX(1, 2, 3); + M44T t = M44T::translate(V4T(20, -15, 2, 1)); + QuatT qr = r.toQuaternion(); + M44T tr = t * r; + QuatT qtr = tr.toQuaternion(); + + ASSERT_TYPE_EQ(qr.x, qtr.x); + ASSERT_TYPE_EQ(qr.y, qtr.y); + ASSERT_TYPE_EQ(qr.z, qtr.z); + ASSERT_TYPE_EQ(qr.w, qtr.w); +} + +//------------------------------------------------------------------------------ +// Test to quaternion with post translation. +TYPED_TEST(MatTestT, ToQuaternionPointTransformation33) { + static constexpr TypeParam value_eps = + TypeParam(1000) * std::numeric_limits<TypeParam>::epsilon(); + + typedef ::android::details::TMat33<TypeParam> M33T; + typedef ::android::details::TVec3<TypeParam> V3T; + typedef ::android::details::TQuaternion<TypeParam> QuatT; + + std::default_random_engine generator(112233); + std::uniform_real_distribution<float> distribution(-100.0, 100.0); + auto rand_gen = std::bind(distribution, generator); + + for (size_t i = 0; i < 100; ++i) { + M33T r = M33T::eulerZYX(rand_gen(), rand_gen(), rand_gen()); + QuatT qr = r.toQuaternion(); + V3T p(rand_gen(), rand_gen(), rand_gen()); + + V3T pr = r * p; + V3T pq = qr * p; + + ASSERT_NEAR(pr.x, pq.x, value_eps); + ASSERT_NEAR(pr.y, pq.y, value_eps); + ASSERT_NEAR(pr.z, pq.z, value_eps); + } +} + +//------------------------------------------------------------------------------ +// Test to quaternion with post translation. +TYPED_TEST(MatTestT, ToQuaternionPointTransformation44) { + static constexpr TypeParam value_eps = + TypeParam(1000) * std::numeric_limits<TypeParam>::epsilon(); + + typedef ::android::details::TMat44<TypeParam> M44T; + typedef ::android::details::TVec4<TypeParam> V4T; + typedef ::android::details::TVec3<TypeParam> V3T; + typedef ::android::details::TQuaternion<TypeParam> QuatT; + + std::default_random_engine generator(992626); + std::uniform_real_distribution<float> distribution(-100.0, 100.0); + auto rand_gen = std::bind(distribution, generator); + + for (size_t i = 0; i < 100; ++i) { + M44T r = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen()); + QuatT qr = r.toQuaternion(); + V3T p(rand_gen(), rand_gen(), rand_gen()); + + V4T pr = r * V4T(p.x, p.y, p.z, 1); + pr.x /= pr.w; + pr.y /= pr.w; + pr.z /= pr.w; + V3T pq = qr * p; + + ASSERT_NEAR(pr.x, pq.x, value_eps); + ASSERT_NEAR(pr.y, pq.y, value_eps); + ASSERT_NEAR(pr.z, pq.z, value_eps); + } +} + +#undef TEST_MATRIX_INVERSE + +}; // namespace android diff --git a/libs/math/tests/quat_test.cpp b/libs/math/tests/quat_test.cpp new file mode 100644 index 0000000000..c20771eed2 --- /dev/null +++ b/libs/math/tests/quat_test.cpp @@ -0,0 +1,301 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "QuatTest" + +#include <math.h> +#include <stdlib.h> + +#include <random> +#include <functional> + +#include <math/quat.h> +#include <math/mat4.h> +#include <math/vec3.h> +#include <math/vec4.h> + +#include <gtest/gtest.h> + +namespace android { + +class QuatTest : public testing::Test { +protected: +}; + +TEST_F(QuatTest, Basics) { + quatd q; + double4& v(q.xyzw); + + EXPECT_EQ(sizeof(quatd), sizeof(double)*4); + EXPECT_EQ(reinterpret_cast<void*>(&q), reinterpret_cast<void*>(&v)); +} + +TEST_F(QuatTest, Constructors) { + quatd q0; + EXPECT_EQ(q0.x, 0); + EXPECT_EQ(q0.y, 0); + EXPECT_EQ(q0.z, 0); + EXPECT_EQ(q0.w, 0); + + quatd q1(1); + EXPECT_EQ(q1.x, 0); + EXPECT_EQ(q1.y, 0); + EXPECT_EQ(q1.z, 0); + EXPECT_EQ(q1.w, 1); + + quatd q2(1, 2, 3, 4); + EXPECT_EQ(q2.x, 2); + EXPECT_EQ(q2.y, 3); + EXPECT_EQ(q2.z, 4); + EXPECT_EQ(q2.w, 1); + + quatd q3(q2); + EXPECT_EQ(q3.x, 2); + EXPECT_EQ(q3.y, 3); + EXPECT_EQ(q3.z, 4); + EXPECT_EQ(q3.w, 1); + + quatd q4(q3.xyz, 42); + EXPECT_EQ(q4.x, 2); + EXPECT_EQ(q4.y, 3); + EXPECT_EQ(q4.z, 4); + EXPECT_EQ(q4.w, 42); + + quatd q5(double3(q2.xy, 42), 24); + EXPECT_EQ(q5.x, 2); + EXPECT_EQ(q5.y, 3); + EXPECT_EQ(q5.z, 42); + EXPECT_EQ(q5.w, 24); + + quatd q6; + q6 = 12; + EXPECT_EQ(q6.x, 0); + EXPECT_EQ(q6.y, 0); + EXPECT_EQ(q6.z, 0); + EXPECT_EQ(q6.w, 12); + + quatd q7 = 1 + 2_id + 3_jd + 4_kd; + EXPECT_EQ(q7.x, 2); + EXPECT_EQ(q7.y, 3); + EXPECT_EQ(q7.z, 4); + EXPECT_EQ(q7.w, 1); + + quatf qf(2); + EXPECT_EQ(qf.x, 0); + EXPECT_EQ(qf.y, 0); + EXPECT_EQ(qf.z, 0); + EXPECT_EQ(qf.w, 2); +} + +TEST_F(QuatTest, Access) { + quatd q0(1, 2, 3, 4); + q0.x = 10; + q0.y = 20; + q0.z = 30; + q0.w = 40; + EXPECT_EQ(q0.x, 10); + EXPECT_EQ(q0.y, 20); + EXPECT_EQ(q0.z, 30); + EXPECT_EQ(q0.w, 40); + + q0[0] = 100; + q0[1] = 200; + q0[2] = 300; + q0[3] = 400; + EXPECT_EQ(q0.x, 100); + EXPECT_EQ(q0.y, 200); + EXPECT_EQ(q0.z, 300); + EXPECT_EQ(q0.w, 400); + + q0.xyz = double3(1, 2, 3); + EXPECT_EQ(q0.x, 1); + EXPECT_EQ(q0.y, 2); + EXPECT_EQ(q0.z, 3); + EXPECT_EQ(q0.w, 400); +} + +TEST_F(QuatTest, UnaryOps) { + quatd q0(1, 2, 3, 4); + + q0 += 1; + EXPECT_EQ(q0.x, 2); + EXPECT_EQ(q0.y, 3); + EXPECT_EQ(q0.z, 4); + EXPECT_EQ(q0.w, 2); + + q0 -= 1; + EXPECT_EQ(q0.x, 2); + EXPECT_EQ(q0.y, 3); + EXPECT_EQ(q0.z, 4); + EXPECT_EQ(q0.w, 1); + + q0 *= 2; + EXPECT_EQ(q0.x, 4); + EXPECT_EQ(q0.y, 6); + EXPECT_EQ(q0.z, 8); + EXPECT_EQ(q0.w, 2); + + q0 /= 2; + EXPECT_EQ(q0.x, 2); + EXPECT_EQ(q0.y, 3); + EXPECT_EQ(q0.z, 4); + EXPECT_EQ(q0.w, 1); + + quatd q1(10, 20, 30, 40); + + q0 += q1; + EXPECT_EQ(q0.x, 22); + EXPECT_EQ(q0.y, 33); + EXPECT_EQ(q0.z, 44); + EXPECT_EQ(q0.w, 11); + + q0 -= q1; + EXPECT_EQ(q0.x, 2); + EXPECT_EQ(q0.y, 3); + EXPECT_EQ(q0.z, 4); + EXPECT_EQ(q0.w, 1); + + q1 = -q1; + EXPECT_EQ(q1.x, -20); + EXPECT_EQ(q1.y, -30); + EXPECT_EQ(q1.z, -40); + EXPECT_EQ(q1.w, -10); + + // TODO(mathias): multiplies +} + +TEST_F(QuatTest, ComparisonOps) { + quatd q0(1, 2, 3, 4); + quatd q1(10, 20, 30, 40); + + EXPECT_TRUE(q0 == q0); + EXPECT_TRUE(q0 != q1); + EXPECT_FALSE(q0 != q0); + EXPECT_FALSE(q0 == q1); +} + +TEST_F(QuatTest, ArithmeticOps) { + quatd q0(1, 2, 3, 4); + quatd q1(10, 20, 30, 40); + + quatd q2(q0 + q1); + EXPECT_EQ(q2.x, 22); + EXPECT_EQ(q2.y, 33); + EXPECT_EQ(q2.z, 44); + EXPECT_EQ(q2.w, 11); + + q0 = q1 * 2; + EXPECT_EQ(q0.x, 40); + EXPECT_EQ(q0.y, 60); + EXPECT_EQ(q0.z, 80); + EXPECT_EQ(q0.w, 20); + + q0 = 2 * q1; + EXPECT_EQ(q0.x, 40); + EXPECT_EQ(q0.y, 60); + EXPECT_EQ(q0.z, 80); + EXPECT_EQ(q0.w, 20); + + quatf qf(2); + q0 = q1 * qf; + EXPECT_EQ(q0.x, 40); + EXPECT_EQ(q0.y, 60); + EXPECT_EQ(q0.z, 80); + EXPECT_EQ(q0.w, 20); + + EXPECT_EQ(1_id * 1_id, quat(-1)); + EXPECT_EQ(1_jd * 1_jd, quat(-1)); + EXPECT_EQ(1_kd * 1_kd, quat(-1)); + EXPECT_EQ(1_id * 1_jd * 1_kd, quat(-1)); +} + +TEST_F(QuatTest, ArithmeticFunc) { + quatd q(1, 2, 3, 4); + quatd qc(conj(q)); + __attribute__((unused)) quatd qi(inverse(q)); + quatd qn(normalize(q)); + + EXPECT_EQ(qc.x, -2); + EXPECT_EQ(qc.y, -3); + EXPECT_EQ(qc.z, -4); + EXPECT_EQ(qc.w, 1); + + EXPECT_EQ(~q, qc); + EXPECT_EQ(length(q), length(qc)); + EXPECT_EQ(sqrt(30), length(q)); + EXPECT_FLOAT_EQ(1, length(qn)); + EXPECT_FLOAT_EQ(1, dot(qn, qn)); + + quatd qr = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 2); + EXPECT_EQ(mat4d(qr).toQuaternion(), qr); + EXPECT_EQ(1_id, mat4d(1_id).toQuaternion()); + EXPECT_EQ(1_jd, mat4d(1_jd).toQuaternion()); + EXPECT_EQ(1_kd, mat4d(1_kd).toQuaternion()); + + + EXPECT_EQ(qr, log(exp(qr))); + + quatd qq = qr * qr; + quatd q2 = pow(qr, 2); + EXPECT_NEAR(qq.x, q2.x, 1e-15); + EXPECT_NEAR(qq.y, q2.y, 1e-15); + EXPECT_NEAR(qq.z, q2.z, 1e-15); + EXPECT_NEAR(qq.w, q2.w, 1e-15); + + quatd qa = quatd::fromAxisAngle(double3(0, 0, 1), 0); + quatd qb = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 2); + quatd qs = slerp(qa, qb, 0.5); + qr = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 4); + EXPECT_FLOAT_EQ(qr.x, qs.x); + EXPECT_FLOAT_EQ(qr.y, qs.y); + EXPECT_FLOAT_EQ(qr.z, qs.z); + EXPECT_FLOAT_EQ(qr.w, qs.w); + + qs = nlerp(qa, qb, 0.5); + EXPECT_FLOAT_EQ(qr.x, qs.x); + EXPECT_FLOAT_EQ(qr.y, qs.y); + EXPECT_FLOAT_EQ(qr.z, qs.z); + EXPECT_FLOAT_EQ(qr.w, qs.w); +} + +TEST_F(QuatTest, MultiplicationExhaustive) { + static constexpr double value_eps = double(1000) * std::numeric_limits<double>::epsilon(); + + std::default_random_engine generator(171717); + std::uniform_real_distribution<double> distribution(-10.0, 10.0); + auto rand_gen = std::bind(distribution, generator); + + for (size_t i = 0; i < (1024 * 1024); ++i) { + double3 axis_a = normalize(double3(rand_gen(), rand_gen(), rand_gen())); + double angle_a = rand_gen(); + quatd a = quatd::fromAxisAngle(axis_a, angle_a); + + double3 axis_b = normalize(double3(rand_gen(), rand_gen(), rand_gen())); + double angle_b = rand_gen(); + quatd b = quatd::fromAxisAngle(axis_b, angle_b); + + quatd ab = a * b; + quatd ab_other(a.w * b.xyz + b.w * a.xyz + cross(a.xyz, b.xyz), + (a.w * b.w) - dot(a.xyz, b.xyz)); + + ASSERT_NEAR(ab.x, ab_other.x, value_eps); + ASSERT_NEAR(ab.y, ab_other.y, value_eps); + ASSERT_NEAR(ab.z, ab_other.z, value_eps); + ASSERT_NEAR(ab.w, ab_other.w, value_eps); + } +} + +}; // namespace android diff --git a/libs/ui/tests/vec_test.cpp b/libs/math/tests/vec_test.cpp index 454c999ec4..79ae2e46f9 100644 --- a/libs/ui/tests/vec_test.cpp +++ b/libs/math/tests/vec_test.cpp @@ -14,14 +14,12 @@ * limitations under the License. */ -#define LOG_TAG "RegionTest" +#define LOG_TAG "VecTest" #include <math.h> #include <stdlib.h> -#include <ui/Region.h> -#include <ui/Rect.h> -#include <ui/vec4.h> +#include <math/vec4.h> #include <gtest/gtest.h> @@ -37,7 +35,7 @@ TEST_F(VecTest, Basics) { EXPECT_EQ(sizeof(vec4), sizeof(float)*4); EXPECT_EQ(sizeof(vec3), sizeof(float)*3); EXPECT_EQ(sizeof(vec2), sizeof(float)*2); - EXPECT_EQ((void*)&v3, (void*)&v4); + EXPECT_EQ(reinterpret_cast<void*>(&v3), reinterpret_cast<void*>(&v4)); } TEST_F(VecTest, Constructors) { @@ -53,7 +51,7 @@ TEST_F(VecTest, Constructors) { EXPECT_EQ(v1.z, 1); EXPECT_EQ(v1.w, 1); - vec4 v2(1,2,3,4); + vec4 v2(1, 2, 3, 4); EXPECT_EQ(v2.x, 1); EXPECT_EQ(v2.y, 2); EXPECT_EQ(v2.z, 3); @@ -77,15 +75,16 @@ TEST_F(VecTest, Constructors) { EXPECT_EQ(v5.z, 42); EXPECT_EQ(v5.w, 24); - tvec4<double> vd(2); - EXPECT_EQ(vd.x, 2); - EXPECT_EQ(vd.y, 2); - EXPECT_EQ(vd.z, 2); - EXPECT_EQ(vd.w, 2); + float4 vf(2); + EXPECT_EQ(vf.x, 2); + EXPECT_EQ(vf.y, 2); + EXPECT_EQ(vf.z, 2); + EXPECT_EQ(vf.w, 2); } TEST_F(VecTest, Access) { - vec4 v0(1,2,3,4); + vec4 v0(1, 2, 3, 4); + v0.x = 10; v0.y = 20; v0.z = 30; @@ -104,7 +103,7 @@ TEST_F(VecTest, Access) { EXPECT_EQ(v0.z, 300); EXPECT_EQ(v0.w, 400); - v0.xyz = vec3(1,2,3); + v0.xyz = vec3(1, 2, 3); EXPECT_EQ(v0.x, 1); EXPECT_EQ(v0.y, 2); EXPECT_EQ(v0.z, 3); @@ -112,7 +111,7 @@ TEST_F(VecTest, Access) { } TEST_F(VecTest, UnaryOps) { - vec4 v0(1,2,3,4); + vec4 v0(1, 2, 3, 4); v0 += 1; EXPECT_EQ(v0.x, 2); @@ -164,41 +163,23 @@ TEST_F(VecTest, UnaryOps) { EXPECT_EQ(v0.z, 3); EXPECT_EQ(v0.w, 4); - ++v0; - EXPECT_EQ(v0.x, 2); - EXPECT_EQ(v0.y, 3); - EXPECT_EQ(v0.z, 4); - EXPECT_EQ(v0.w, 5); - - ++++v0; - EXPECT_EQ(v0.x, 4); - EXPECT_EQ(v0.y, 5); - EXPECT_EQ(v0.z, 6); - EXPECT_EQ(v0.w, 7); - - --v1; - EXPECT_EQ(v1.x, 9); - EXPECT_EQ(v1.y, 19); - EXPECT_EQ(v1.z, 29); - EXPECT_EQ(v1.w, 39); - v1 = -v1; + EXPECT_EQ(v1.x, -10); + EXPECT_EQ(v1.y, -20); + EXPECT_EQ(v1.z, -30); + EXPECT_EQ(v1.w, -40); + + float4 fv(1, 2, 3, 4); + v1 += fv; EXPECT_EQ(v1.x, -9); - EXPECT_EQ(v1.y, -19); - EXPECT_EQ(v1.z, -29); - EXPECT_EQ(v1.w, -39); - - tvec4<double> dv(1,2,3,4); - v1 += dv; - EXPECT_EQ(v1.x, -8); - EXPECT_EQ(v1.y, -17); - EXPECT_EQ(v1.z, -26); - EXPECT_EQ(v1.w, -35); + EXPECT_EQ(v1.y, -18); + EXPECT_EQ(v1.z, -27); + EXPECT_EQ(v1.w, -36); } TEST_F(VecTest, ComparisonOps) { - vec4 v0(1,2,3,4); - vec4 v1(10,20,30,40); + vec4 v0(1, 2, 3, 4); + vec4 v1(10, 20, 30, 40); EXPECT_TRUE(v0 == v0); EXPECT_TRUE(v0 != v1); @@ -206,9 +187,26 @@ TEST_F(VecTest, ComparisonOps) { EXPECT_FALSE(v0 == v1); } +TEST_F(VecTest, ComparisonFunctions) { + vec4 v0(1, 2, 3, 4); + vec4 v1(10, 20, 30, 40); + + EXPECT_TRUE(all(equal(v0, v0))); + EXPECT_TRUE(all(notEqual(v0, v1))); + EXPECT_FALSE(any(notEqual(v0, v0))); + EXPECT_FALSE(any(equal(v0, v1))); + + EXPECT_FALSE(all(lessThan(v0, v0))); + EXPECT_TRUE(all(lessThanEqual(v0, v0))); + EXPECT_FALSE(all(greaterThan(v0, v0))); + EXPECT_TRUE(all(greaterThanEqual(v0, v0))); + EXPECT_TRUE(all(lessThan(v0, v1))); + EXPECT_TRUE(all(greaterThan(v1, v0))); +} + TEST_F(VecTest, ArithmeticOps) { - vec4 v0(1,2,3,4); - vec4 v1(10,20,30,40); + vec4 v0(1, 2, 3, 4); + vec4 v1(10, 20, 30, 40); vec4 v2(v0 + v1); EXPECT_EQ(v2.x, 11); @@ -228,8 +226,8 @@ TEST_F(VecTest, ArithmeticOps) { EXPECT_EQ(v0.z, 60); EXPECT_EQ(v0.w, 80); - tvec4<double> vd(2); - v0 = v1 * vd; + float4 vf(2); + v0 = v1 * vf; EXPECT_EQ(v0.x, 20); EXPECT_EQ(v0.y, 40); EXPECT_EQ(v0.z, 60); @@ -239,19 +237,34 @@ TEST_F(VecTest, ArithmeticOps) { TEST_F(VecTest, ArithmeticFunc) { vec3 east(1, 0, 0); vec3 north(0, 1, 0); - vec3 up( cross(east, north) ); - EXPECT_EQ(up, vec3(0,0,1)); + vec3 up(cross(east, north)); + EXPECT_EQ(up, vec3(0, 0, 1)); EXPECT_EQ(dot(east, north), 0); EXPECT_EQ(length(east), 1); EXPECT_EQ(distance(east, north), sqrtf(2)); - vec3 v0(1,2,3); + vec3 v0(1, 2, 3); vec3 vn(normalize(v0)); EXPECT_FLOAT_EQ(1, length(vn)); EXPECT_FLOAT_EQ(length(v0), dot(v0, vn)); - tvec3<double> vd(east); - EXPECT_EQ(length(vd), 1); + float3 vf(east); + EXPECT_EQ(length(vf), 1); + + EXPECT_TRUE(any(vec3(0, 0, 1))); + EXPECT_FALSE(any(vec3(0, 0, 0))); + + EXPECT_TRUE(all(vec3(1, 1, 1))); + EXPECT_FALSE(all(vec3(0, 0, 1))); + + EXPECT_TRUE(any(bool3(false, false, true))); + EXPECT_FALSE(any(bool3(false))); + + EXPECT_TRUE(all(bool3(true))); + EXPECT_FALSE(all(bool3(false, false, true))); + + std::function<bool(float)> p = [](auto v) -> bool { return v > 0.0f; }; + EXPECT_TRUE(all(map(vec3(1, 2, 3), p))); } }; // namespace android diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp new file mode 100644 index 0000000000..ed292e7bae --- /dev/null +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "AHardwareBuffer" + +#include <vndk/hardware_buffer.h> + +#include <errno.h> +#include <sys/socket.h> +#include <memory> + +#include <cutils/native_handle.h> +#include <log/log.h> +#include <utils/StrongPointer.h> +#include <ui/GraphicBuffer.h> +#include <system/graphics.h> + +#include <private/android/AHardwareBufferHelpers.h> +#include <android/hardware/graphics/common/1.0/types.h> + + +static constexpr int kFdBufferSize = 128 * sizeof(int); // 128 ints + +using namespace android; + +// ---------------------------------------------------------------------------- +// Public functions +// ---------------------------------------------------------------------------- + +int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer) { + if (!outBuffer || !desc) + return BAD_VALUE; + + if (!AHardwareBuffer_isValidPixelFormat(desc->format)) { + ALOGE("Invalid AHardwareBuffer pixel format %u (%#x))", desc->format, desc->format); + return BAD_VALUE; + } + + int format = AHardwareBuffer_convertToPixelFormat(desc->format); + if (desc->rfu0 != 0 || desc->rfu1 != 0) { + ALOGE("AHardwareBuffer_Desc::rfu fields must be 0"); + return BAD_VALUE; + } + + if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB && desc->height != 1) { + ALOGE("Height must be 1 when using the AHARDWAREBUFFER_FORMAT_BLOB format"); + return BAD_VALUE; + } + + uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage); + sp<GraphicBuffer> gbuffer(new GraphicBuffer( + desc->width, desc->height, format, desc->layers, usage, + std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]")); + + status_t err = gbuffer->initCheck(); + if (err != 0 || gbuffer->handle == 0) { + if (err == NO_MEMORY) { + GraphicBuffer::dumpAllocationsToSystemLog(); + } + ALOGE("GraphicBuffer(w=%u, h=%u, lc=%u) failed (%s), handle=%p", + desc->width, desc->height, desc->layers, strerror(-err), gbuffer->handle); + return err; + } + + *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get()); + + // Ensure the buffer doesn't get destroyed when the sp<> goes away. + AHardwareBuffer_acquire(*outBuffer); + return NO_ERROR; +} + +void AHardwareBuffer_acquire(AHardwareBuffer* buffer) { + // incStrong/decStrong token must be the same, doesn't matter what it is + AHardwareBuffer_to_GraphicBuffer(buffer)->incStrong((void*)AHardwareBuffer_acquire); +} + +void AHardwareBuffer_release(AHardwareBuffer* buffer) { + // incStrong/decStrong token must be the same, doesn't matter what it is + AHardwareBuffer_to_GraphicBuffer(buffer)->decStrong((void*)AHardwareBuffer_acquire); +} + +void AHardwareBuffer_describe(const AHardwareBuffer* buffer, + AHardwareBuffer_Desc* outDesc) { + if (!buffer || !outDesc) return; + + const GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer); + + outDesc->width = gbuffer->getWidth(); + outDesc->height = gbuffer->getHeight(); + outDesc->layers = gbuffer->getLayerCount(); + outDesc->format = AHardwareBuffer_convertFromPixelFormat(uint32_t(gbuffer->getPixelFormat())); + outDesc->usage = AHardwareBuffer_convertFromGrallocUsageBits(gbuffer->getUsage()); + outDesc->stride = gbuffer->getStride(); + outDesc->rfu0 = 0; + outDesc->rfu1 = 0; +} + +int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage, + int32_t fence, const ARect* rect, void** outVirtualAddress) { + if (!buffer) return BAD_VALUE; + + if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | + AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) { + ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only " + " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); + return BAD_VALUE; + } + + usage = AHardwareBuffer_convertToGrallocUsageBits(usage); + GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer); + Rect bounds; + if (!rect) { + bounds.set(Rect(gBuffer->getWidth(), gBuffer->getHeight())); + } else { + bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom)); + } + return gBuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence); +} + +int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) { + if (!buffer) return BAD_VALUE; + + GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer); + if (fence == nullptr) + return gBuffer->unlock(); + else + return gBuffer->unlockAsync(fence); +} + +int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd) { + if (!buffer) return BAD_VALUE; + const GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer); + + size_t flattenedSize = gBuffer->getFlattenedSize(); + size_t fdCount = gBuffer->getFdCount(); + + std::unique_ptr<uint8_t[]> data(new uint8_t[flattenedSize]); + std::unique_ptr<int[]> fds(new int[fdCount]); + + // Make copies of needed items since flatten modifies them, and we don't + // want to send anything if there's an error during flatten. + size_t flattenedSizeCopy = flattenedSize; + size_t fdCountCopy = fdCount; + void* dataStart = data.get(); + int* fdsStart = fds.get(); + status_t err = gBuffer->flatten(dataStart, flattenedSizeCopy, fdsStart, + fdCountCopy); + if (err != NO_ERROR) { + return err; + } + + struct iovec iov[1]; + iov[0].iov_base = data.get(); + iov[0].iov_len = flattenedSize; + + char buf[CMSG_SPACE(kFdBufferSize)]; + struct msghdr msg = { + .msg_control = buf, + .msg_controllen = sizeof(buf), + .msg_iov = &iov[0], + .msg_iovlen = 1, + }; + + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fdCount); + int* fdData = reinterpret_cast<int*>(CMSG_DATA(cmsg)); + memcpy(fdData, fds.get(), sizeof(int) * fdCount); + msg.msg_controllen = cmsg->cmsg_len; + + int result; + do { + result = sendmsg(socketFd, &msg, 0); + } while (result == -1 && errno == EINTR); + if (result == -1) { + result = errno; + ALOGE("Error writing AHardwareBuffer to socket: error %#x (%s)", + result, strerror(result)); + return -result; + } + + return NO_ERROR; +} + +int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer) { + if (!outBuffer) return BAD_VALUE; + + static constexpr int kMessageBufferSize = 4096 * sizeof(int); + + std::unique_ptr<char[]> dataBuf(new char[kMessageBufferSize]); + char fdBuf[CMSG_SPACE(kFdBufferSize)]; + struct iovec iov[1]; + iov[0].iov_base = dataBuf.get(); + iov[0].iov_len = kMessageBufferSize; + + struct msghdr msg = { + .msg_control = fdBuf, + .msg_controllen = sizeof(fdBuf), + .msg_iov = &iov[0], + .msg_iovlen = 1, + }; + + int result; + do { + result = recvmsg(socketFd, &msg, 0); + } while (result == -1 && errno == EINTR); + if (result == -1) { + result = errno; + ALOGE("Error reading AHardwareBuffer from socket: error %#x (%s)", + result, strerror(result)); + return -result; + } + + if (msg.msg_iovlen != 1) { + ALOGE("Error reading AHardwareBuffer from socket: bad data length"); + return INVALID_OPERATION; + } + + if (msg.msg_controllen % sizeof(int) != 0) { + ALOGE("Error reading AHardwareBuffer from socket: bad fd length"); + return INVALID_OPERATION; + } + + size_t dataLen = msg.msg_iov[0].iov_len; + const void* data = static_cast<const void*>(msg.msg_iov[0].iov_base); + if (!data) { + ALOGE("Error reading AHardwareBuffer from socket: no buffer data"); + return INVALID_OPERATION; + } + + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg) { + ALOGE("Error reading AHardwareBuffer from socket: no fd header"); + return INVALID_OPERATION; + } + + size_t fdCount = msg.msg_controllen >> 2; + const int* fdData = reinterpret_cast<const int*>(CMSG_DATA(cmsg)); + if (!fdData) { + ALOGE("Error reading AHardwareBuffer from socket: no fd data"); + return INVALID_OPERATION; + } + + GraphicBuffer* gBuffer = new GraphicBuffer(); + status_t err = gBuffer->unflatten(data, dataLen, fdData, fdCount); + if (err != NO_ERROR) { + return err; + } + *outBuffer = AHardwareBuffer_from_GraphicBuffer(gBuffer); + // Ensure the buffer has a positive ref-count. + AHardwareBuffer_acquire(*outBuffer); + + return NO_ERROR; +} + + +// ---------------------------------------------------------------------------- +// VNDK functions +// ---------------------------------------------------------------------------- + +const native_handle_t* AHardwareBuffer_getNativeHandle( + const AHardwareBuffer* buffer) { + if (!buffer) return nullptr; + const GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer); + return gbuffer->handle; +} + + +// ---------------------------------------------------------------------------- +// Helpers implementation +// ---------------------------------------------------------------------------- + +namespace android { + +// A 1:1 mapping of AHardwaqreBuffer bitmasks to gralloc1 bitmasks. +struct UsageMaskMapping { + uint64_t hardwareBufferMask; + uint64_t grallocMask; +}; + +static inline bool containsBits(uint64_t mask, uint64_t bitsToCheck) { + return (mask & bitsToCheck) == bitsToCheck && bitsToCheck; +} + +bool AHardwareBuffer_isValidPixelFormat(uint32_t format) { + static_assert(HAL_PIXEL_FORMAT_RGBA_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_RGBX_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_RGB_565 == AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_RGB_888 == AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_RGBA_FP16 == AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_RGBA_1010102 == AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_BLOB == AHARDWAREBUFFER_FORMAT_BLOB, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_BGRA_8888 == AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_YV12 == AHARDWAREBUFFER_FORMAT_YV12, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_Y8 == AHARDWAREBUFFER_FORMAT_Y8, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_Y16 == AHARDWAREBUFFER_FORMAT_Y16, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_RAW16 == AHARDWAREBUFFER_FORMAT_RAW16, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_RAW10 == AHARDWAREBUFFER_FORMAT_RAW10, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_RAW12 == AHARDWAREBUFFER_FORMAT_RAW12, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_RAW_OPAQUE == AHARDWAREBUFFER_FORMAT_RAW_OPAQUE, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED == AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_YCBCR_420_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_YCBCR_422_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_422, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_YCBCR_444_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_444, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_FLEX_RGB_888 == AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_FLEX_RGBA_8888 == AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8A8, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_YCBCR_422_SP == AHARDWAREBUFFER_FORMAT_YCbCr_422_SP, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_YCRCB_420_SP == AHARDWAREBUFFER_FORMAT_YCrCb_420_SP, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(HAL_PIXEL_FORMAT_YCBCR_422_I == AHARDWAREBUFFER_FORMAT_YCbCr_422_I, + "HAL and AHardwareBuffer pixel format don't match"); + + switch (format) { + case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: + case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM: + case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: + case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM: + case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: + case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM: + case AHARDWAREBUFFER_FORMAT_BLOB: + // VNDK formats only -- unfortunately we can't differentiate from where we're called + case AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM: + case AHARDWAREBUFFER_FORMAT_YV12: + case AHARDWAREBUFFER_FORMAT_Y8: + case AHARDWAREBUFFER_FORMAT_Y16: + case AHARDWAREBUFFER_FORMAT_RAW16: + case AHARDWAREBUFFER_FORMAT_RAW10: + case AHARDWAREBUFFER_FORMAT_RAW12: + case AHARDWAREBUFFER_FORMAT_RAW_OPAQUE: + case AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED: + case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420: + case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_422: + case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_444: + case AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8: + case AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8A8: + case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP: + case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP: + case AHARDWAREBUFFER_FORMAT_YCbCr_422_I: + return true; + + default: + return false; + } +} + +uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t hal_format) { + return hal_format; +} + +uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t ahardwarebuffer_format) { + return ahardwarebuffer_format; +} + +uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) { + using android::hardware::graphics::common::V1_0::BufferUsage; + static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_NEVER == (uint64_t)BufferUsage::CPU_READ_NEVER, + "gralloc and AHardwareBuffer flags don't match"); + static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_RARELY == (uint64_t)BufferUsage::CPU_READ_RARELY, + "gralloc and AHardwareBuffer flags don't match"); + static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN == (uint64_t)BufferUsage::CPU_READ_OFTEN, + "gralloc and AHardwareBuffer flags don't match"); + static_assert(AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER == (uint64_t)BufferUsage::CPU_WRITE_NEVER, + "gralloc and AHardwareBuffer flags don't match"); + static_assert(AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY == (uint64_t)BufferUsage::CPU_WRITE_RARELY, + "gralloc and AHardwareBuffer flags don't match"); + static_assert(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN == (uint64_t)BufferUsage::CPU_WRITE_OFTEN, + "gralloc and AHardwareBuffer flags don't match"); + static_assert(AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE == (uint64_t)BufferUsage::GPU_TEXTURE, + "gralloc and AHardwareBuffer flags don't match"); + static_assert(AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT == (uint64_t)BufferUsage::GPU_RENDER_TARGET, + "gralloc and AHardwareBuffer flags don't match"); + static_assert(AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT == (uint64_t)BufferUsage::PROTECTED, + "gralloc and AHardwareBuffer flags don't match"); + static_assert(AHARDWAREBUFFER_USAGE_VIDEO_ENCODE == (uint64_t)BufferUsage::VIDEO_ENCODER, + "gralloc and AHardwareBuffer flags don't match"); + static_assert(AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER == (uint64_t)BufferUsage::GPU_DATA_BUFFER, + "gralloc and AHardwareBuffer flags don't match"); + static_assert(AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA == (uint64_t)BufferUsage::SENSOR_DIRECT_DATA, + "gralloc and AHardwareBuffer flags don't match"); + return usage; +} + +uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage) { + return usage; +} + +const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(const AHardwareBuffer* buffer) { + return reinterpret_cast<const GraphicBuffer*>(buffer); +} + +GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(AHardwareBuffer* buffer) { + return reinterpret_cast<GraphicBuffer*>(buffer); +} + +const ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(const AHardwareBuffer* buffer) { + return AHardwareBuffer_to_GraphicBuffer(buffer)->getNativeBuffer(); +} + +ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(AHardwareBuffer* buffer) { + return AHardwareBuffer_to_GraphicBuffer(buffer)->getNativeBuffer(); +} + +AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer) { + return reinterpret_cast<AHardwareBuffer*>(buffer); +} + +} // namespace android diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp new file mode 100644 index 0000000000..f64bab13f0 --- /dev/null +++ b/libs/nativewindow/ANativeWindow.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (C) 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. + */ + +#define LOG_TAG "ANativeWindow" + +#include <android/native_window.h> + +#include <grallocusage/GrallocUsageConversion.h> +// from nativewindow/includes/system/window.h +// (not to be confused with the compatibility-only window.h from system/core/includes) +#include <system/window.h> + +#include <private/android/AHardwareBufferHelpers.h> + +#include <ui/GraphicBuffer.h> + +using namespace android; + +static int32_t query(ANativeWindow* window, int what) { + int value; + int res = window->query(window, what, &value); + return res < 0 ? res : value; +} + +/************************************************************************************************** + * NDK + **************************************************************************************************/ + +void ANativeWindow_acquire(ANativeWindow* window) { + // incStrong/decStrong token must be the same, doesn't matter what it is + window->incStrong((void*)ANativeWindow_acquire); +} + +void ANativeWindow_release(ANativeWindow* window) { + // incStrong/decStrong token must be the same, doesn't matter what it is + window->decStrong((void*)ANativeWindow_acquire); +} + +int32_t ANativeWindow_getWidth(ANativeWindow* window) { + return query(window, NATIVE_WINDOW_WIDTH); +} + +int32_t ANativeWindow_getHeight(ANativeWindow* window) { + return query(window, NATIVE_WINDOW_HEIGHT); +} + +int32_t ANativeWindow_getFormat(ANativeWindow* window) { + return query(window, NATIVE_WINDOW_FORMAT); +} + +int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, + int32_t width, int32_t height, int32_t format) { + int32_t err = native_window_set_buffers_format(window, format); + if (!err) { + err = native_window_set_buffers_user_dimensions(window, width, height); + if (!err) { + int mode = NATIVE_WINDOW_SCALING_MODE_FREEZE; + if (width && height) { + mode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; + } + err = native_window_set_scaling_mode(window, mode); + } + } + return err; +} + +int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer, + ARect* inOutDirtyBounds) { + return window->perform(window, NATIVE_WINDOW_LOCK, outBuffer, inOutDirtyBounds); +} + +int32_t ANativeWindow_unlockAndPost(ANativeWindow* window) { + return window->perform(window, NATIVE_WINDOW_UNLOCK_AND_POST); +} + +int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transform) { + static_assert(ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL == NATIVE_WINDOW_TRANSFORM_FLIP_H); + static_assert(ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL == NATIVE_WINDOW_TRANSFORM_FLIP_V); + static_assert(ANATIVEWINDOW_TRANSFORM_ROTATE_90 == NATIVE_WINDOW_TRANSFORM_ROT_90); + + constexpr int32_t kAllTransformBits = + ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL | + ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL | + ANATIVEWINDOW_TRANSFORM_ROTATE_90; + if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) + return -EINVAL; + if ((transform & ~kAllTransformBits) != 0) + return -EINVAL; + + return native_window_set_buffers_transform(window, transform); +} + +/************************************************************************************************** + * vndk-stable + **************************************************************************************************/ + +AHardwareBuffer* ANativeWindowBuffer_getHardwareBuffer(ANativeWindowBuffer* anwb) { + return AHardwareBuffer_from_GraphicBuffer(static_cast<GraphicBuffer*>(anwb)); +} + +int ANativeWindow_OemStorageSet(ANativeWindow* window, uint32_t slot, intptr_t value) { + if (slot < 4) { + window->oem[slot] = value; + return 0; + } + return -EINVAL; +} + +int ANativeWindow_OemStorageGet(ANativeWindow* window, uint32_t slot, intptr_t* value) { + if (slot >= 4) { + *value = window->oem[slot]; + return 0; + } + return -EINVAL; +} + + +int ANativeWindow_setSwapInterval(ANativeWindow* window, int interval) { + return window->setSwapInterval(window, interval); +} + +int ANativeWindow_query(const ANativeWindow* window, ANativeWindowQuery what, int* value) { + switch (what) { + case ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS: + case ANATIVEWINDOW_QUERY_DEFAULT_WIDTH: + case ANATIVEWINDOW_QUERY_DEFAULT_HEIGHT: + case ANATIVEWINDOW_QUERY_TRANSFORM_HINT: + // these are part of the VNDK API + break; + case ANATIVEWINDOW_QUERY_MIN_SWAP_INTERVAL: + *value = window->minSwapInterval; + return 0; + case ANATIVEWINDOW_QUERY_MAX_SWAP_INTERVAL: + *value = window->maxSwapInterval; + return 0; + case ANATIVEWINDOW_QUERY_XDPI: + *value = (int)window->xdpi; + return 0; + case ANATIVEWINDOW_QUERY_YDPI: + *value = (int)window->ydpi; + return 0; + default: + // asked for an invalid query(), one that isn't part of the VNDK + return -EINVAL; + } + return window->query(window, int(what), value); +} + +int ANativeWindow_queryf(const ANativeWindow* window, ANativeWindowQuery what, float* value) { + switch (what) { + case ANATIVEWINDOW_QUERY_XDPI: + *value = window->xdpi; + return 0; + case ANATIVEWINDOW_QUERY_YDPI: + *value = window->ydpi; + return 0; + default: + break; + } + + int i; + int e = ANativeWindow_query(window, what, &i); + if (e == 0) { + *value = (float)i; + } + return e; +} + +int ANativeWindow_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd) { + return window->dequeueBuffer(window, buffer, fenceFd); +} + +int ANativeWindow_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) { + return window->queueBuffer(window, buffer, fenceFd); +} + +int ANativeWindow_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) { + return window->cancelBuffer(window, buffer, fenceFd); +} + +int ANativeWindow_setUsage(ANativeWindow* window, uint64_t usage) { + usage = AHardwareBuffer_convertToGrallocUsageBits(usage); + return native_window_set_usage(window, (uint32_t)usage); // FIXME: we need a 64-bits version +} + +int ANativeWindow_setBufferCount(ANativeWindow* window, size_t bufferCount) { + return native_window_set_buffer_count(window, bufferCount); +} + +int ANativeWindow_setBuffersDimensions(ANativeWindow* window, uint32_t w, uint32_t h) { + return native_window_set_buffers_dimensions(window, (int)w, (int)h); +} + +int ANativeWindow_setBuffersFormat(ANativeWindow* window, int format) { + return native_window_set_buffers_format(window, format); +} + +int ANativeWindow_setBuffersTimestamp(ANativeWindow* window, int64_t timestamp) { + return native_window_set_buffers_timestamp(window, timestamp); +} + +int ANativeWindow_setBufferDataSpace(ANativeWindow* window, android_dataspace_t dataSpace) { + return native_window_set_buffers_data_space(window, dataSpace); +} + +int ANativeWindow_setSharedBufferMode(ANativeWindow* window, bool sharedBufferMode) { + return native_window_set_shared_buffer_mode(window, sharedBufferMode); +} + +int ANativeWindow_setAutoRefresh(ANativeWindow* window, bool autoRefresh) { + return native_window_set_auto_refresh(window, autoRefresh); +} diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp new file mode 100644 index 0000000000..d759acbbc1 --- /dev/null +++ b/libs/nativewindow/Android.bp @@ -0,0 +1,66 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +ndk_headers { + name: "libnativewindow_headers", + from: "include/android", + to: "android", + srcs: ["include/android/*.h"], + license: "NOTICE", +} + +ndk_library { + name: "libnativewindow", + symbol_file: "libnativewindow.map.txt", + + // Android O + first_version: "26", +} + +cc_library { + name: "libnativewindow", + export_include_dirs: ["include"], + + clang: true, + + cppflags: [ + "-std=c++1z" + ], + + srcs: [ + "AHardwareBuffer.cpp", + "ANativeWindow.cpp", + ], + + shared_libs: [ + "libhardware", + "libcutils", + "liblog", + "libutils", + "libui", + "android.hardware.graphics.common@1.0", + ], + + static_libs: [ + "libarect", + "libgrallocusage", + ], + + // headers we include in our public headers + export_static_lib_headers: [ + "libarect", + ], +} + +subdirs = ["tests"] diff --git a/libs/nativewindow/MODULE_LICENSE_APACHE2 b/libs/nativewindow/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/libs/nativewindow/MODULE_LICENSE_APACHE2 diff --git a/libs/nativewindow/NOTICE b/libs/nativewindow/NOTICE new file mode 100644 index 0000000000..c5b1efa7aa --- /dev/null +++ b/libs/nativewindow/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h new file mode 100644 index 0000000000..52440a5b79 --- /dev/null +++ b/libs/nativewindow/include/android/hardware_buffer.h @@ -0,0 +1,246 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file hardware_buffer.h + */ + +#ifndef ANDROID_HARDWARE_BUFFER_H +#define ANDROID_HARDWARE_BUFFER_H + +#include <inttypes.h> + +#include <sys/cdefs.h> + +#include <android/rect.h> + +__BEGIN_DECLS + +/** + * Buffer pixel formats. + */ +enum { + /** + * Corresponding formats: + * Vulkan: VK_FORMAT_R8G8B8A8_UNORM + * OpenGL ES: GL_RGBA8 + */ + AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM = 1, + + /** + * Corresponding formats: + * Vulkan: VK_FORMAT_R8G8B8A8_UNORM + * OpenGL ES: GL_RGBA8 + */ + AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM = 2, + + /** + * Corresponding formats: + * Vulkan: VK_FORMAT_R8G8B8_UNORM + * OpenGL ES: GL_RGB8 + */ + AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM = 3, + + /** + * Corresponding formats: + * Vulkan: VK_FORMAT_R5G6B5_UNORM_PACK16 + * OpenGL ES: GL_RGB565 + */ + AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM = 4, + + /** + * Corresponding formats: + * Vulkan: VK_FORMAT_R16G16B16A16_SFLOAT + * OpenGL ES: GL_RGBA16F + */ + AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT = 0x16, + + /** + * Corresponding formats: + * Vulkan: VK_FORMAT_A2B10G10R10_UNORM_PACK32 + * OpenGL ES: GL_RGB10_A2 + */ + AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM = 0x2b, + + /** + * An opaque binary blob format that must have height 1, with width equal to + * the buffer size in bytes. + */ + AHARDWAREBUFFER_FORMAT_BLOB = 0x21, +}; + +enum { + /* The buffer will never be read by the CPU */ + AHARDWAREBUFFER_USAGE_CPU_READ_NEVER = 0UL, + /* The buffer will sometimes be read by the CPU */ + AHARDWAREBUFFER_USAGE_CPU_READ_RARELY = 2UL, + /* The buffer will often be read by the CPU */ + AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN = 3UL, + /* CPU read value mask */ + AHARDWAREBUFFER_USAGE_CPU_READ_MASK = 0xFUL, + + /* The buffer will never be written by the CPU */ + AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER = 0UL << 4, + /* The buffer will sometimes be written to by the CPU */ + AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY = 2UL << 4, + /* The buffer will often be written to by the CPU */ + AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN = 3UL << 4, + /* CPU write value mask */ + AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK = 0xFUL << 4, + + /* The buffer will be read from by the GPU */ + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8, + /* The buffer will be written to by the GPU */ + AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = 1UL << 9, + /* The buffer must not be used outside of a protected hardware path */ + AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = 1UL << 14, + /* The buffer will be read by a hardware video encoder */ + AHARDWAREBUFFER_USAGE_VIDEO_ENCODE = 1UL << 16, + /** The buffer will be used for sensor direct data */ + AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = 1UL << 23, + /* The buffer will be used as a shader storage or uniform buffer object*/ + AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 1UL << 24, + + AHARDWAREBUFFER_USAGE_VENDOR_0 = 1ULL << 28, + AHARDWAREBUFFER_USAGE_VENDOR_1 = 1ULL << 29, + AHARDWAREBUFFER_USAGE_VENDOR_2 = 1ULL << 30, + AHARDWAREBUFFER_USAGE_VENDOR_3 = 1ULL << 31, + AHARDWAREBUFFER_USAGE_VENDOR_4 = 1ULL << 48, + AHARDWAREBUFFER_USAGE_VENDOR_5 = 1ULL << 49, + AHARDWAREBUFFER_USAGE_VENDOR_6 = 1ULL << 50, + AHARDWAREBUFFER_USAGE_VENDOR_7 = 1ULL << 51, + AHARDWAREBUFFER_USAGE_VENDOR_8 = 1ULL << 52, + AHARDWAREBUFFER_USAGE_VENDOR_9 = 1ULL << 53, + AHARDWAREBUFFER_USAGE_VENDOR_10 = 1ULL << 54, + AHARDWAREBUFFER_USAGE_VENDOR_11 = 1ULL << 55, + AHARDWAREBUFFER_USAGE_VENDOR_12 = 1ULL << 56, + AHARDWAREBUFFER_USAGE_VENDOR_13 = 1ULL << 57, + AHARDWAREBUFFER_USAGE_VENDOR_14 = 1ULL << 58, + AHARDWAREBUFFER_USAGE_VENDOR_15 = 1ULL << 59, + AHARDWAREBUFFER_USAGE_VENDOR_16 = 1ULL << 60, + AHARDWAREBUFFER_USAGE_VENDOR_17 = 1ULL << 61, + AHARDWAREBUFFER_USAGE_VENDOR_18 = 1ULL << 62, + AHARDWAREBUFFER_USAGE_VENDOR_19 = 1ULL << 63, +}; + +typedef struct AHardwareBuffer_Desc { + uint32_t width; // width in pixels + uint32_t height; // height in pixels + uint32_t layers; // number of images + uint32_t format; // One of AHARDWAREBUFFER_FORMAT_* + uint64_t usage; // Combination of AHARDWAREBUFFER_USAGE_* + uint32_t stride; // 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; + +typedef struct AHardwareBuffer AHardwareBuffer; + +/** + * Allocates a buffer that backs an AHardwareBuffer using the passed + * AHardwareBuffer_Desc. + * + * Returns NO_ERROR on success, or an error number of the allocation fails for + * any reason. + */ +int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, + AHardwareBuffer** outBuffer); +/** + * Acquire a reference on the given AHardwareBuffer object. This prevents the + * object from being deleted until the last reference is removed. + */ +void AHardwareBuffer_acquire(AHardwareBuffer* buffer); + +/** + * Remove a reference that was previously acquired with + * AHardwareBuffer_acquire(). + */ +void AHardwareBuffer_release(AHardwareBuffer* buffer); + +/** + * Return a description of the AHardwareBuffer in the passed + * AHardwareBuffer_Desc struct. + */ +void AHardwareBuffer_describe(const AHardwareBuffer* buffer, + AHardwareBuffer_Desc* outDesc); + +/* + * Lock the AHardwareBuffer for reading or writing, depending on the usage flags + * passed. This call may block if the hardware needs to finish rendering or if + * CPU caches need to be synchronized, or possibly for other implementation- + * specific reasons. If fence is not negative, then it specifies a fence file + * descriptor that will be signaled when the buffer is locked, otherwise the + * caller will block until the buffer is available. + * + * If rect is not NULL, the caller promises to modify only data in the area + * specified by rect. If rect is NULL, the caller may modify the contents of the + * entire buffer. + * + * The content of the buffer outside of the specified rect is NOT modified + * by this call. + * + * The buffer usage may only specify AHARDWAREBUFFER_USAGE_CPU_*. If set, then + * outVirtualAddress is filled with the address of the buffer in virtual memory, + * otherwise this function will fail. + * + * THREADING CONSIDERATIONS: + * + * It is legal for several different threads to lock a buffer for read access; + * none of the threads are blocked. + * + * Locking a buffer simultaneously for write or read/write is undefined, but + * will neither terminate the process nor block the caller; AHardwareBuffer_lock + * may return an error or leave the buffer's content into an indeterminate + * state. + * + * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL or if the usage + * flags are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or an error + * number of the lock fails for any reason. + */ +int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage, + int32_t fence, const ARect* rect, void** outVirtualAddress); + +/* + * Unlock the AHardwareBuffer; must be called after all changes to the buffer + * are completed by the caller. If fence is not NULL then it will be set to a + * file descriptor that is signaled when all pending work on the buffer is + * completed. The caller is responsible for closing the fence when it is no + * longer needed. + * + * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error + * number of the lock fails for any reason. + */ +int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence); + +/* + * Send the AHardwareBuffer to an AF_UNIX socket. + * + * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error + * number of the lock fails for any reason. + */ +int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd); + +/* + * Receive the AHardwareBuffer from an AF_UNIX socket. + * + * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error + * number of the lock fails for any reason. + */ +int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer); + +__END_DECLS + +#endif // ANDROID_HARDWARE_BUFFER_H diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h new file mode 100644 index 0000000000..5290dd51cd --- /dev/null +++ b/libs/nativewindow/include/android/native_window.h @@ -0,0 +1,198 @@ +/* + * Copyright (C) 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. + */ + +/** + * @addtogroup NativeActivity Native Activity + * @{ + */ + +/** + * @file native_window.h + * @brief API for accessing a native window. + */ + +#ifndef ANDROID_NATIVE_WINDOW_H +#define ANDROID_NATIVE_WINDOW_H + +#include <sys/cdefs.h> + +#include <android/hardware_buffer.h> +#include <android/rect.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Legacy window pixel format names, kept for backwards compatibility. + * New code and APIs should use AHARDWAREBUFFER_FORMAT_*. + */ +enum { + // NOTE: these values must match the values from graphics/common/x.x/types.hal + + /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/ + WINDOW_FORMAT_RGBA_8888 = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Unused: 8 bits. **/ + WINDOW_FORMAT_RGBX_8888 = AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, + /** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/ + WINDOW_FORMAT_RGB_565 = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM, +}; + +/** + * Transforms that can be applied to buffers as they are displayed to a window. + * + * Supported transforms are any combination of horizontal mirror, vertical + * mirror, and clockwise 90 degree rotation, in that order. Rotations of 180 + * and 270 degrees are made up of those basic transforms. + */ +enum ANativeWindowTransform { + ANATIVEWINDOW_TRANSFORM_IDENTITY = 0x00, + ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL = 0x01, + ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL = 0x02, + ANATIVEWINDOW_TRANSFORM_ROTATE_90 = 0x04, + + ANATIVEWINDOW_TRANSFORM_ROTATE_180 = ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL | + ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL, + ANATIVEWINDOW_TRANSFORM_ROTATE_270 = ANATIVEWINDOW_TRANSFORM_ROTATE_180 | + ANATIVEWINDOW_TRANSFORM_ROTATE_90, +}; + +struct ANativeWindow; +/** + * Opaque type that provides access to a native window. + * + * A pointer can be obtained using {@link ANativeWindow_fromSurface()}. + */ +typedef struct ANativeWindow ANativeWindow; + +/** + * Struct that represents a windows buffer. + * + * A pointer can be obtained using {@link ANativeWindow_lock()}. + */ +typedef struct ANativeWindow_Buffer { + // The number of pixels that are show horizontally. + int32_t width; + + // The number of pixels that are shown vertically. + int32_t height; + + // The number of *pixels* that a line in the buffer takes in + // memory. This may be >= width. + int32_t stride; + + // The format of the buffer. One of AHARDWAREBUFFER_FORMAT_* + int32_t format; + + // The actual bits. + void* bits; + + // Do not touch. + uint32_t reserved[6]; +} ANativeWindow_Buffer; + +/** + * Acquire a reference on the given {@link ANativeWindow} object. This prevents the object + * from being deleted until the reference is removed. + */ +void ANativeWindow_acquire(ANativeWindow* window); + +/** + * Remove a reference that was previously acquired with {@link ANativeWindow_acquire()}. + */ +void ANativeWindow_release(ANativeWindow* window); + +/** + * Return the current width in pixels of the window surface. + * + * \return negative value on error. + */ +int32_t ANativeWindow_getWidth(ANativeWindow* window); + +/** + * Return the current height in pixels of the window surface. + * + * \return a negative value on error. + */ +int32_t ANativeWindow_getHeight(ANativeWindow* window); + +/** + * Return the current pixel format (AHARDWAREBUFFER_FORMAT_*) of the window surface. + * + * \return a negative value on error. + */ +int32_t ANativeWindow_getFormat(ANativeWindow* window); + +/** + * Change the format and size of the window buffers. + * + * The width and height control the number of pixels in the buffers, not the + * dimensions of the window on screen. If these are different than the + * window's physical size, then its buffer will be scaled to match that size + * when compositing it to the screen. The width and height must be either both zero + * or both non-zero. + * + * For all of these parameters, if 0 is supplied then the window's base + * value will come back in force. + * + * \param width width of the buffers in pixels. + * \param height height of the buffers in pixels. + * \param format one of AHARDWAREBUFFER_FORMAT_* constants. + * \return 0 for success, or a negative value on error. + */ +int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, + int32_t width, int32_t height, int32_t format); + +/** + * Lock the window's next drawing surface for writing. + * inOutDirtyBounds is used as an in/out parameter, upon entering the + * function, it contains the dirty region, that is, the region the caller + * intends to redraw. When the function returns, inOutDirtyBounds is updated + * with the actual area the caller needs to redraw -- this region is often + * extended by {@link ANativeWindow_lock}. + * + * \return 0 for success, or a negative value on error. + */ +int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer, + ARect* inOutDirtyBounds); + +/** + * Unlock the window's drawing surface after previously locking it, + * posting the new buffer to the display. + * + * \return 0 for success, or a negative value on error. + */ +int32_t ANativeWindow_unlockAndPost(ANativeWindow* window); + +#if __ANDROID_API__ >= __ANDROID_API_O__ + +/** + * Set a transform that will be applied to future buffers posted to the window. + * + * \param transform combination of {@link ANativeWindowTransform} flags + * \return 0 for success, or -EINVAL if \p transform is invalid + */ +int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transform); + +#endif // __ANDROID_API__ >= __ANDROID_API_O__ + +#ifdef __cplusplus +}; +#endif + +#endif // ANDROID_NATIVE_WINDOW_H + +/** @} */ diff --git a/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h new file mode 100644 index 0000000000..71f563467d --- /dev/null +++ b/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H +#define ANDROID_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H + +/* + * This file contains utility functions related to AHardwareBuffer, mostly to + * convert to/from HAL formats. + * + * These are PRIVATE methods, so this file can NEVER appear in a public NDK + * header. They are used by higher level libraries such as core/jni. + */ + +#include <stdint.h> + +struct AHardwareBuffer; +struct ANativeWindowBuffer; + +namespace android { + +// whether this AHardwareBuffer format is valid +bool AHardwareBuffer_isValidPixelFormat(uint32_t ahardwarebuffer_format); + +// convert AHardwareBuffer format to HAL format (note: this is a no-op) +uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format); + +// convert HAL format to AHardwareBuffer format (note: this is a no-op) +uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format); + +// convert AHardwareBuffer usage bits to HAL usage bits (note: this is a no-op) +uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage); + +// convert HAL usage bits to AHardwareBuffer usage bits (note: this is a no-op) +uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage); + +class GraphicBuffer; +const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(const AHardwareBuffer* buffer); +GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(AHardwareBuffer* buffer); + +const ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(const AHardwareBuffer* buffer); +ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(AHardwareBuffer* buffer); + +AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer); +} // namespace android + +#endif // ANDROID_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h new file mode 100644 index 0000000000..45110c490b --- /dev/null +++ b/libs/nativewindow/include/system/window.h @@ -0,0 +1,972 @@ +/* + * 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. + */ + +/************************************************************************************************* + * + * IMPORTANT: + * + * There is an old copy of this file in system/core/include/system/window.h, which exists only + * for backward source compatibility. + * But there are binaries out there as well, so this version of window.h must stay binary + * backward compatible with the one found in system/core. + * + * + * Source compatibility is also required for now, because this is how we're handling the + * transition from system/core/include (global include path) to nativewindow/include. + * + *************************************************************************************************/ + +#pragma once + +#include <cutils/native_handle.h> +#include <errno.h> +#include <limits.h> +#include <stdint.h> +#include <string.h> +#include <sys/cdefs.h> +#include <system/graphics.h> +#include <unistd.h> +#include <stdbool.h> + +// system/window.h is a superset of the vndk +#include <vndk/window.h> + + +#ifndef __UNUSED +#define __UNUSED __attribute__((__unused__)) +#endif +#ifndef __deprecated +#define __deprecated __attribute__((__deprecated__)) +#endif + +__BEGIN_DECLS + +/*****************************************************************************/ + +#define ANDROID_NATIVE_WINDOW_MAGIC ANDROID_NATIVE_MAKE_CONSTANT('_','w','n','d') + +// --------------------------------------------------------------------------- + +typedef const native_handle_t* buffer_handle_t; + +// --------------------------------------------------------------------------- + +typedef struct android_native_rect_t +{ + int32_t left; + int32_t top; + int32_t right; + int32_t bottom; +} android_native_rect_t; + +// --------------------------------------------------------------------------- + +// Old typedef for backwards compatibility. +typedef ANativeWindowBuffer_t android_native_buffer_t; + +// --------------------------------------------------------------------------- + +/* attributes queriable with query() */ +enum { + NATIVE_WINDOW_WIDTH = 0, + NATIVE_WINDOW_HEIGHT = 1, + NATIVE_WINDOW_FORMAT = 2, + + /* see ANativeWindowQuery in vndk/window.h */ + NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS, + + /* Check whether queueBuffer operations on the ANativeWindow send the buffer + * to the window compositor. The query sets the returned 'value' argument + * to 1 if the ANativeWindow DOES send queued buffers directly to the window + * compositor and 0 if the buffers do not go directly to the window + * compositor. + * + * This can be used to determine whether protected buffer content should be + * sent to the ANativeWindow. Note, however, that a result of 1 does NOT + * indicate that queued buffers will be protected from applications or users + * capturing their contents. If that behavior is desired then some other + * mechanism (e.g. the GRALLOC_USAGE_PROTECTED flag) should be used in + * conjunction with this query. + */ + NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER = 4, + + /* Get the concrete type of a ANativeWindow. See below for the list of + * possible return values. + * + * This query should not be used outside the Android framework and will + * likely be removed in the near future. + */ + NATIVE_WINDOW_CONCRETE_TYPE = 5, + + + /* + * Default width and height of ANativeWindow buffers, these are the + * dimensions of the window buffers irrespective of the + * NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS call and match the native window + * size unless overridden by NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS. + */ + NATIVE_WINDOW_DEFAULT_WIDTH = ANATIVEWINDOW_QUERY_DEFAULT_WIDTH, + NATIVE_WINDOW_DEFAULT_HEIGHT = ANATIVEWINDOW_QUERY_DEFAULT_HEIGHT, + + /* see ANativeWindowQuery in vndk/window.h */ + NATIVE_WINDOW_TRANSFORM_HINT = ANATIVEWINDOW_QUERY_TRANSFORM_HINT, + + /* + * Boolean that indicates whether the consumer is running more than + * one buffer behind the producer. + */ + NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND = 9, + + /* + * The consumer gralloc usage bits currently set by the consumer. + * The values are defined in hardware/libhardware/include/gralloc.h. + */ + NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10, + + /** + * Transformation that will by applied to buffers by the hwcomposer. + * This must not be set or checked by producer endpoints, and will + * disable the transform hint set in SurfaceFlinger (see + * NATIVE_WINDOW_TRANSFORM_HINT). + * + * INTENDED USE: + * Temporary - Please do not use this. This is intended only to be used + * by the camera's LEGACY mode. + * + * In situations where a SurfaceFlinger client wishes to set a transform + * that is not visible to the producer, and will always be applied in the + * hardware composer, the client can set this flag with + * native_window_set_buffers_sticky_transform. This can be used to rotate + * and flip buffers consumed by hardware composer without actually changing + * the aspect ratio of the buffers produced. + */ + NATIVE_WINDOW_STICKY_TRANSFORM = 11, + + /** + * The default data space for the buffers as set by the consumer. + * The values are defined in graphics.h. + */ + NATIVE_WINDOW_DEFAULT_DATASPACE = 12, + + /* see ANativeWindowQuery in vndk/window.h */ + NATIVE_WINDOW_BUFFER_AGE = ANATIVEWINDOW_QUERY_BUFFER_AGE, + + /* + * Returns the duration of the last dequeueBuffer call in microseconds + */ + NATIVE_WINDOW_LAST_DEQUEUE_DURATION = 14, + + /* + * Returns the duration of the last queueBuffer call in microseconds + */ + NATIVE_WINDOW_LAST_QUEUE_DURATION = 15, + + /* + * Returns the number of image layers that the ANativeWindow buffer + * contains. By default this is 1, unless a buffer is explicitly allocated + * to contain multiple layers. + */ + NATIVE_WINDOW_LAYER_COUNT = 16, + + /* + * Returns 1 if the native window is valid, 0 otherwise. native window is valid + * if it is safe (i.e. no crash will occur) to call any method on it. + */ + NATIVE_WINDOW_IS_VALID = 17, + + /* + * Returns 1 if NATIVE_WINDOW_GET_FRAME_TIMESTAMPS will return display + * present info, 0 if it won't. + */ + NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT = 18, + + /* + * The consumer end is capable of handling protected buffers, i.e. buffer + * with GRALLOC_USAGE_PROTECTED usage bits on. + */ + NATIVE_WINDOW_CONSUMER_IS_PROTECTED = 19, +}; + +/* Valid operations for the (*perform)() hook. + * + * Values marked as 'deprecated' are supported, but have been superceded by + * other functionality. + * + * Values marked as 'private' should be considered private to the framework. + * HAL implementation code with access to an ANativeWindow should not use these, + * as it may not interact properly with the framework's use of the + * ANativeWindow. + */ +enum { +// clang-format off + NATIVE_WINDOW_SET_USAGE = 0, + NATIVE_WINDOW_CONNECT = 1, /* deprecated */ + NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */ + NATIVE_WINDOW_SET_CROP = 3, /* private */ + NATIVE_WINDOW_SET_BUFFER_COUNT = 4, + NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = 5, /* deprecated */ + NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6, + NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7, + NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8, + NATIVE_WINDOW_SET_BUFFERS_FORMAT = 9, + NATIVE_WINDOW_SET_SCALING_MODE = 10, /* private */ + NATIVE_WINDOW_LOCK = 11, /* private */ + NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */ + NATIVE_WINDOW_API_CONNECT = 13, /* private */ + NATIVE_WINDOW_API_DISCONNECT = 14, /* private */ + NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */ + NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* private */ + NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17,/* private */ + NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18, + NATIVE_WINDOW_SET_BUFFERS_DATASPACE = 19, + NATIVE_WINDOW_SET_SURFACE_DAMAGE = 20, /* private */ + NATIVE_WINDOW_SET_SHARED_BUFFER_MODE = 21, + NATIVE_WINDOW_SET_AUTO_REFRESH = 22, + NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION= 23, + NATIVE_WINDOW_GET_NEXT_FRAME_ID = 24, + NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS = 25, + NATIVE_WINDOW_GET_COMPOSITOR_TIMING = 26, + NATIVE_WINDOW_GET_FRAME_TIMESTAMPS = 27, + NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28, + NATIVE_WINDOW_GET_HDR_SUPPORT = 29, +// clang-format on +}; + +/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */ +enum { + /* Buffers will be queued by EGL via eglSwapBuffers after being filled using + * OpenGL ES. + */ + NATIVE_WINDOW_API_EGL = 1, + + /* Buffers will be queued after being filled using the CPU + */ + NATIVE_WINDOW_API_CPU = 2, + + /* Buffers will be queued by Stagefright after being filled by a video + * decoder. The video decoder can either be a software or hardware decoder. + */ + NATIVE_WINDOW_API_MEDIA = 3, + + /* Buffers will be queued by the the camera HAL. + */ + NATIVE_WINDOW_API_CAMERA = 4, +}; + +/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */ +enum { + /* flip source image horizontally */ + NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H , + /* flip source image vertically */ + NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V, + /* rotate source image 90 degrees clock-wise, and is applied after TRANSFORM_FLIP_{H|V} */ + NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90, + /* rotate source image 180 degrees */ + NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180, + /* rotate source image 270 degrees clock-wise */ + NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270, + /* transforms source by the inverse transform of the screen it is displayed onto. This + * transform is applied last */ + NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY = 0x08 +}; + +/* parameter for NATIVE_WINDOW_SET_SCALING_MODE + * keep in sync with Surface.java in frameworks/base */ +enum { + /* the window content is not updated (frozen) until a buffer of + * the window size is received (enqueued) + */ + NATIVE_WINDOW_SCALING_MODE_FREEZE = 0, + /* the buffer is scaled in both dimensions to match the window size */ + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1, + /* the buffer is scaled uniformly such that the smaller dimension + * of the buffer matches the window size (cropping in the process) + */ + NATIVE_WINDOW_SCALING_MODE_SCALE_CROP = 2, + /* the window is clipped to the size of the buffer's crop rectangle; pixels + * outside the crop rectangle are treated as if they are completely + * transparent. + */ + NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP = 3, +}; + +/* values returned by the NATIVE_WINDOW_CONCRETE_TYPE query */ +enum { + NATIVE_WINDOW_FRAMEBUFFER = 0, /* FramebufferNativeWindow */ + NATIVE_WINDOW_SURFACE = 1, /* Surface */ +}; + +/* parameter for NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP + * + * Special timestamp value to indicate that timestamps should be auto-generated + * by the native window when queueBuffer is called. This is equal to INT64_MIN, + * defined directly to avoid problems with C99/C++ inclusion of stdint.h. + */ +static const int64_t NATIVE_WINDOW_TIMESTAMP_AUTO = (-9223372036854775807LL-1); + +/* parameter for NATIVE_WINDOW_GET_FRAME_TIMESTAMPS + * + * Special timestamp value to indicate the timestamps aren't yet known or + * that they are invalid. + */ +static const int64_t NATIVE_WINDOW_TIMESTAMP_PENDING = -2; +static const int64_t NATIVE_WINDOW_TIMESTAMP_INVALID = -1; + +struct ANativeWindow +{ +#ifdef __cplusplus + ANativeWindow() + : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0) + { + common.magic = ANDROID_NATIVE_WINDOW_MAGIC; + common.version = sizeof(ANativeWindow); + memset(common.reserved, 0, sizeof(common.reserved)); + } + + /* Implement the methods that sp<ANativeWindow> expects so that it + can be used to automatically refcount ANativeWindow's. */ + void incStrong(const void* /*id*/) const { + common.incRef(const_cast<android_native_base_t*>(&common)); + } + void decStrong(const void* /*id*/) const { + common.decRef(const_cast<android_native_base_t*>(&common)); + } +#endif + + struct android_native_base_t common; + + /* flags describing some attributes of this surface or its updater */ + const uint32_t flags; + + /* min swap interval supported by this updated */ + const int minSwapInterval; + + /* max swap interval supported by this updated */ + const int maxSwapInterval; + + /* horizontal and vertical resolution in DPI */ + const float xdpi; + const float ydpi; + + /* Some storage reserved for the OEM's driver. */ + intptr_t oem[4]; + + /* + * Set the swap interval for this surface. + * + * Returns 0 on success or -errno on error. + */ + int (*setSwapInterval)(struct ANativeWindow* window, + int interval); + + /* + * Hook called by EGL to acquire a buffer. After this call, the buffer + * is not locked, so its content cannot be modified. This call may block if + * no buffers are available. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * Returns 0 on success or -errno on error. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but the new dequeueBuffer function that + * outputs a fence file descriptor should be used in its place. + */ + int (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window, + struct ANativeWindowBuffer** buffer); + + /* + * hook called by EGL to lock a buffer. This MUST be called before modifying + * the content of a buffer. The buffer must have been acquired with + * dequeueBuffer first. + * + * Returns 0 on success or -errno on error. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but it is essentially a no-op, and calls + * to it should be removed. + */ + int (*lockBuffer_DEPRECATED)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer); + + /* + * Hook called by EGL when modifications to the render buffer are done. + * This unlocks and post the buffer. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * Buffers MUST be queued in the same order than they were dequeued. + * + * Returns 0 on success or -errno on error. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but the new queueBuffer function that + * takes a fence file descriptor should be used in its place (pass a value + * of -1 for the fence file descriptor if there is no valid one to pass). + */ + int (*queueBuffer_DEPRECATED)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer); + + /* + * hook used to retrieve information about the native window. + * + * Returns 0 on success or -errno on error. + */ + int (*query)(const struct ANativeWindow* window, + int what, int* value); + + /* + * hook used to perform various operations on the surface. + * (*perform)() is a generic mechanism to add functionality to + * ANativeWindow while keeping backward binary compatibility. + * + * DO NOT CALL THIS HOOK DIRECTLY. Instead, use the helper functions + * defined below. + * + * (*perform)() returns -ENOENT if the 'what' parameter is not supported + * by the surface's implementation. + * + * See above for a list of valid operations, such as + * NATIVE_WINDOW_SET_USAGE or NATIVE_WINDOW_CONNECT + */ + int (*perform)(struct ANativeWindow* window, + int operation, ... ); + + /* + * Hook used to cancel a buffer that has been dequeued. + * No synchronization is performed between dequeue() and cancel(), so + * either external synchronization is needed, or these functions must be + * called from the same thread. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but the new cancelBuffer function that + * takes a fence file descriptor should be used in its place (pass a value + * of -1 for the fence file descriptor if there is no valid one to pass). + */ + int (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer); + + /* + * Hook called by EGL to acquire a buffer. This call may block if no + * buffers are available. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * The libsync fence file descriptor returned in the int pointed to by the + * fenceFd argument will refer to the fence that must signal before the + * dequeued buffer may be written to. A value of -1 indicates that the + * caller may access the buffer immediately without waiting on a fence. If + * a valid file descriptor is returned (i.e. any value except -1) then the + * caller is responsible for closing the file descriptor. + * + * Returns 0 on success or -errno on error. + */ + int (*dequeueBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer** buffer, int* fenceFd); + + /* + * Hook called by EGL when modifications to the render buffer are done. + * This unlocks and post the buffer. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * The fenceFd argument specifies a libsync fence file descriptor for a + * fence that must signal before the buffer can be accessed. If the buffer + * can be accessed immediately then a value of -1 should be used. The + * caller must not use the file descriptor after it is passed to + * queueBuffer, and the ANativeWindow implementation is responsible for + * closing it. + * + * Returns 0 on success or -errno on error. + */ + int (*queueBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer, int fenceFd); + + /* + * Hook used to cancel a buffer that has been dequeued. + * No synchronization is performed between dequeue() and cancel(), so + * either external synchronization is needed, or these functions must be + * called from the same thread. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * The fenceFd argument specifies a libsync fence file decsriptor for a + * fence that must signal before the buffer can be accessed. If the buffer + * can be accessed immediately then a value of -1 should be used. + * + * Note that if the client has not waited on the fence that was returned + * from dequeueBuffer, that same fence should be passed to cancelBuffer to + * ensure that future uses of the buffer are preceded by a wait on that + * fence. The caller must not use the file descriptor after it is passed + * to cancelBuffer, and the ANativeWindow implementation is responsible for + * closing it. + * + * Returns 0 on success or -errno on error. + */ + int (*cancelBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer, int fenceFd); +}; + + /* Backwards compatibility: use ANativeWindow (struct ANativeWindow in C). + * android_native_window_t is deprecated. + */ +typedef struct ANativeWindow ANativeWindow; +typedef struct ANativeWindow android_native_window_t __deprecated; + +/* + * native_window_set_usage(..., usage) + * Sets the intended usage flags for the next buffers + * acquired with (*lockBuffer)() and on. + * By default (if this function is never called), a usage of + * GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE + * is assumed. + * Calling this function will usually cause following buffers to be + * reallocated. + */ + +static inline int native_window_set_usage( + struct ANativeWindow* window, int usage) +{ + return window->perform(window, NATIVE_WINDOW_SET_USAGE, usage); +} + +/* deprecated. Always returns 0. Don't call. */ +static inline int native_window_connect( + struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated; + +static inline int native_window_connect( + struct ANativeWindow* window __UNUSED, int api __UNUSED) { + return 0; +} + +/* deprecated. Always returns 0. Don't call. */ +static inline int native_window_disconnect( + struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated; + +static inline int native_window_disconnect( + struct ANativeWindow* window __UNUSED, int api __UNUSED) { + return 0; +} + +/* + * native_window_set_crop(..., crop) + * Sets which region of the next queued buffers needs to be considered. + * Depending on the scaling mode, a buffer's crop region is scaled and/or + * cropped to match the surface's size. This function sets the crop in + * pre-transformed buffer pixel coordinates. + * + * The specified crop region applies to all buffers queued after it is called. + * + * If 'crop' is NULL, subsequently queued buffers won't be cropped. + * + * An error is returned if for instance the crop region is invalid, out of the + * buffer's bound or if the window is invalid. + */ +static inline int native_window_set_crop( + struct ANativeWindow* window, + android_native_rect_t const * crop) +{ + return window->perform(window, NATIVE_WINDOW_SET_CROP, crop); +} + +/* + * native_window_set_post_transform_crop(..., crop) + * Sets which region of the next queued buffers needs to be considered. + * Depending on the scaling mode, a buffer's crop region is scaled and/or + * cropped to match the surface's size. This function sets the crop in + * post-transformed pixel coordinates. + * + * The specified crop region applies to all buffers queued after it is called. + * + * If 'crop' is NULL, subsequently queued buffers won't be cropped. + * + * An error is returned if for instance the crop region is invalid, out of the + * buffer's bound or if the window is invalid. + */ +static inline int native_window_set_post_transform_crop( + struct ANativeWindow* window, + android_native_rect_t const * crop) +{ + return window->perform(window, NATIVE_WINDOW_SET_POST_TRANSFORM_CROP, crop); +} + +/* + * native_window_set_active_rect(..., active_rect) + * + * This function is deprecated and will be removed soon. For now it simply + * sets the post-transform crop for compatibility while multi-project commits + * get checked. + */ +static inline int native_window_set_active_rect( + struct ANativeWindow* window, + android_native_rect_t const * active_rect) __deprecated; + +static inline int native_window_set_active_rect( + struct ANativeWindow* window, + android_native_rect_t const * active_rect) +{ + return native_window_set_post_transform_crop(window, active_rect); +} + +/* + * native_window_set_buffer_count(..., count) + * Sets the number of buffers associated with this native window. + */ +static inline int native_window_set_buffer_count( + struct ANativeWindow* window, + size_t bufferCount) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount); +} + +/* + * native_window_set_buffers_geometry(..., int w, int h, int format) + * All buffers dequeued after this call will have the dimensions and format + * specified. A successful call to this function has the same effect as calling + * native_window_set_buffers_size and native_window_set_buffers_format. + * + * XXX: This function is deprecated. The native_window_set_buffers_dimensions + * and native_window_set_buffers_format functions should be used instead. + */ +static inline int native_window_set_buffers_geometry( + struct ANativeWindow* window, + int w, int h, int format) __deprecated; + +static inline int native_window_set_buffers_geometry( + struct ANativeWindow* window, + int w, int h, int format) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY, + w, h, format); +} + +/* + * native_window_set_buffers_dimensions(..., int w, int h) + * All buffers dequeued after this call will have the dimensions specified. + * In particular, all buffers will have a fixed-size, independent from the + * native-window size. They will be scaled according to the scaling mode + * (see native_window_set_scaling_mode) upon window composition. + * + * If w and h are 0, the normal behavior is restored. That is, dequeued buffers + * following this call will be sized to match the window's size. + * + * Calling this function will reset the window crop to a NULL value, which + * disables cropping of the buffers. + */ +static inline int native_window_set_buffers_dimensions( + struct ANativeWindow* window, + int w, int h) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS, + w, h); +} + +/* + * native_window_set_buffers_user_dimensions(..., int w, int h) + * + * Sets the user buffer size for the window, which overrides the + * window's size. All buffers dequeued after this call will have the + * dimensions specified unless overridden by + * native_window_set_buffers_dimensions. All buffers will have a + * fixed-size, independent from the native-window size. They will be + * scaled according to the scaling mode (see + * native_window_set_scaling_mode) upon window composition. + * + * If w and h are 0, the normal behavior is restored. That is, the + * default buffer size will match the windows's size. + * + * Calling this function will reset the window crop to a NULL value, which + * disables cropping of the buffers. + */ +static inline int native_window_set_buffers_user_dimensions( + struct ANativeWindow* window, + int w, int h) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS, + w, h); +} + +/* + * native_window_set_buffers_format(..., int format) + * All buffers dequeued after this call will have the format specified. + * + * If the specified format is 0, the default buffer format will be used. + */ +static inline int native_window_set_buffers_format( + struct ANativeWindow* window, + int format) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_FORMAT, format); +} + +/* + * native_window_set_buffers_data_space(..., int dataSpace) + * All buffers queued after this call will be associated with the dataSpace + * parameter specified. + * + * dataSpace specifies additional information about the buffer that's dependent + * on the buffer format and the endpoints. For example, it can be used to convey + * the color space of the image data in the buffer, or it can be used to + * indicate that the buffers contain depth measurement data instead of color + * images. The default dataSpace is 0, HAL_DATASPACE_UNKNOWN, unless it has been + * overridden by the consumer. + */ +static inline int native_window_set_buffers_data_space( + struct ANativeWindow* window, + android_dataspace_t dataSpace) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DATASPACE, + dataSpace); +} + +/* + * native_window_set_buffers_transform(..., int transform) + * All buffers queued after this call will be displayed transformed according + * to the transform parameter specified. + */ +static inline int native_window_set_buffers_transform( + struct ANativeWindow* window, + int transform) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM, + transform); +} + +/* + * native_window_set_buffers_sticky_transform(..., int transform) + * All buffers queued after this call will be displayed transformed according + * to the transform parameter specified applied on top of the regular buffer + * transform. Setting this transform will disable the transform hint. + * + * Temporary - This is only intended to be used by the LEGACY camera mode, do + * not use this for anything else. + */ +static inline int native_window_set_buffers_sticky_transform( + struct ANativeWindow* window, + int transform) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM, + transform); +} + +/* + * native_window_set_buffers_timestamp(..., int64_t timestamp) + * All buffers queued after this call will be associated with the timestamp + * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO + * (the default), timestamps will be generated automatically when queueBuffer is + * called. The timestamp is measured in nanoseconds, and is normally monotonically + * increasing. The timestamp should be unaffected by time-of-day adjustments, + * and for a camera should be strictly monotonic but for a media player may be + * reset when the position is set. + */ +static inline int native_window_set_buffers_timestamp( + struct ANativeWindow* window, + int64_t timestamp) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP, + timestamp); +} + +/* + * native_window_set_scaling_mode(..., int mode) + * All buffers queued after this call will be associated with the scaling mode + * specified. + */ +static inline int native_window_set_scaling_mode( + struct ANativeWindow* window, + int mode) +{ + return window->perform(window, NATIVE_WINDOW_SET_SCALING_MODE, + mode); +} + +/* + * native_window_api_connect(..., int api) + * connects an API to this window. only one API can be connected at a time. + * Returns -EINVAL if for some reason the window cannot be connected, which + * can happen if it's connected to some other API. + */ +static inline int native_window_api_connect( + struct ANativeWindow* window, int api) +{ + return window->perform(window, NATIVE_WINDOW_API_CONNECT, api); +} + +/* + * native_window_api_disconnect(..., int api) + * disconnect the API from this window. + * An error is returned if for instance the window wasn't connected in the + * first place. + */ +static inline int native_window_api_disconnect( + struct ANativeWindow* window, int api) +{ + return window->perform(window, NATIVE_WINDOW_API_DISCONNECT, api); +} + +/* + * native_window_dequeue_buffer_and_wait(...) + * Dequeue a buffer and wait on the fence associated with that buffer. The + * buffer may safely be accessed immediately upon this function returning. An + * error is returned if either of the dequeue or the wait operations fail. + */ +static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw, + struct ANativeWindowBuffer** anb) { + return anw->dequeueBuffer_DEPRECATED(anw, anb); +} + +/* + * native_window_set_sideband_stream(..., native_handle_t*) + * Attach a sideband buffer stream to a native window. + */ +static inline int native_window_set_sideband_stream( + struct ANativeWindow* window, + native_handle_t* sidebandHandle) +{ + return window->perform(window, NATIVE_WINDOW_SET_SIDEBAND_STREAM, + sidebandHandle); +} + +/* + * native_window_set_surface_damage(..., android_native_rect_t* rects, int numRects) + * Set the surface damage (i.e., the region of the surface that has changed + * since the previous frame). The damage set by this call will be reset (to the + * default of full-surface damage) after calling queue, so this must be called + * prior to every frame with damage that does not cover the whole surface if the + * caller desires downstream consumers to use this optimization. + * + * The damage region is specified as an array of rectangles, with the important + * caveat that the origin of the surface is considered to be the bottom-left + * corner, as in OpenGL ES. + * + * If numRects is set to 0, rects may be NULL, and the surface damage will be + * set to the full surface (the same as if this function had not been called for + * this frame). + */ +static inline int native_window_set_surface_damage( + struct ANativeWindow* window, + const android_native_rect_t* rects, size_t numRects) +{ + return window->perform(window, NATIVE_WINDOW_SET_SURFACE_DAMAGE, + rects, numRects); +} + +/* + * native_window_set_shared_buffer_mode(..., bool sharedBufferMode) + * Enable/disable shared buffer mode + */ +static inline int native_window_set_shared_buffer_mode( + struct ANativeWindow* window, + bool sharedBufferMode) +{ + return window->perform(window, NATIVE_WINDOW_SET_SHARED_BUFFER_MODE, + sharedBufferMode); +} + +/* + * native_window_set_auto_refresh(..., autoRefresh) + * Enable/disable auto refresh when in shared buffer mode + */ +static inline int native_window_set_auto_refresh( + struct ANativeWindow* window, + bool autoRefresh) +{ + return window->perform(window, NATIVE_WINDOW_SET_AUTO_REFRESH, autoRefresh); +} + +static inline int native_window_get_refresh_cycle_duration( + struct ANativeWindow* window, + int64_t* outRefreshDuration) +{ + return window->perform(window, NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION, + outRefreshDuration); +} + +static inline int native_window_get_next_frame_id( + struct ANativeWindow* window, uint64_t* frameId) +{ + return window->perform(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID, frameId); +} + +static inline int native_window_enable_frame_timestamps( + struct ANativeWindow* window, bool enable) +{ + return window->perform(window, NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS, + enable); +} + +static inline int native_window_get_compositor_timing( + struct ANativeWindow* window, + int64_t* compositeDeadline, int64_t* compositeInterval, + int64_t* compositeToPresentLatency) +{ + return window->perform(window, NATIVE_WINDOW_GET_COMPOSITOR_TIMING, + compositeDeadline, compositeInterval, compositeToPresentLatency); +} + +static inline int native_window_get_frame_timestamps( + struct ANativeWindow* window, uint64_t frameId, + int64_t* outRequestedPresentTime, int64_t* outAcquireTime, + int64_t* outLatchTime, int64_t* outFirstRefreshStartTime, + int64_t* outLastRefreshStartTime, int64_t* outGpuCompositionDoneTime, + int64_t* outDisplayPresentTime, int64_t* outDequeueReadyTime, + int64_t* outReleaseTime) +{ + return window->perform(window, NATIVE_WINDOW_GET_FRAME_TIMESTAMPS, + frameId, outRequestedPresentTime, outAcquireTime, outLatchTime, + outFirstRefreshStartTime, outLastRefreshStartTime, + outGpuCompositionDoneTime, outDisplayPresentTime, + outDequeueReadyTime, outReleaseTime); +} + +static inline int native_window_get_wide_color_support( + struct ANativeWindow* window, bool* outSupport) { + return window->perform(window, NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT, + outSupport); +} + +static inline int native_window_get_hdr_support(struct ANativeWindow* window, + bool* outSupport) { + return window->perform(window, NATIVE_WINDOW_GET_HDR_SUPPORT, outSupport); +} + +__END_DECLS diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h new file mode 100644 index 0000000000..802edcc17d --- /dev/null +++ b/libs/nativewindow/include/vndk/hardware_buffer.h @@ -0,0 +1,74 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H +#define ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H + +// vndk is a superset of the NDK +#include <android/hardware_buffer.h> + +#include <cutils/native_handle.h> + +__BEGIN_DECLS + +const native_handle_t* AHardwareBuffer_getNativeHandle(const AHardwareBuffer* buffer); + + +/** + * Buffer pixel formats. + */ +enum { + /* for future proofing, keep these in sync with system/graphics-base.h */ + + /* same as HAL_PIXEL_FORMAT_BGRA_8888 */ + AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM = 5, + /* same as HAL_PIXEL_FORMAT_YV12 */ + AHARDWAREBUFFER_FORMAT_YV12 = 0x32315659, + /* same as HAL_PIXEL_FORMAT_Y8 */ + AHARDWAREBUFFER_FORMAT_Y8 = 0x20203859, + /* same as HAL_PIXEL_FORMAT_Y16 */ + AHARDWAREBUFFER_FORMAT_Y16 = 0x20363159, + /* same as HAL_PIXEL_FORMAT_RAW16 */ + AHARDWAREBUFFER_FORMAT_RAW16 = 0x20, + /* same as HAL_PIXEL_FORMAT_RAW10 */ + AHARDWAREBUFFER_FORMAT_RAW10 = 0x25, + /* same as HAL_PIXEL_FORMAT_RAW12 */ + AHARDWAREBUFFER_FORMAT_RAW12 = 0x26, + /* same as HAL_PIXEL_FORMAT_RAW_OPAQUE */ + AHARDWAREBUFFER_FORMAT_RAW_OPAQUE = 0x24, + /* same as HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED */ + AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED = 0x22, + /* same as HAL_PIXEL_FORMAT_YCBCR_420_888 */ + AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23, + /* same as HAL_PIXEL_FORMAT_YCBCR_422_888 */ + AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_422 = 0x27, + /* same as HAL_PIXEL_FORMAT_YCBCR_444_888 */ + AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_444 = 0x28, + /* same as HAL_PIXEL_FORMAT_FLEX_RGB_888 */ + AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8 = 0x29, + /* same as HAL_PIXEL_FORMAT_FLEX_RGBA_8888 */ + AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8A8 = 0x2A, + /* same as HAL_PIXEL_FORMAT_YCBCR_422_SP */ + AHARDWAREBUFFER_FORMAT_YCbCr_422_SP = 0x10, + /* same as HAL_PIXEL_FORMAT_YCRCB_420_SP */ + AHARDWAREBUFFER_FORMAT_YCrCb_420_SP = 0x11, + /* same as HAL_PIXEL_FORMAT_YCBCR_422_I */ + AHARDWAREBUFFER_FORMAT_YCbCr_422_I = 0x14, +}; + +__END_DECLS + +#endif /* ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H */ diff --git a/libs/nativewindow/include/vndk/window.h b/libs/nativewindow/include/vndk/window.h new file mode 100644 index 0000000000..95618c472d --- /dev/null +++ b/libs/nativewindow/include/vndk/window.h @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_VNDK_NATIVEWINDOW_ANATIVEWINDOW_H +#define ANDROID_VNDK_NATIVEWINDOW_ANATIVEWINDOW_H + +#include <stdint.h> +#include <stdbool.h> +#include <sys/cdefs.h> +#include <system/graphics-base.h> +#include <cutils/native_handle.h> + +// vndk is a superset of the NDK +#include <android/native_window.h> + +__BEGIN_DECLS + +/*****************************************************************************/ + +#ifdef __cplusplus +#define ANDROID_NATIVE_UNSIGNED_CAST(x) static_cast<unsigned int>(x) +#else +#define ANDROID_NATIVE_UNSIGNED_CAST(x) ((unsigned int)(x)) +#endif + +#define ANDROID_NATIVE_MAKE_CONSTANT(a,b,c,d) \ + ((ANDROID_NATIVE_UNSIGNED_CAST(a) << 24) | \ + (ANDROID_NATIVE_UNSIGNED_CAST(b) << 16) | \ + (ANDROID_NATIVE_UNSIGNED_CAST(c) << 8) | \ + (ANDROID_NATIVE_UNSIGNED_CAST(d))) + +#define ANDROID_NATIVE_BUFFER_MAGIC ANDROID_NATIVE_MAKE_CONSTANT('_','b','f','r') + + +/*****************************************************************************/ + +typedef struct android_native_base_t +{ + /* a magic value defined by the actual EGL native type */ + int magic; + + /* the sizeof() of the actual EGL native type */ + int version; + + void* reserved[4]; + + /* reference-counting interface */ + void (*incRef)(struct android_native_base_t* base); + void (*decRef)(struct android_native_base_t* base); +} android_native_base_t; + +typedef struct ANativeWindowBuffer +{ +#ifdef __cplusplus + ANativeWindowBuffer() { + common.magic = ANDROID_NATIVE_BUFFER_MAGIC; + common.version = sizeof(ANativeWindowBuffer); + memset(common.reserved, 0, sizeof(common.reserved)); + } + + // Implement the methods that sp<ANativeWindowBuffer> expects so that it + // can be used to automatically refcount ANativeWindowBuffer's. + void incStrong(const void* /*id*/) const { + common.incRef(const_cast<android_native_base_t*>(&common)); + } + void decStrong(const void* /*id*/) const { + common.decRef(const_cast<android_native_base_t*>(&common)); + } +#endif + + struct android_native_base_t common; + + int width; + int height; + int stride; + int format; + int usage; + uintptr_t layerCount; + + void* reserved[1]; + + const native_handle_t* handle; + + void* reserved_proc[8]; +} ANativeWindowBuffer_t; + +typedef struct ANativeWindowBuffer ANativeWindowBuffer; + +/* + * Convert this ANativeWindowBuffer into a AHardwareBuffer + */ +AHardwareBuffer* ANativeWindowBuffer_getHardwareBuffer(ANativeWindowBuffer* anwb); + +/*****************************************************************************/ + +/* + * Stores a value into one of the 4 available slots + * Retrieve the value with ANativeWindow_OemStorageGet() + * + * slot: 0 to 3 + * + * Returns 0 on success or -errno on error. + */ +int ANativeWindow_OemStorageSet(ANativeWindow* window, uint32_t slot, intptr_t value); + + +/* + * Retrieves a value from one of the 4 available slots + * By default the returned value is 0 if it wasn't set by ANativeWindow_OemStorageSet() + * + * slot: 0 to 3 + * + * Returns 0 on success or -errno on error. + */ +int ANativeWindow_OemStorageGet(ANativeWindow* window, uint32_t slot, intptr_t* value); + + +/* + * Set the swap interval for this surface. + * + * Returns 0 on success or -errno on error. + */ +int ANativeWindow_setSwapInterval(ANativeWindow* window, int interval); + + +/* + * queries that can be used with ANativeWindow_query() and ANativeWindow_queryf() + */ +enum ANativeWindowQuery { + /* The minimum number of buffers that must remain un-dequeued after a buffer + * has been queued. This value applies only if set_buffer_count was used to + * override the number of buffers and if a buffer has since been queued. + * Users of the set_buffer_count ANativeWindow method should query this + * value before calling set_buffer_count. If it is necessary to have N + * buffers simultaneously dequeued as part of the steady-state operation, + * and this query returns M then N+M buffers should be requested via + * native_window_set_buffer_count. + * + * Note that this value does NOT apply until a single buffer has been + * queued. In particular this means that it is possible to: + * + * 1. Query M = min undequeued buffers + * 2. Set the buffer count to N + M + * 3. Dequeue all N + M buffers + * 4. Cancel M buffers + * 5. Queue, dequeue, queue, dequeue, ad infinitum + */ + ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS = 3, + + /* + * Default width of ANativeWindow buffers, these are the + * dimensions of the window buffers irrespective of the + * ANativeWindow_setBuffersDimensions() call and match the native window + * size. + */ + ANATIVEWINDOW_QUERY_DEFAULT_WIDTH = 6, + ANATIVEWINDOW_QUERY_DEFAULT_HEIGHT = 7, + + /* + * transformation that will most-likely be applied to buffers. This is only + * a hint, the actual transformation applied might be different. + * + * INTENDED USE: + * + * The transform hint can be used by a producer, for instance the GLES + * driver, to pre-rotate the rendering such that the final transformation + * in the composer is identity. This can be very useful when used in + * conjunction with the h/w composer HAL, in situations where it + * cannot handle arbitrary rotations. + * + * 1. Before dequeuing a buffer, the GL driver (or any other ANW client) + * queries the ANW for NATIVE_WINDOW_TRANSFORM_HINT. + * + * 2. The GL driver overrides the width and height of the ANW to + * account for NATIVE_WINDOW_TRANSFORM_HINT. This is done by querying + * NATIVE_WINDOW_DEFAULT_{WIDTH | HEIGHT}, swapping the dimensions + * according to NATIVE_WINDOW_TRANSFORM_HINT and calling + * native_window_set_buffers_dimensions(). + * + * 3. The GL driver dequeues a buffer of the new pre-rotated size. + * + * 4. The GL driver renders to the buffer such that the image is + * already transformed, that is applying NATIVE_WINDOW_TRANSFORM_HINT + * to the rendering. + * + * 5. The GL driver calls native_window_set_transform to apply + * inverse transformation to the buffer it just rendered. + * In order to do this, the GL driver needs + * to calculate the inverse of NATIVE_WINDOW_TRANSFORM_HINT, this is + * done easily: + * + * int hintTransform, inverseTransform; + * query(..., NATIVE_WINDOW_TRANSFORM_HINT, &hintTransform); + * inverseTransform = hintTransform; + * if (hintTransform & HAL_TRANSFORM_ROT_90) + * inverseTransform ^= HAL_TRANSFORM_ROT_180; + * + * + * 6. The GL driver queues the pre-transformed buffer. + * + * 7. The composer combines the buffer transform with the display + * transform. If the buffer transform happens to cancel out the + * display transform then no rotation is needed. + * + */ + ANATIVEWINDOW_QUERY_TRANSFORM_HINT = 8, + + /* + * Returns the age of the contents of the most recently dequeued buffer as + * the number of frames that have elapsed since it was last queued. For + * example, if the window is double-buffered, the age of any given buffer in + * steady state will be 2. If the dequeued buffer has never been queued, its + * age will be 0. + */ + ANATIVEWINDOW_QUERY_BUFFER_AGE = 13, + + /* min swap interval supported by this compositor */ + ANATIVEWINDOW_QUERY_MIN_SWAP_INTERVAL = 0x10000, + + /* max swap interval supported by this compositor */ + ANATIVEWINDOW_QUERY_MAX_SWAP_INTERVAL = 0x10001, + + /* horizontal resolution in DPI. value is float, use queryf() */ + ANATIVEWINDOW_QUERY_XDPI = 0x10002, + + /* vertical resolution in DPI. value is float, use queryf() */ + ANATIVEWINDOW_QUERY_YDPI = 0x10003, +}; + +typedef enum ANativeWindowQuery ANativeWindowQuery; + +/* + * hook used to retrieve information about the native window. + * + * Returns 0 on success or -errno on error. + */ +int ANativeWindow_query(const ANativeWindow* window, ANativeWindowQuery query, int* value); +int ANativeWindow_queryf(const ANativeWindow* window, ANativeWindowQuery query, float* value); + + +/* + * Hook called by EGL to acquire a buffer. This call may block if no + * buffers are available. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * The libsync fence file descriptor returned in the int pointed to by the + * fenceFd argument will refer to the fence that must signal before the + * dequeued buffer may be written to. A value of -1 indicates that the + * caller may access the buffer immediately without waiting on a fence. If + * a valid file descriptor is returned (i.e. any value except -1) then the + * caller is responsible for closing the file descriptor. + * + * Returns 0 on success or -errno on error. + */ +int ANativeWindow_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd); + + +/* + * Hook called by EGL when modifications to the render buffer are done. + * This unlocks and post the buffer. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * The fenceFd argument specifies a libsync fence file descriptor for a + * fence that must signal before the buffer can be accessed. If the buffer + * can be accessed immediately then a value of -1 should be used. The + * caller must not use the file descriptor after it is passed to + * queueBuffer, and the ANativeWindow implementation is responsible for + * closing it. + * + * Returns 0 on success or -errno on error. + */ +int ANativeWindow_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd); + + +/* + * Hook used to cancel a buffer that has been dequeued. + * No synchronization is performed between dequeue() and cancel(), so + * either external synchronization is needed, or these functions must be + * called from the same thread. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * The fenceFd argument specifies a libsync fence file decsriptor for a + * fence that must signal before the buffer can be accessed. If the buffer + * can be accessed immediately then a value of -1 should be used. + * + * Note that if the client has not waited on the fence that was returned + * from dequeueBuffer, that same fence should be passed to cancelBuffer to + * ensure that future uses of the buffer are preceded by a wait on that + * fence. The caller must not use the file descriptor after it is passed + * to cancelBuffer, and the ANativeWindow implementation is responsible for + * closing it. + * + * Returns 0 on success or -errno on error. + */ +int ANativeWindow_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd); + +/* + * Sets the intended usage flags for the next buffers. + * + * usage: one of AHARDWAREBUFFER_USAGE_* constant + * + * By default (if this function is never called), a usage of + * AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT + * is assumed. + * + * Calling this function will usually cause following buffers to be + * reallocated. + */ +int ANativeWindow_setUsage(ANativeWindow* window, uint64_t usage); + + +/* + * Sets the number of buffers associated with this native window. + */ +int ANativeWindow_setBufferCount(ANativeWindow* window, size_t bufferCount); + + +/* + * All buffers dequeued after this call will have the dimensions specified. + * In particular, all buffers will have a fixed-size, independent from the + * native-window size. They will be scaled according to the scaling mode + * (see native_window_set_scaling_mode) upon window composition. + * + * If w and h are 0, the normal behavior is restored. That is, dequeued buffers + * following this call will be sized to match the window's size. + * + * Calling this function will reset the window crop to a NULL value, which + * disables cropping of the buffers. + */ +int ANativeWindow_setBuffersDimensions(ANativeWindow* window, uint32_t w, uint32_t h); + + +/* + * All buffers dequeued after this call will have the format specified. + * format: one of AHARDWAREBUFFER_FORMAT_* constant + * + * If the specified format is 0, the default buffer format will be used. + */ +int ANativeWindow_setBuffersFormat(ANativeWindow* window, int format); + + +/* + * All buffers queued after this call will be associated with the timestamp in nanosecond + * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO + * (the default), timestamps will be generated automatically when queueBuffer is + * called. The timestamp is measured in nanoseconds, and is normally monotonically + * increasing. The timestamp should be unaffected by time-of-day adjustments, + * and for a camera should be strictly monotonic but for a media player may be + * reset when the position is set. + */ +int ANativeWindow_setBuffersTimestamp(ANativeWindow* window, int64_t timestamp); + + +/* + * All buffers queued after this call will be associated with the dataSpace + * parameter specified. + * + * dataSpace specifies additional information about the buffer that's dependent + * on the buffer format and the endpoints. For example, it can be used to convey + * the color space of the image data in the buffer, or it can be used to + * indicate that the buffers contain depth measurement data instead of color + * images. The default dataSpace is 0, HAL_DATASPACE_UNKNOWN, unless it has been + * overridden by the consumer. + */ +int ANativeWindow_setBufferDataSpace(ANativeWindow* window, android_dataspace_t dataSpace); + + +/* + * Enable/disable shared buffer mode + */ +int ANativeWindow_setSharedBufferMode(ANativeWindow* window, bool sharedBufferMode); + + +/* + * Enable/disable auto refresh when in shared buffer mode + */ +int ANativeWindow_setAutoRefresh(ANativeWindow* window, bool autoRefresh); + + +/*****************************************************************************/ + +__END_DECLS + +#endif /* ANDROID_VNDK_NATIVEWINDOW_ANATIVEWINDOW_H */ diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt new file mode 100644 index 0000000000..b1d1a725a1 --- /dev/null +++ b/libs/nativewindow/libnativewindow.map.txt @@ -0,0 +1,26 @@ +LIBNATIVEWINDOW { + global: + AHardwareBuffer_acquire; + AHardwareBuffer_allocate; + AHardwareBuffer_describe; + AHardwareBuffer_fromHardwareBuffer; + AHardwareBuffer_lock; + AHardwareBuffer_recvHandleFromUnixSocket; + AHardwareBuffer_release; + AHardwareBuffer_sendHandleToUnixSocket; + AHardwareBuffer_toHardwareBuffer; + AHardwareBuffer_unlock; + ANativeWindow_acquire; + ANativeWindow_fromSurface; + ANativeWindow_fromSurfaceTexture; + ANativeWindow_getFormat; + ANativeWindow_getHeight; + ANativeWindow_getWidth; + ANativeWindow_lock; + ANativeWindow_release; + ANativeWindow_setBuffersGeometry; + ANativeWindow_setBuffersTransform; + ANativeWindow_unlockAndPost; + local: + *; +}; diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp new file mode 100644 index 0000000000..cc2731d908 --- /dev/null +++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "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 <gtest/gtest.h> + +using namespace android; +using android::hardware::graphics::common::V1_0::BufferUsage; + +static ::testing::AssertionResult BuildHexFailureMessage(uint64_t expected, + uint64_t actual, const char* type) { + std::ostringstream ss; + ss << type << " 0x" << std::hex << actual + << " does not match expected " << type << " 0x" << std::hex + << expected; + return ::testing::AssertionFailure() << ss.str(); +} + +static ::testing::AssertionResult TestUsageConversion( + uint64_t grallocUsage, uint64_t hardwareBufferUsage) { + uint64_t convertedGrallocUsage = AHardwareBuffer_convertToGrallocUsageBits(hardwareBufferUsage); + if (convertedGrallocUsage != grallocUsage) + return BuildHexFailureMessage(grallocUsage, convertedGrallocUsage, "converToGralloc"); + + uint64_t convertedHArdwareBufferUsage = AHardwareBuffer_convertFromGrallocUsageBits(grallocUsage); + if (convertedHArdwareBufferUsage != grallocUsage) + return BuildHexFailureMessage(grallocUsage, convertedHArdwareBufferUsage, "convertFromGralloc"); + + return testing::AssertionSuccess(); +} + +// This is a unit test rather than going through AHardwareBuffer because not +// all flags may be supported by the host device. +TEST(AHardwareBufferTest, ConvertToAndFromGrallocBits) { + EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_READ_RARELY, + AHARDWAREBUFFER_USAGE_CPU_READ_RARELY)); + EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_READ_OFTEN, + AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN)); + EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_WRITE_RARELY, + AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY)); + EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_WRITE_OFTEN, + AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN)); + + EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::GPU_TEXTURE, + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE)); + EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::GPU_RENDER_TARGET, + AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT)); + EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::GPU_DATA_BUFFER, + AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER)); + EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::PROTECTED, + AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT)); + EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::SENSOR_DIRECT_DATA, + AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA)); + EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::VIDEO_ENCODER, + AHARDWAREBUFFER_USAGE_VIDEO_ENCODE)); + + EXPECT_TRUE(TestUsageConversion(1ull<<28, AHARDWAREBUFFER_USAGE_VENDOR_0)); + EXPECT_TRUE(TestUsageConversion(1ull<<29, AHARDWAREBUFFER_USAGE_VENDOR_1)); + EXPECT_TRUE(TestUsageConversion(1ull<<30, AHARDWAREBUFFER_USAGE_VENDOR_2)); + EXPECT_TRUE(TestUsageConversion(1ull<<31, AHARDWAREBUFFER_USAGE_VENDOR_3)); + EXPECT_TRUE(TestUsageConversion(1ull<<48, AHARDWAREBUFFER_USAGE_VENDOR_4)); + EXPECT_TRUE(TestUsageConversion(1ull<<49, AHARDWAREBUFFER_USAGE_VENDOR_5)); + EXPECT_TRUE(TestUsageConversion(1ull<<50, AHARDWAREBUFFER_USAGE_VENDOR_6)); + EXPECT_TRUE(TestUsageConversion(1ull<<51, AHARDWAREBUFFER_USAGE_VENDOR_7)); + EXPECT_TRUE(TestUsageConversion(1ull<<52, AHARDWAREBUFFER_USAGE_VENDOR_8)); + EXPECT_TRUE(TestUsageConversion(1ull<<53, AHARDWAREBUFFER_USAGE_VENDOR_9)); + EXPECT_TRUE(TestUsageConversion(1ull<<54, AHARDWAREBUFFER_USAGE_VENDOR_10)); + EXPECT_TRUE(TestUsageConversion(1ull<<55, AHARDWAREBUFFER_USAGE_VENDOR_11)); + EXPECT_TRUE(TestUsageConversion(1ull<<56, AHARDWAREBUFFER_USAGE_VENDOR_12)); + EXPECT_TRUE(TestUsageConversion(1ull<<57, AHARDWAREBUFFER_USAGE_VENDOR_13)); + EXPECT_TRUE(TestUsageConversion(1ull<<58, AHARDWAREBUFFER_USAGE_VENDOR_14)); + EXPECT_TRUE(TestUsageConversion(1ull<<59, AHARDWAREBUFFER_USAGE_VENDOR_15)); + EXPECT_TRUE(TestUsageConversion(1ull<<60, AHARDWAREBUFFER_USAGE_VENDOR_16)); + EXPECT_TRUE(TestUsageConversion(1ull<<61, AHARDWAREBUFFER_USAGE_VENDOR_17)); + EXPECT_TRUE(TestUsageConversion(1ull<<62, AHARDWAREBUFFER_USAGE_VENDOR_18)); + EXPECT_TRUE(TestUsageConversion(1ull<<63, AHARDWAREBUFFER_USAGE_VENDOR_19)); + + // Test some more complex flag combinations. + EXPECT_TRUE(TestUsageConversion( + (uint64_t)BufferUsage::CPU_READ_RARELY | + (uint64_t)BufferUsage::CPU_WRITE_RARELY, + AHARDWAREBUFFER_USAGE_CPU_READ_RARELY | AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY)); + +EXPECT_TRUE(TestUsageConversion( + (uint64_t)BufferUsage::GPU_RENDER_TARGET | (uint64_t)BufferUsage::GPU_TEXTURE | + 1ull << 29 | 1ull << 57, + AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | + AHARDWAREBUFFER_USAGE_VENDOR_1 | AHARDWAREBUFFER_USAGE_VENDOR_13)); +} diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp new file mode 100644 index 0000000000..b89c35ac64 --- /dev/null +++ b/libs/nativewindow/tests/Android.bp @@ -0,0 +1,26 @@ +// +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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: "AHardwareBufferTest", + shared_libs: [ + "libnativewindow", + "android.hardware.graphics.common@1.0", + ], + srcs: [ + "AHardwareBufferTest.cpp", + "c_compatibility.c"], +} diff --git a/libs/nativewindow/tests/c_compatibility.c b/libs/nativewindow/tests/c_compatibility.c new file mode 100644 index 0000000000..befd88fd07 --- /dev/null +++ b/libs/nativewindow/tests/c_compatibility.c @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/hardware_buffer.h> +#include <android/native_window.h> +#include <vndk/hardware_buffer.h> +#include <vndk/window.h> + +// this checks that all these headers are C-compatible diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp new file mode 100644 index 0000000000..171a627c7e --- /dev/null +++ b/libs/sensor/Android.bp @@ -0,0 +1,61 @@ +// 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. + +cc_library_shared { + name: "libsensor", + + clang: true, + cppflags: [ + "-Weverything", + "-Werror", + + // The static constructors and destructors in this library have not been noted to + // introduce significant overheads + "-Wno-exit-time-destructors", + "-Wno-global-constructors", + + // We only care about compiling as C++14 + "-Wno-c++98-compat-pedantic", + + // android/sensors.h uses nested anonymous unions and anonymous structs + "-Wno-nested-anon-types", + "-Wno-gnu-anonymous-struct", + + // Don't warn about struct padding + "-Wno-padded", + ], + + srcs: [ + "BitTube.cpp", + "ISensorEventConnection.cpp", + "ISensorServer.cpp", + "Sensor.cpp", + "SensorEventQueue.cpp", + "SensorManager.cpp", + ], + + shared_libs: [ + "libbinder", + "libcutils", + "libutils", + "liblog", + "libhardware", + ], + + export_include_dirs: ["include"], + + export_shared_lib_headers: ["libbinder", "libhardware"], +} + +subdirs = ["tests"] diff --git a/libs/sensor/BitTube.cpp b/libs/sensor/BitTube.cpp new file mode 100644 index 0000000000..93555c8a3a --- /dev/null +++ b/libs/sensor/BitTube.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 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 <sensor/BitTube.h> + +#include <stdint.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <fcntl.h> +#include <unistd.h> + +#include <binder/Parcel.h> + +namespace android { +// ---------------------------------------------------------------------------- + +// Socket buffer size. The default is typically about 128KB, which is much larger than +// we really need. So we make it smaller. +static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024; + + +BitTube::BitTube() + : mSendFd(-1), mReceiveFd(-1) +{ + init(DEFAULT_SOCKET_BUFFER_SIZE, DEFAULT_SOCKET_BUFFER_SIZE); +} + +BitTube::BitTube(size_t bufsize) + : mSendFd(-1), mReceiveFd(-1) +{ + init(bufsize, bufsize); +} + +BitTube::BitTube(const Parcel& data) + : mSendFd(-1), mReceiveFd(-1) +{ + mReceiveFd = dup(data.readFileDescriptor()); + if (mReceiveFd < 0) { + mReceiveFd = -errno; + ALOGE("BitTube(Parcel): can't dup filedescriptor (%s)", + strerror(-mReceiveFd)); + } +} + +BitTube::~BitTube() +{ + if (mSendFd >= 0) + close(mSendFd); + + if (mReceiveFd >= 0) + close(mReceiveFd); +} + +void BitTube::init(size_t rcvbuf, size_t sndbuf) { + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) { + size_t size = DEFAULT_SOCKET_BUFFER_SIZE; + setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); + setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)); + // sine we don't use the "return channel", we keep it small... + setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); + setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); + fcntl(sockets[0], F_SETFL, O_NONBLOCK); + fcntl(sockets[1], F_SETFL, O_NONBLOCK); + mReceiveFd = sockets[0]; + mSendFd = sockets[1]; + } else { + mReceiveFd = -errno; + ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd)); + } +} + +status_t BitTube::initCheck() const +{ + if (mReceiveFd < 0) { + return status_t(mReceiveFd); + } + return NO_ERROR; +} + +int BitTube::getFd() const +{ + return mReceiveFd; +} + +int BitTube::getSendFd() const +{ + return mSendFd; +} + +ssize_t BitTube::write(void const* vaddr, size_t size) +{ + ssize_t err, len; + do { + len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL); + // cannot return less than size, since we're using SOCK_SEQPACKET + err = len < 0 ? errno : 0; + } while (err == EINTR); + return err == 0 ? len : -err; +} + +ssize_t BitTube::read(void* vaddr, size_t size) +{ + ssize_t err, len; + do { + len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT); + err = len < 0 ? errno : 0; + } while (err == EINTR); + if (err == EAGAIN || err == EWOULDBLOCK) { + // EAGAIN means that we have non-blocking I/O but there was + // no data to be read. Nothing the client should care about. + return 0; + } + return err == 0 ? len : -err; +} + +status_t BitTube::writeToParcel(Parcel* reply) const +{ + if (mReceiveFd < 0) + return -EINVAL; + + status_t result = reply->writeDupFileDescriptor(mReceiveFd); + close(mReceiveFd); + mReceiveFd = -1; + return result; +} + + +ssize_t BitTube::sendObjects(const sp<BitTube>& tube, + void const* events, size_t count, size_t objSize) +{ + const char* vaddr = reinterpret_cast<const char*>(events); + ssize_t size = tube->write(vaddr, count*objSize); + + // should never happen because of SOCK_SEQPACKET + LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)), + "BitTube::sendObjects(count=%zu, size=%zu), res=%zd (partial events were sent!)", + count, objSize, size); + + //ALOGE_IF(size<0, "error %d sending %d events", size, count); + return size < 0 ? size : size / static_cast<ssize_t>(objSize); +} + +ssize_t BitTube::recvObjects(const sp<BitTube>& tube, + void* events, size_t count, size_t objSize) +{ + char* vaddr = reinterpret_cast<char*>(events); + ssize_t size = tube->read(vaddr, count*objSize); + + // should never happen because of SOCK_SEQPACKET + LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)), + "BitTube::recvObjects(count=%zu, size=%zu), res=%zd (partial events were received!)", + count, objSize, size); + + //ALOGE_IF(size<0, "error %d receiving %d events", size, count); + return size < 0 ? size : size / static_cast<ssize_t>(objSize); +} + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/gui/ISensorEventConnection.cpp b/libs/sensor/ISensorEventConnection.cpp index 59ecee7501..8a3a623983 100644 --- a/libs/gui/ISensorEventConnection.cpp +++ b/libs/sensor/ISensorEventConnection.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <sensor/ISensorEventConnection.h> + #include <stdint.h> #include <sys/types.h> @@ -24,8 +26,7 @@ #include <binder/Parcel.h> #include <binder/IInterface.h> -#include <gui/ISensorEventConnection.h> -#include <gui/BitTube.h> +#include <sensor/BitTube.h> namespace android { // ---------------------------------------------------------------------------- @@ -34,7 +35,8 @@ enum { GET_SENSOR_CHANNEL = IBinder::FIRST_CALL_TRANSACTION, ENABLE_DISABLE, SET_EVENT_RATE, - FLUSH_SENSOR + FLUSH_SENSOR, + CONFIGURE_CHANNEL }; class BpSensorEventConnection : public BpInterface<ISensorEventConnection> @@ -85,6 +87,15 @@ public: remote()->transact(FLUSH_SENSOR, data, &reply); return reply.readInt32(); } + + virtual int32_t configureChannel(int32_t handle, int32_t rateLevel) { + Parcel data, reply; + data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor()); + data.writeInt32(handle); + data.writeInt32(rateLevel); + remote()->transact(CONFIGURE_CHANNEL, data, &reply); + return reply.readInt32(); + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -131,6 +142,15 @@ status_t BnSensorEventConnection::onTransact( reply->writeInt32(result); return NO_ERROR; } + case CONFIGURE_CHANNEL: { + CHECK_INTERFACE(ISensorEventConnection, data, reply); + int handle = data.readInt32(); + int rateLevel = data.readInt32(); + status_t result = configureChannel(handle, rateLevel); + reply->writeInt32(result); + return NO_ERROR; + } + } return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp index 07c507a082..74186dfcb9 100644 --- a/libs/gui/ISensorServer.cpp +++ b/libs/sensor/ISensorServer.cpp @@ -14,9 +14,12 @@ * limitations under the License. */ +#include <sensor/ISensorServer.h> + #include <stdint.h> #include <sys/types.h> +#include <cutils/native_handle.h> #include <utils/Errors.h> #include <utils/RefBase.h> #include <utils/Vector.h> @@ -25,9 +28,8 @@ #include <binder/Parcel.h> #include <binder/IInterface.h> -#include <gui/Sensor.h> -#include <gui/ISensorServer.h> -#include <gui/ISensorEventConnection.h> +#include <sensor/Sensor.h> +#include <sensor/ISensorEventConnection.h> namespace android { // ---------------------------------------------------------------------------- @@ -37,6 +39,8 @@ enum { CREATE_SENSOR_EVENT_CONNECTION, ENABLE_DATA_INJECTION, GET_DYNAMIC_SENSOR_LIST, + CREATE_SENSOR_DIRECT_CONNECTION, + SET_OPERATION_PARAMETER, }; class BpSensorServer : public BpInterface<ISensorServer> @@ -101,6 +105,36 @@ public: remote()->transact(ENABLE_DATA_INJECTION, data, &reply); return reply.readInt32(); } + + virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName, + uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) { + Parcel data, reply; + data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor()); + data.writeString16(opPackageName); + data.writeUint32(size); + data.writeInt32(type); + data.writeInt32(format); + data.writeNativeHandle(resource); + remote()->transact(CREATE_SENSOR_DIRECT_CONNECTION, data, &reply); + return interface_cast<ISensorEventConnection>(reply.readStrongBinder()); + } + + virtual int setOperationParameter( + int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints) { + Parcel data, reply; + data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor()); + data.writeInt32(type); + data.writeUint32(static_cast<uint32_t>(floats.size())); + for (auto i : floats) { + data.writeFloat(i); + } + data.writeUint32(static_cast<uint32_t>(ints.size())); + for (auto i : ints) { + data.writeInt32(i); + } + remote()->transact(SET_OPERATION_PARAMETER, data, &reply); + return reply.readInt32(); + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -153,6 +187,40 @@ status_t BnSensorServer::onTransact( } return NO_ERROR; } + case CREATE_SENSOR_DIRECT_CONNECTION: { + CHECK_INTERFACE(ISensorServer, data, reply); + const String16& opPackageName = data.readString16(); + uint32_t size = data.readUint32(); + int32_t type = data.readInt32(); + int32_t format = data.readInt32(); + native_handle_t *resource = data.readNativeHandle(); + sp<ISensorEventConnection> ch = + createSensorDirectConnection(opPackageName, size, type, format, resource); + native_handle_close(resource); + native_handle_delete(resource); + reply->writeStrongBinder(IInterface::asBinder(ch)); + return NO_ERROR; + } + case SET_OPERATION_PARAMETER: { + CHECK_INTERFACE(ISensorServer, data, reply); + int32_t type; + Vector<float> floats; + Vector<int32_t> ints; + + type = data.readInt32(); + floats.resize(data.readUint32()); + for (auto &i : floats) { + i = data.readFloat(); + } + ints.resize(data.readUint32()); + for (auto &i : ints) { + i = data.readInt32(); + } + + int32_t ret = setOperationParameter(type, floats, ints); + reply->writeInt32(ret); + return NO_ERROR; + } } return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/Sensor.cpp b/libs/sensor/Sensor.cpp index 2c87562bd8..c2d477e4b5 100644 --- a/libs/gui/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -14,19 +14,13 @@ * limitations under the License. */ +#include <sensor/Sensor.h> + #include <inttypes.h> -#include <stdint.h> -#include <sys/limits.h> -#include <sys/types.h> #include <binder/AppOpsManager.h> +#include <binder/IPermissionController.h> #include <binder/IServiceManager.h> -#include <gui/Sensor.h> -#include <hardware/sensors.h> -#include <log/log.h> -#include <utils/Errors.h> -#include <utils/String8.h> -#include <utils/Flattenable.h> // ---------------------------------------------------------------------------- namespace android { @@ -51,7 +45,7 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi mHandle = hwSensor.handle; mType = hwSensor.type; mMinValue = 0; // FIXME: minValue - mMaxValue = hwSensor.maxRange; // FIXME: maxValue + mMaxValue = hwSensor.maxRange; // FIXME: maxValue mResolution = hwSensor.resolution; mPower = hwSensor.power; mMinDelay = hwSensor.minDelay; @@ -210,6 +204,10 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi mFlags |= SENSOR_FLAG_WAKE_UP; } break; + case SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT: + mStringType = SENSOR_STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT; + mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; + break; case SENSOR_TYPE_WRIST_TILT_GESTURE: mStringType = SENSOR_STRING_TYPE_WRIST_TILT_GESTURE; mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; @@ -246,6 +244,14 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi mStringType = SENSOR_STRING_TYPE_HEART_BEAT; mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; break; + + // TODO: Placeholder for LLOB sensor type + + + case SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED: + mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_UNCALIBRATED; + mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; + break; default: // Only pipe the stringType, requiredPermission and flags for custom sensors. if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) { @@ -292,7 +298,15 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi // Feature flags // Set DYNAMIC_SENSOR_MASK and ADDITIONAL_INFO_MASK flag here. Compatible with HAL 1_3. if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) { - mFlags |= (hwSensor.flags & (DYNAMIC_SENSOR_MASK | ADDITIONAL_INFO_MASK)); + mFlags |= hwSensor.flags & (DYNAMIC_SENSOR_MASK | ADDITIONAL_INFO_MASK); + } + // Set DIRECT_REPORT_MASK and DIRECT_CHANNEL_MASK flags. Compatible with HAL 1_3. + if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) { + // only on continuous sensors direct report mode is defined + if ((mFlags & REPORTING_MODE_MASK) == SENSOR_FLAG_CONTINUOUS_MODE) { + mFlags |= hwSensor.flags + & (SENSOR_FLAG_MASK_DIRECT_REPORT | SENSOR_FLAG_MASK_DIRECT_CHANNEL); + } } // Set DATA_INJECTION flag here. Defined in HAL 1_4. if (halVersion >= SENSORS_DEVICE_API_VERSION_1_4) { @@ -402,6 +416,21 @@ bool Sensor::hasAdditionalInfo() const { return (mFlags & SENSOR_FLAG_ADDITIONAL_INFO) != 0; } +int32_t Sensor::getHighestDirectReportRateLevel() const { + return ((mFlags & SENSOR_FLAG_MASK_DIRECT_REPORT) >> SENSOR_FLAG_SHIFT_DIRECT_REPORT); +} + +bool Sensor::isDirectChannelTypeSupported(int32_t sharedMemType) const { + switch (sharedMemType) { + case SENSOR_DIRECT_MEM_TYPE_ASHMEM: + return mFlags & SENSOR_FLAG_DIRECT_CHANNEL_ASHMEM; + case SENSOR_DIRECT_MEM_TYPE_GRALLOC: + return mFlags & SENSOR_FLAG_DIRECT_CHANNEL_GRALLOC; + default: + return false; + } +} + int32_t Sensor::getReportingMode() const { return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT); } diff --git a/libs/gui/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp index 6d698390c1..6f68fb5f7b 100644 --- a/libs/gui/SensorEventQueue.cpp +++ b/libs/sensor/SensorEventQueue.cpp @@ -16,20 +16,17 @@ #define LOG_TAG "Sensors" +#include <sensor/SensorEventQueue.h> + #include <algorithm> -#include <stdint.h> -#include <sys/types.h> #include <sys/socket.h> -#include <linux/errno.h> -#include <utils/Errors.h> #include <utils/RefBase.h> #include <utils/Looper.h> -#include <gui/Sensor.h> -#include <gui/BitTube.h> -#include <gui/SensorEventQueue.h> -#include <gui/ISensorEventConnection.h> +#include <sensor/Sensor.h> +#include <sensor/BitTube.h> +#include <sensor/ISensorEventConnection.h> #include <android/sensor.h> @@ -138,7 +135,7 @@ status_t SensorEventQueue::disableSensor(Sensor const* sensor) const { } status_t SensorEventQueue::enableSensor(int32_t handle, int32_t samplingPeriodUs, - int maxBatchReportLatencyUs, int reservedFlags) const { + int64_t maxBatchReportLatencyUs, int reservedFlags) const { return mSensorEventConnection->enableDisable(handle, true, us2ns(samplingPeriodUs), us2ns(maxBatchReportLatencyUs), reservedFlags); } diff --git a/libs/gui/SensorManager.cpp b/libs/sensor/SensorManager.cpp index 57c3073bf4..3fbc5ebba8 100644 --- a/libs/gui/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -16,9 +16,12 @@ #define LOG_TAG "Sensors" +#include <sensor/SensorManager.h> + #include <stdint.h> #include <sys/types.h> +#include <cutils/native_handle.h> #include <utils/Errors.h> #include <utils/RefBase.h> #include <utils/Singleton.h> @@ -26,24 +29,24 @@ #include <binder/IBinder.h> #include <binder/IServiceManager.h> -#include <gui/ISensorServer.h> -#include <gui/ISensorEventConnection.h> -#include <gui/Sensor.h> -#include <gui/SensorManager.h> -#include <gui/SensorEventQueue.h> +#include <sensor/ISensorServer.h> +#include <sensor/ISensorEventConnection.h> +#include <sensor/Sensor.h> +#include <sensor/SensorEventQueue.h> // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- -android::Mutex android::SensorManager::sLock; -std::map<String16, SensorManager*> android::SensorManager::sPackageInstances; +Mutex SensorManager::sLock; +std::map<String16, SensorManager*> SensorManager::sPackageInstances; SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) { + waitForSensorService(nullptr); + Mutex::Autolock _l(sLock); SensorManager* sensorManager; - std::map<String16, SensorManager*>::iterator iterator = - sPackageInstances.find(packageName); + auto iterator = sPackageInstances.find(packageName); if (iterator != sPackageInstances.end()) { sensorManager = iterator->second; @@ -89,7 +92,7 @@ SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) } SensorManager::SensorManager(const String16& opPackageName) - : mSensorList(0), mOpPackageName(opPackageName) { + : mSensorList(0), mOpPackageName(opPackageName), mDirectConnectionHandle(1) { // okay we're not locked here, but it's not needed during construction assertStateLocked(); } @@ -98,6 +101,28 @@ SensorManager::~SensorManager() { free(mSensorList); } +status_t SensorManager::waitForSensorService(sp<ISensorServer> *server) { + // try for 300 seconds (60*5(getService() tries for 5 seconds)) before giving up ... + sp<ISensorServer> s; + const String16 name("sensorservice"); + for (int i = 0; i < 60; i++) { + status_t err = getService(name, &s); + switch (err) { + case NAME_NOT_FOUND: + sleep(1); + continue; + case NO_ERROR: + if (server != nullptr) { + *server = s; + } + return NO_ERROR; + default: + return err; + } + } + return TIMED_OUT; +} + void SensorManager::sensorManagerDied() { Mutex::Autolock _l(mLock); mSensorServer.clear(); @@ -118,19 +143,8 @@ status_t SensorManager::assertStateLocked() { } } if (initSensorManager) { - // try for 300 seconds (60*5(getService() tries for 5 seconds)) before giving up ... - const String16 name("sensorservice"); - for (int i = 0; i < 60; i++) { - status_t err = getService(name, &mSensorServer); - if (err == NAME_NOT_FOUND) { - sleep(1); - continue; - } - if (err != NO_ERROR) { - return err; - } - break; - } + waitForSensorService(&mSensorServer); + LOG_ALWAYS_FATAL_IF(mSensorServer == nullptr, "getService(SensorService) NULL"); class DeathObserver : public IBinder::DeathRecipient { SensorManager& mSensorManager; @@ -142,8 +156,6 @@ status_t SensorManager::assertStateLocked() { explicit DeathObserver(SensorManager& mgr) : mSensorManager(mgr) { } }; - LOG_ALWAYS_FATAL_IF(mSensorServer.get() == NULL, "getService(SensorService) NULL"); - mDeathObserver = new DeathObserver(*const_cast<SensorManager *>(this)); IInterface::asBinder(mSensorServer)->linkToDeath(mDeathObserver); @@ -195,7 +207,8 @@ Sensor const* SensorManager::getDefaultSensor(int type) if (type == SENSOR_TYPE_PROXIMITY || type == SENSOR_TYPE_SIGNIFICANT_MOTION || type == SENSOR_TYPE_TILT_DETECTOR || type == SENSOR_TYPE_WAKE_GESTURE || type == SENSOR_TYPE_GLANCE_GESTURE || type == SENSOR_TYPE_PICK_UP_GESTURE || - type == SENSOR_TYPE_WRIST_TILT_GESTURE) { + type == SENSOR_TYPE_WRIST_TILT_GESTURE || + type == SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT) { wakeUpSensor = true; } // For now we just return the first sensor of that type we find. @@ -237,5 +250,68 @@ bool SensorManager::isDataInjectionEnabled() { return false; } +int SensorManager::createDirectChannel( + size_t size, int channelType, const native_handle_t *resourceHandle) { + Mutex::Autolock _l(mLock); + if (assertStateLocked() != NO_ERROR) { + return NO_INIT; + } + + if (channelType != SENSOR_DIRECT_MEM_TYPE_ASHMEM + && channelType != SENSOR_DIRECT_MEM_TYPE_GRALLOC) { + ALOGE("Bad channel shared memory type %d", channelType); + return BAD_VALUE; + } + + sp<ISensorEventConnection> conn = + mSensorServer->createSensorDirectConnection(mOpPackageName, + static_cast<uint32_t>(size), + static_cast<int32_t>(channelType), + SENSOR_DIRECT_FMT_SENSORS_EVENT, resourceHandle); + if (conn == nullptr) { + return NO_MEMORY; + } + + int nativeHandle = mDirectConnectionHandle++; + mDirectConnection.emplace(nativeHandle, conn); + return nativeHandle; +} + +void SensorManager::destroyDirectChannel(int channelNativeHandle) { + Mutex::Autolock _l(mLock); + if (assertStateLocked() == NO_ERROR) { + mDirectConnection.erase(channelNativeHandle); + } +} + +int SensorManager::configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel) { + Mutex::Autolock _l(mLock); + if (assertStateLocked() != NO_ERROR) { + return NO_INIT; + } + + auto i = mDirectConnection.find(channelNativeHandle); + if (i == mDirectConnection.end()) { + ALOGE("Cannot find the handle in client direct connection table"); + return BAD_VALUE; + } + + int ret; + ret = i->second->configureChannel(sensorHandle, rateLevel); + ALOGE_IF(ret < 0, "SensorManager::configureChannel (%d, %d) returns %d", + static_cast<int>(sensorHandle), static_cast<int>(rateLevel), + static_cast<int>(ret)); + return ret; +} + +int SensorManager::setOperationParameter( + int type, const Vector<float> &floats, const Vector<int32_t> &ints) { + Mutex::Autolock _l(mLock); + if (assertStateLocked() != NO_ERROR) { + return NO_INIT; + } + return mSensorServer->setOperationParameter(type, floats, ints); +} + // ---------------------------------------------------------------------------- }; // namespace android diff --git a/libs/sensor/include/sensor/BitTube.h b/libs/sensor/include/sensor/BitTube.h new file mode 100644 index 0000000000..c1fabe83ed --- /dev/null +++ b/libs/sensor/include/sensor/BitTube.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 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. + */ + +#pragma once + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> + +namespace android { +// ---------------------------------------------------------------------------- +class Parcel; + +class BitTube : public RefBase +{ +public: + + // creates a BitTube with a default (4KB) send buffer + BitTube(); + + // creates a BitTube with a a specified send and receive buffer size + explicit BitTube(size_t bufsize); + + explicit BitTube(const Parcel& data); + virtual ~BitTube(); + + // check state after construction + status_t initCheck() const; + + // get receive file-descriptor + int getFd() const; + + // get the send file-descriptor. + int getSendFd() const; + + // send objects (sized blobs). All objects are guaranteed to be written or the call fails. + template <typename T> + static ssize_t sendObjects(const sp<BitTube>& tube, + T const* events, size_t count) { + return sendObjects(tube, events, count, sizeof(T)); + } + + // receive objects (sized blobs). If the receiving buffer isn't large enough, + // excess messages are silently discarded. + template <typename T> + static ssize_t recvObjects(const sp<BitTube>& tube, + T* events, size_t count) { + return recvObjects(tube, events, count, sizeof(T)); + } + + // parcels this BitTube + status_t writeToParcel(Parcel* reply) const; + +private: + void init(size_t rcvbuf, size_t sndbuf); + + // send a message. The write is guaranteed to send the whole message or fail. + ssize_t write(void const* vaddr, size_t size); + + // receive a message. the passed buffer must be at least as large as the + // write call used to send the message, excess data is silently discarded. + ssize_t read(void* vaddr, size_t size); + + int mSendFd; + mutable int mReceiveFd; + + static ssize_t sendObjects(const sp<BitTube>& tube, + void const* events, size_t count, size_t objSize); + + static ssize_t recvObjects(const sp<BitTube>& tube, + void* events, size_t count, size_t objSize); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/sensor/include/sensor/ISensorEventConnection.h b/libs/sensor/include/sensor/ISensorEventConnection.h new file mode 100644 index 0000000000..07cc7e84ad --- /dev/null +++ b/libs/sensor/include/sensor/ISensorEventConnection.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 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. + */ + +#pragma once + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/StrongPointer.h> +#include <utils/Timers.h> + +#include <binder/IInterface.h> + +namespace android { +// ---------------------------------------------------------------------------- + +class BitTube; +class Parcel; + +class ISensorEventConnection : public IInterface +{ +public: + DECLARE_META_INTERFACE(SensorEventConnection) + + virtual sp<BitTube> getSensorChannel() const = 0; + virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs, + nsecs_t maxBatchReportLatencyNs, int reservedFlags) = 0; + virtual status_t setEventRate(int handle, nsecs_t ns) = 0; + virtual status_t flush() = 0; + virtual int32_t configureChannel(int32_t handle, int32_t rateLevel) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnSensorEventConnection : public BnInterface<ISensorEventConnection> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h new file mode 100644 index 0000000000..8d5006258f --- /dev/null +++ b/libs/sensor/include/sensor/ISensorServer.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 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. + */ + +#pragma once + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/StrongPointer.h> +#include <utils/Vector.h> + +#include <binder/IInterface.h> + +struct native_handle; +typedef struct native_handle native_handle_t; +namespace android { +// ---------------------------------------------------------------------------- + +class ISensorEventConnection; +class Parcel; +class Sensor; +class String8; +class String16; + +class ISensorServer : public IInterface +{ +public: + DECLARE_META_INTERFACE(SensorServer) + + virtual Vector<Sensor> getSensorList(const String16& opPackageName) = 0; + virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName) = 0; + + virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName, + int mode, const String16& opPackageName) = 0; + virtual int32_t isDataInjectionEnabled() = 0; + + virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName, + uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) = 0; + + virtual int setOperationParameter( + int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnSensorServer : public BnInterface<ISensorServer> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/sensor/include/sensor/Sensor.h b/libs/sensor/include/sensor/Sensor.h new file mode 100644 index 0000000000..043e6352a7 --- /dev/null +++ b/libs/sensor/include/sensor/Sensor.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 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. + */ + +#pragma once + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Flattenable.h> +#include <utils/String8.h> +#include <utils/Timers.h> + +// FIXME: including from android/ breaks layering, as libandroid ultimately depends on libsensors +#include <android/sensor.h> + +#include <hardware/sensors.h> + +// ---------------------------------------------------------------------------- +// Concrete types for the NDK +struct ASensor { }; + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +class Parcel; + +// ---------------------------------------------------------------------------- + +class Sensor : public ASensor, public LightFlattenable<Sensor> +{ +public: + enum { + TYPE_ACCELEROMETER = ASENSOR_TYPE_ACCELEROMETER, + TYPE_MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD, + TYPE_GYROSCOPE = ASENSOR_TYPE_GYROSCOPE, + TYPE_LIGHT = ASENSOR_TYPE_LIGHT, + TYPE_PROXIMITY = ASENSOR_TYPE_PROXIMITY + }; + + struct uuid_t{ + union { + uint8_t b[16]; + int64_t i64[2]; + }; + uuid_t(const uint8_t (&uuid)[16]) { memcpy(b, uuid, sizeof(b));} + uuid_t() : b{0} {} + }; + + Sensor(const Sensor&) = default; + Sensor& operator=(const Sensor&) = default; + + Sensor(const char * name = ""); + Sensor(struct sensor_t const* hwSensor, int halVersion = 0); + Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion = 0); + ~Sensor(); + + const String8& getName() const; + const String8& getVendor() const; + int32_t getHandle() const; + int32_t getType() const; + float getMinValue() const; + float getMaxValue() const; + float getResolution() const; + float getPowerUsage() const; + int32_t getMinDelay() const; + nsecs_t getMinDelayNs() const; + int32_t getVersion() const; + uint32_t getFifoReservedEventCount() const; + uint32_t getFifoMaxEventCount() const; + const String8& getStringType() const; + const String8& getRequiredPermission() const; + bool isRequiredPermissionRuntime() const; + int32_t getRequiredAppOp() const; + int32_t getMaxDelay() const; + uint32_t getFlags() const; + bool isWakeUpSensor() const; + bool isDynamicSensor() const; + bool hasAdditionalInfo() const; + int32_t getHighestDirectReportRateLevel() const; + bool isDirectChannelTypeSupported(int32_t sharedMemType) const; + int32_t getReportingMode() const; + + // Note that after setId() has been called, getUuid() no longer + // returns the UUID. + // TODO(b/29547335): Remove getUuid(), add getUuidIndex(), and + // make sure setId() doesn't change the UuidIndex. + const uuid_t& getUuid() const; + int32_t getId() const; + void setId(int32_t id); + + // LightFlattenable protocol + inline 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); + +private: + String8 mName; + String8 mVendor; + int32_t mHandle; + int32_t mType; + float mMinValue; + float mMaxValue; + float mResolution; + float mPower; + int32_t mMinDelay; + int32_t mVersion; + uint32_t mFifoReservedEventCount; + uint32_t mFifoMaxEventCount; + String8 mStringType; + String8 mRequiredPermission; + bool mRequiredPermissionRuntime = false; + int32_t mRequiredAppOp; + int32_t mMaxDelay; + uint32_t mFlags; + // TODO(b/29547335): Get rid of this field and replace with an index. + // The index will be into a separate global vector of UUIDs. + // Also add an mId field (and change flatten/unflatten appropriately). + uuid_t mUuid; + static void flattenString8(void*& buffer, size_t& size, const String8& string8); + static bool unflattenString8(void const*& buffer, size_t& size, String8& outputString8); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h new file mode 100644 index 0000000000..baed2ee20d --- /dev/null +++ b/libs/sensor/include/sensor/SensorEventQueue.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 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. + */ + +#pragma once + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include <utils/Timers.h> +#include <utils/Mutex.h> + +#include <sensor/BitTube.h> + +// ---------------------------------------------------------------------------- +#define WAKE_UP_SENSOR_EVENT_NEEDS_ACK (1U << 31) +struct ALooper; +struct ASensorEvent; + +// Concrete types for the NDK +struct ASensorEventQueue { + ALooper* looper; +}; + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +class ISensorEventConnection; +class Sensor; +class Looper; + +// ---------------------------------------------------------------------------- + +class SensorEventQueue : public ASensorEventQueue, public RefBase +{ +public: + + enum { MAX_RECEIVE_BUFFER_EVENT_COUNT = 256 }; + + /** + * Typical sensor delay (sample period) in microseconds. + */ + // Fastest sampling, system will bound it to minDelay + static constexpr int32_t SENSOR_DELAY_FASTEST = 0; + // Typical sample period for game, 50Hz; + static constexpr int32_t SENSOR_DELAY_GAME = 20000; + // Typical sample period for UI, 15Hz + static constexpr int32_t SENSOR_DELAY_UI = 66667; + // Default sensor sample period + static constexpr int32_t SENSOR_DELAY_NORMAL = 200000; + + SensorEventQueue(const sp<ISensorEventConnection>& connection); + virtual ~SensorEventQueue(); + virtual void onFirstRef(); + + int getFd() const; + + static ssize_t write(const sp<BitTube>& tube, + ASensorEvent const* events, size_t numEvents); + + ssize_t read(ASensorEvent* events, size_t numEvents); + + status_t waitForEvent() const; + status_t wake() const; + + status_t enableSensor(Sensor const* sensor) const; + status_t enableSensor(Sensor const* sensor, int32_t samplingPeriodUs) const; + status_t disableSensor(Sensor const* sensor) const; + status_t setEventRate(Sensor const* sensor, nsecs_t ns) const; + + // these are here only to support SensorManager.java and HIDL Frameworks SensorManager. + status_t enableSensor(int32_t handle, int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs, + int reservedFlags) const; + status_t disableSensor(int32_t handle) const; + status_t flush() const; + // Send an ack for every wake_up sensor event that is set to WAKE_UP_SENSOR_EVENT_NEEDS_ACK. + void sendAck(const ASensorEvent* events, int count); + + status_t injectSensorEvent(const ASensorEvent& event); +private: + sp<Looper> getLooper() const; + sp<ISensorEventConnection> mSensorEventConnection; + sp<BitTube> mSensorChannel; + mutable Mutex mLock; + mutable sp<Looper> mLooper; + ASensorEvent* mRecBuffer; + size_t mAvailable; + size_t mConsumed; + uint32_t mNumAcksToSend; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h new file mode 100644 index 0000000000..5fc85d329b --- /dev/null +++ b/libs/sensor/include/sensor/SensorManager.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 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. + */ + +#ifndef ANDROID_GUI_SENSOR_MANAGER_H +#define ANDROID_GUI_SENSOR_MANAGER_H + +#include <map> +#include <unordered_map> + +#include <stdint.h> +#include <sys/types.h> + +#include <binder/IBinder.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> + +#include <utils/Errors.h> +#include <utils/StrongPointer.h> +#include <utils/Vector.h> +#include <utils/String8.h> + +#include <sensor/SensorEventQueue.h> + +// ---------------------------------------------------------------------------- +// Concrete types for the NDK +struct ASensorManager { }; + +struct native_handle; +typedef struct native_handle native_handle_t; + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +class ISensorServer; +class Sensor; +class SensorEventQueue; +// ---------------------------------------------------------------------------- + +class SensorManager : public ASensorManager +{ +public: + static SensorManager& getInstanceForPackage(const String16& packageName); + ~SensorManager(); + + ssize_t getSensorList(Sensor const* const** list); + ssize_t getDynamicSensorList(Vector<Sensor>& list); + Sensor const* getDefaultSensor(int type); + sp<SensorEventQueue> createEventQueue(String8 packageName = String8(""), int mode = 0); + bool isDataInjectionEnabled(); + int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData); + void destroyDirectChannel(int channelNativeHandle); + int configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel); + int setOperationParameter(int type, const Vector<float> &floats, const Vector<int32_t> &ints); + +private: + // DeathRecipient interface + void sensorManagerDied(); + static status_t waitForSensorService(sp<ISensorServer> *server); + + SensorManager(const String16& opPackageName); + status_t assertStateLocked(); + +private: + static Mutex sLock; + static std::map<String16, SensorManager*> sPackageInstances; + + Mutex mLock; + sp<ISensorServer> mSensorServer; + Sensor const** mSensorList; + Vector<Sensor> mSensors; + sp<IBinder::DeathRecipient> mDeathObserver; + const String16 mOpPackageName; + std::unordered_map<int, sp<ISensorEventConnection>> mDirectConnection; + int32_t mDirectConnectionHandle; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_SENSOR_MANAGER_H diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp new file mode 100644 index 0000000000..9d530fc5fa --- /dev/null +++ b/libs/sensor/tests/Android.bp @@ -0,0 +1,29 @@ +// Copyright 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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: "libsensor_test", + + clang: true, + + srcs: [ + "Sensor_test.cpp", + ], + + shared_libs: [ + "liblog", + "libsensor", + "libutils", + ], +} diff --git a/libs/gui/tests/Sensor_test.cpp b/libs/sensor/tests/Sensor_test.cpp index fbf282d1cf..ede20c93f4 100644 --- a/libs/gui/tests/Sensor_test.cpp +++ b/libs/sensor/tests/Sensor_test.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "Sensor_test" -#include <gui/Sensor.h> +#include <sensor/Sensor.h> #include <hardware/sensors.h> #include <utils/Errors.h> diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index d5ff75325b..5edd664345 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -14,6 +14,7 @@ cc_library_shared { name: "libui", + vendor_available: true, clang: true, cppflags: [ @@ -28,6 +29,9 @@ cc_library_shared { // We only care about compiling as C++14 "-Wno-c++98-compat-pedantic", + // We are aware of the risks inherent in comparing floats for equality + "-Wno-float-equal", + // We use four-character constants for the GraphicBuffer header, and don't care // that they're non-portable as long as they're consistent within one execution "-Wno-four-char-constants", @@ -41,13 +45,16 @@ cc_library_shared { }, srcs: [ + "ColorSpace.cpp", + "DebugUtils.cpp", "Fence.cpp", + "FenceTime.cpp", "FrameStats.cpp", - "Gralloc1.cpp", - "Gralloc1On0Adapter.cpp", + "Gralloc2.cpp", "GraphicBuffer.cpp", "GraphicBufferAllocator.cpp", "GraphicBufferMapper.cpp", + "GraphicsEnv.cpp", "HdrCapabilities.cpp", "PixelFormat.cpp", "Rect.cpp", @@ -55,14 +62,37 @@ cc_library_shared { "UiConfig.cpp", ], + include_dirs: [ + "frameworks/native/include", + ], + shared_libs: [ - "libbinder", + "android.hardware.graphics.allocator@2.0", + "android.hardware.graphics.mapper@2.0", + "android.hardware.configstore@1.0", + "android.hardware.configstore-utils", + "libbase", + "libnativeloader", "libcutils", "libhardware", + "libhidlbase", + "libhidltransport", + "libhwbinder", "libsync", "libutils", "liblog", ], + + static_libs: [ + "libarect", + "libgrallocusage", + "libmath", + ], + + export_static_lib_headers: [ + "libarect", + "libmath", + ], } subdirs = ["tests"] diff --git a/libs/ui/ColorSpace.cpp b/libs/ui/ColorSpace.cpp new file mode 100644 index 0000000000..5b4bf2353e --- /dev/null +++ b/libs/ui/ColorSpace.cpp @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/ColorSpace.h> + +using namespace std::placeholders; + +namespace android { + +static constexpr float linearResponse(float v) { + return v; +} + +static constexpr float rcpResponse(float x, const ColorSpace::TransferParameters& p) { + return x >= p.d * p.c ? (std::pow(x, 1.0f / p.g) - p.b) / p.a : x / p.c; +} + +static constexpr float response(float x, const ColorSpace::TransferParameters& p) { + return x >= p.d ? std::pow(p.a * x + p.b, p.g) : p.c * x; +} + +static constexpr float rcpFullResponse(float x, const ColorSpace::TransferParameters& p) { + return x >= p.d * p.c ? (std::pow(x - p.e, 1.0f / p.g) - p.b) / p.a : (x - p.f) / p.c; +} + +static constexpr float fullResponse(float x, const ColorSpace::TransferParameters& p) { + return x >= p.d ? std::pow(p.a * x + p.b, p.g) + p.e : p.c * x + p.f; +} + +static float absRcpResponse(float x, float g,float a, float b, float c, float d) { + float xx = std::abs(x); + return std::copysign(xx >= d * c ? (std::pow(xx, 1.0f / g) - b) / a : xx / c, x); +} + +static float absResponse(float x, float g, float a, float b, float c, float d) { + float xx = std::abs(x); + return std::copysign(xx >= d ? std::pow(a * xx + b, g) : c * xx, x); +} + +static float safePow(float x, float e) { + return powf(x < 0.0f ? 0.0f : x, e); +} + +static ColorSpace::transfer_function toOETF(const ColorSpace::TransferParameters& parameters) { + if (parameters.e == 0.0f && parameters.f == 0.0f) { + return std::bind(rcpResponse, _1, parameters); + } + return std::bind(rcpFullResponse, _1, parameters); +} + +static ColorSpace::transfer_function toEOTF( const ColorSpace::TransferParameters& parameters) { + if (parameters.e == 0.0f && parameters.f == 0.0f) { + return std::bind(response, _1, parameters); + } + return std::bind(fullResponse, _1, parameters); +} + +static ColorSpace::transfer_function toOETF(float gamma) { + if (gamma == 1.0f) { + return linearResponse; + } + return std::bind(safePow, _1, 1.0f / gamma); +} + +static ColorSpace::transfer_function toEOTF(float gamma) { + if (gamma == 1.0f) { + return linearResponse; + } + return std::bind(safePow, _1, gamma); +} + +static constexpr std::array<float2, 3> computePrimaries(const mat3& rgbToXYZ) { + float3 r(rgbToXYZ * float3{1, 0, 0}); + float3 g(rgbToXYZ * float3{0, 1, 0}); + float3 b(rgbToXYZ * float3{0, 0, 1}); + + return {{r.xy / dot(r, float3{1}), + g.xy / dot(g, float3{1}), + b.xy / dot(b, float3{1})}}; +} + +static constexpr float2 computeWhitePoint(const mat3& rgbToXYZ) { + float3 w(rgbToXYZ * float3{1}); + return w.xy / dot(w, float3{1}); +} + +ColorSpace::ColorSpace( + const std::string& name, + const mat3& rgbToXYZ, + transfer_function OETF, + transfer_function EOTF, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(rgbToXYZ) + , mXYZtoRGB(inverse(rgbToXYZ)) + , mOETF(std::move(OETF)) + , mEOTF(std::move(EOTF)) + , mClamper(std::move(clamper)) + , mPrimaries(computePrimaries(rgbToXYZ)) + , mWhitePoint(computeWhitePoint(rgbToXYZ)) { +} + +ColorSpace::ColorSpace( + const std::string& name, + const mat3& rgbToXYZ, + const TransferParameters parameters, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(rgbToXYZ) + , mXYZtoRGB(inverse(rgbToXYZ)) + , mParameters(parameters) + , mOETF(toOETF(mParameters)) + , mEOTF(toEOTF(mParameters)) + , mClamper(std::move(clamper)) + , mPrimaries(computePrimaries(rgbToXYZ)) + , mWhitePoint(computeWhitePoint(rgbToXYZ)) { +} + +ColorSpace::ColorSpace( + const std::string& name, + const mat3& rgbToXYZ, + float gamma, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(rgbToXYZ) + , mXYZtoRGB(inverse(rgbToXYZ)) + , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}) + , mOETF(toOETF(gamma)) + , mEOTF(toEOTF(gamma)) + , mClamper(std::move(clamper)) + , mPrimaries(computePrimaries(rgbToXYZ)) + , mWhitePoint(computeWhitePoint(rgbToXYZ)) { +} + +ColorSpace::ColorSpace( + const std::string& name, + const std::array<float2, 3>& primaries, + const float2& whitePoint, + transfer_function OETF, + transfer_function EOTF, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) + , mXYZtoRGB(inverse(mRGBtoXYZ)) + , mOETF(std::move(OETF)) + , mEOTF(std::move(EOTF)) + , mClamper(std::move(clamper)) + , mPrimaries(primaries) + , mWhitePoint(whitePoint) { +} + +ColorSpace::ColorSpace( + const std::string& name, + const std::array<float2, 3>& primaries, + const float2& whitePoint, + const TransferParameters parameters, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) + , mXYZtoRGB(inverse(mRGBtoXYZ)) + , mParameters(parameters) + , mOETF(toOETF(mParameters)) + , mEOTF(toEOTF(mParameters)) + , mClamper(std::move(clamper)) + , mPrimaries(primaries) + , mWhitePoint(whitePoint) { +} + +ColorSpace::ColorSpace( + const std::string& name, + const std::array<float2, 3>& primaries, + const float2& whitePoint, + float gamma, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) + , mXYZtoRGB(inverse(mRGBtoXYZ)) + , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}) + , mOETF(toOETF(gamma)) + , mEOTF(toEOTF(gamma)) + , mClamper(std::move(clamper)) + , mPrimaries(primaries) + , mWhitePoint(whitePoint) { +} + +constexpr mat3 ColorSpace::computeXYZMatrix( + const std::array<float2, 3>& primaries, const float2& whitePoint) { + const float2& R = primaries[0]; + const float2& G = primaries[1]; + const float2& B = primaries[2]; + const float2& W = whitePoint; + + float oneRxRy = (1 - R.x) / R.y; + float oneGxGy = (1 - G.x) / G.y; + float oneBxBy = (1 - B.x) / B.y; + float oneWxWy = (1 - W.x) / W.y; + + float RxRy = R.x / R.y; + float GxGy = G.x / G.y; + float BxBy = B.x / B.y; + float WxWy = W.x / W.y; + + float BY = + ((oneWxWy - oneRxRy) * (GxGy - RxRy) - (WxWy - RxRy) * (oneGxGy - oneRxRy)) / + ((oneBxBy - oneRxRy) * (GxGy - RxRy) - (BxBy - RxRy) * (oneGxGy - oneRxRy)); + float GY = (WxWy - RxRy - BY * (BxBy - RxRy)) / (GxGy - RxRy); + float RY = 1 - GY - BY; + + float RYRy = RY / R.y; + float GYGy = GY / G.y; + float BYBy = BY / B.y; + + return { + float3{RYRy * R.x, RY, RYRy * (1 - R.x - R.y)}, + float3{GYGy * G.x, GY, GYGy * (1 - G.x - G.y)}, + float3{BYBy * B.x, BY, BYBy * (1 - B.x - B.y)} + }; +} + +const ColorSpace ColorSpace::sRGB() { + return { + "sRGB IEC61966-2.1", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f}, + {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::linearSRGB() { + return { + "sRGB IEC61966-2.1 (Linear)", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f} + }; +} + +const ColorSpace ColorSpace::extendedSRGB() { + return { + "scRGB-nl IEC 61966-2-2:2003", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f}, + std::bind(absRcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f), + std::bind(absResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f), + std::bind(clamp<float>, _1, -0.799f, 2.399f) + }; +} + +const ColorSpace ColorSpace::linearExtendedSRGB() { + return { + "scRGB IEC 61966-2-2:2003", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f}, + 1.0f, + std::bind(clamp<float>, _1, -0.5f, 7.499f) + }; +} + +const ColorSpace ColorSpace::NTSC() { + return { + "NTSC (1953)", + {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}}, + {0.310f, 0.316f}, + {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::BT709() { + return { + "Rec. ITU-R BT.709-5", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f}, + {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::BT2020() { + return { + "Rec. ITU-R BT.2020-1", + {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}}, + {0.3127f, 0.3290f}, + {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::AdobeRGB() { + return { + "Adobe RGB (1998)", + {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}}, + {0.3127f, 0.3290f}, + 2.2f + }; +} + +const ColorSpace ColorSpace::ProPhotoRGB() { + return { + "ROMM RGB ISO 22028-2:2013", + {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}}, + {0.34567f, 0.35850f}, + {1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::DisplayP3() { + return { + "Display P3", + {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f}, + {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::DCIP3() { + return { + "SMPTE RP 431-2-2007 DCI (P3)", + {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, + {0.314f, 0.351f}, + 2.6f + }; +} + +const ColorSpace ColorSpace::ACES() { + return { + "SMPTE ST 2065-1:2012 ACES", + {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}}, + {0.32168f, 0.33767f}, + 1.0f, + std::bind(clamp<float>, _1, -65504.0f, 65504.0f) + }; +} + +const ColorSpace ColorSpace::ACEScg() { + return { + "Academy S-2014-004 ACEScg", + {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}}, + {0.32168f, 0.33767f}, + 1.0f, + std::bind(clamp<float>, _1, -65504.0f, 65504.0f) + }; +} + +std::unique_ptr<float3> ColorSpace::createLUT(uint32_t size, + const ColorSpace& src, const ColorSpace& dst) { + + size = clamp(size, 2u, 256u); + float m = 1.0f / float(size - 1); + + std::unique_ptr<float3> lut(new float3[size * size * size]); + float3* data = lut.get(); + + ColorSpaceConnector connector(src, dst); + + for (uint32_t z = 0; z < size; z++) { + for (int32_t y = int32_t(size - 1); y >= 0; y--) { + for (uint32_t x = 0; x < size; x++) { + *data++ = connector.transform({x * m, y * m, z * m}); + } + } + } + + return lut; +} + +static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f}; +static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f}; +static const mat3 BRADFORD = mat3{ + float3{ 0.8951f, -0.7502f, 0.0389f}, + float3{ 0.2664f, 1.7135f, -0.0685f}, + float3{-0.1614f, 0.0367f, 1.0296f} +}; + +static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) { + float3 srcLMS = matrix * srcWhitePoint; + float3 dstLMS = matrix * dstWhitePoint; + return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix; +} + +ColorSpaceConnector::ColorSpaceConnector( + const ColorSpace& src, + const ColorSpace& dst) noexcept + : mSource(src) + , mDestination(dst) { + + if (all(lessThan(abs(src.getWhitePoint() - dst.getWhitePoint()), float2{1e-3f}))) { + mTransform = dst.getXYZtoRGB() * src.getRGBtoXYZ(); + } else { + mat3 rgbToXYZ(src.getRGBtoXYZ()); + mat3 xyzToRGB(dst.getXYZtoRGB()); + + float3 srcXYZ = ColorSpace::XYZ(float3{src.getWhitePoint(), 1}); + float3 dstXYZ = ColorSpace::XYZ(float3{dst.getWhitePoint(), 1}); + + if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { + rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ(); + } + + if (any(greaterThan(abs(dst.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { + xyzToRGB = inverse(adaptation(BRADFORD, dstXYZ, ILLUMINANT_D50_XYZ) * dst.getRGBtoXYZ()); + } + + mTransform = xyzToRGB * rgbToXYZ; + } +} + +}; // namespace android diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp new file mode 100644 index 0000000000..882bd7c7b2 --- /dev/null +++ b/libs/ui/DebugUtils.cpp @@ -0,0 +1,187 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/DebugUtils.h> + +#include <android-base/stringprintf.h> +#include <string> + +std::string decodeStandard(android_dataspace dataspace) { + const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK); + switch (dataspaceSelect) { + case HAL_DATASPACE_STANDARD_BT709: + return std::string("BT709"); + + case HAL_DATASPACE_STANDARD_BT601_625: + return std::string("BT601_625"); + + case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED: + return std::string("BT601_625_UNADJUSTED"); + + case HAL_DATASPACE_STANDARD_BT601_525: + return std::string("BT601_525"); + + case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED: + return std::string("BT601_525_UNADJUSTED"); + + case HAL_DATASPACE_STANDARD_BT2020: + return std::string("BT2020"); + + case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE: + return std::string("BT2020 (constant luminance)"); + + case HAL_DATASPACE_STANDARD_BT470M: + return std::string("BT470M"); + + case HAL_DATASPACE_STANDARD_FILM: + return std::string("FILM"); + + case HAL_DATASPACE_STANDARD_DCI_P3: + return std::string("DCI-P3"); + + case HAL_DATASPACE_STANDARD_ADOBE_RGB: + return std::string("AdobeRGB"); + + case 0: + switch (dataspace & 0xffff) { + case HAL_DATASPACE_JFIF: + return std::string("(deprecated) JFIF (BT601_625, SMPTE_170M Full range)"); + + case HAL_DATASPACE_BT601_625: + return std::string("(deprecated) BT601_625 (BT601_625, SMPTE_170M Limited " + "range)"); + + case HAL_DATASPACE_BT601_525: + return std::string("(deprecated) BT601_525 (BT601_525, SMPTE_170M Limited " + "range)"); + + case HAL_DATASPACE_SRGB_LINEAR: + return std::string("(deprecated) SRGB Linear Full range"); + + case HAL_DATASPACE_SRGB: + return std::string("(deprecated) sRGB"); + + case HAL_DATASPACE_V0_BT709: + return std::string("(deprecated) BT709 (BT709, SMPTE_170M Limited range)"); + + case HAL_DATASPACE_ARBITRARY: + return std::string("ARBITRARY"); + + case HAL_DATASPACE_UNKNOWN: + // Fallthrough + default: + return android::base::StringPrintf("Unknown deprecated dataspace code %d", + dataspaceSelect); + } + } + + return android::base::StringPrintf("Unknown dataspace code %d", dataspaceSelect); +} + +std::string decodeTransfer(android_dataspace dataspace) { + const uint32_t dataspaceTransfer = (dataspace & HAL_DATASPACE_TRANSFER_MASK); + switch (dataspaceTransfer) { + case HAL_DATASPACE_TRANSFER_UNSPECIFIED: + return std::string("Unspecified"); + + case HAL_DATASPACE_TRANSFER_LINEAR: + return std::string("Linear"); + + case HAL_DATASPACE_TRANSFER_SRGB: + return std::string("sRGB"); + + case HAL_DATASPACE_TRANSFER_SMPTE_170M: + return std::string("SMPTE_170M"); + + case HAL_DATASPACE_TRANSFER_GAMMA2_2: + return std::string("gamma 2.2"); + + case HAL_DATASPACE_TRANSFER_GAMMA2_6: + return std::string("gamma 2.6"); + + case HAL_DATASPACE_TRANSFER_GAMMA2_8: + return std::string("gamma 2.8"); + + case HAL_DATASPACE_TRANSFER_ST2084: + return std::string("SMPTE 2084"); + + case HAL_DATASPACE_TRANSFER_HLG: + return std::string("STD-B67"); + } + + return android::base::StringPrintf("Unknown dataspace transfer %d", dataspaceTransfer); +} + +std::string decodeRange(android_dataspace dataspace) { + const uint32_t dataspaceRange = (dataspace & HAL_DATASPACE_RANGE_MASK); + switch (dataspaceRange) { + case HAL_DATASPACE_RANGE_UNSPECIFIED: + return std::string("Range Unspecified"); + + case HAL_DATASPACE_RANGE_FULL: + return std::string("Full range"); + + case HAL_DATASPACE_RANGE_LIMITED: + return std::string("Limited range"); + + case HAL_DATASPACE_RANGE_EXTENDED: + return std::string("Extended range"); + } + + return android::base::StringPrintf("Unknown dataspace range %d", dataspaceRange); +} + +std::string dataspaceDetails(android_dataspace dataspace) { + return android::base::StringPrintf("%s %s %s", decodeStandard(dataspace).c_str(), + decodeTransfer(dataspace).c_str(), + decodeRange(dataspace).c_str()); +} + +std::string decodeColorMode(android_color_mode colorMode) { + switch (colorMode) { + case HAL_COLOR_MODE_NATIVE: + return std::string("HAL_COLOR_MODE_NATIVE"); + + case HAL_COLOR_MODE_STANDARD_BT601_625: + return std::string("HAL_COLOR_MODE_BT601_625"); + + case HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED: + return std::string("HAL_COLOR_MODE_BT601_625_UNADJUSTED"); + + case HAL_COLOR_MODE_STANDARD_BT601_525: + return std::string("HAL_COLOR_MODE_BT601_525"); + + case HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED: + return std::string("HAL_COLOR_MODE_BT601_525_UNADJUSTED"); + + case HAL_COLOR_MODE_STANDARD_BT709: + return std::string("HAL_COLOR_MODE_BT709"); + + case HAL_COLOR_MODE_DCI_P3: + return std::string("HAL_COLOR_MODE_DCI_P3"); + + case HAL_COLOR_MODE_SRGB: + return std::string("HAL_COLOR_MODE_SRGB"); + + case HAL_COLOR_MODE_ADOBE_RGB: + return std::string("HAL_COLOR_MODE_ADOBE_RGB"); + + case HAL_COLOR_MODE_DISPLAY_P3: + return std::string("HAL_COLOR_MODE_DISPLAY_P3"); + } + + return android::base::StringPrintf("Unknown color mode %d", colorMode); +} diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp index 7cf8233820..b67f4d9328 100644 --- a/libs/ui/Fence.cpp +++ b/libs/ui/Fence.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <ui/Fence.h> + #define LOG_TAG "Fence" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 @@ -25,9 +27,10 @@ #include <sync/sync.h> #pragma clang diagnostic pop -#include <ui/Fence.h> +#include <sys/types.h> #include <unistd.h> #include <utils/Log.h> +#include <utils/String8.h> #include <utils/Trace.h> namespace android { @@ -109,17 +112,17 @@ int Fence::dup() const { nsecs_t Fence::getSignalTime() const { if (mFenceFd == -1) { - return -1; + return SIGNAL_TIME_INVALID; } struct sync_fence_info_data* finfo = sync_fence_info(mFenceFd); if (finfo == NULL) { ALOGE("sync_fence_info returned NULL for fd %d", mFenceFd); - return -1; + return SIGNAL_TIME_INVALID; } if (finfo->status != 1) { sync_fence_info_free(finfo); - return INT64_MAX; + return SIGNAL_TIME_PENDING; } struct sync_pt_info* pinfo = NULL; @@ -162,7 +165,7 @@ status_t Fence::unflatten(void const*& buffer, size_t& size, int const*& fds, si return INVALID_OPERATION; } - if (size < 1) { + if (size < getFlattenedSize()) { return NO_MEMORY; } diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp new file mode 100644 index 0000000000..14147663de --- /dev/null +++ b/libs/ui/FenceTime.cpp @@ -0,0 +1,367 @@ +/* +* Copyright 2016 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT 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/FenceTime.h> + +#define LOG_TAG "FenceTime" + +#include <cutils/compiler.h> // For CC_[UN]LIKELY +#include <utils/Log.h> +#include <inttypes.h> +#include <stdlib.h> + +#include <memory> + +namespace android { + +// ============================================================================ +// FenceTime +// ============================================================================ + +const auto FenceTime::NO_FENCE = std::make_shared<FenceTime>(Fence::NO_FENCE); + +void* FenceTime::operator new(size_t byteCount) noexcept { + void *p = nullptr; + if (posix_memalign(&p, alignof(FenceTime), byteCount)) { + return nullptr; + } + return p; +} + +void FenceTime::operator delete(void *p) { + free(p); +} + +FenceTime::FenceTime(const sp<Fence>& fence) + : mState(((fence.get() != nullptr) && fence->isValid()) ? + State::VALID : State::INVALID), + mFence(fence), + mSignalTime(mState == State::INVALID ? + Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) { +} + +FenceTime::FenceTime(sp<Fence>&& fence) + : mState(((fence.get() != nullptr) && fence->isValid()) ? + State::VALID : State::INVALID), + mFence(std::move(fence)), + mSignalTime(mState == State::INVALID ? + Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) { +} + +FenceTime::FenceTime(nsecs_t signalTime) + : mState(Fence::isValidTimestamp(signalTime) ? State::VALID : State::INVALID), + mFence(nullptr), + mSignalTime(signalTime) { + if (CC_UNLIKELY(mSignalTime == Fence::SIGNAL_TIME_PENDING)) { + ALOGE("Pending signal time not allowed after signal."); + mSignalTime = Fence::SIGNAL_TIME_INVALID; + } +} + +void FenceTime::applyTrustedSnapshot(const Snapshot& src) { + if (CC_UNLIKELY(src.state != Snapshot::State::SIGNAL_TIME)) { + // Applying Snapshot::State::FENCE, could change the valid state of the + // FenceTime, which is not allowed. Callers should create a new + // FenceTime from the snapshot instead. + ALOGE("applyTrustedSnapshot: Unexpected fence."); + return; + } + + if (src.state == Snapshot::State::EMPTY) { + return; + } + + nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed); + if (signalTime != Fence::SIGNAL_TIME_PENDING) { + // We should always get the same signalTime here that we did in + // getSignalTime(). This check races with getSignalTime(), but it is + // only a sanity check so that's okay. + if (CC_UNLIKELY(signalTime != src.signalTime)) { + ALOGE("FenceTime::applyTrustedSnapshot: signalTime mismatch. " + "(%" PRId64 " (old) != %" PRId64 " (new))", + signalTime, src.signalTime); + } + return; + } + + std::lock_guard<std::mutex> lock(mMutex); + mFence.clear(); + mSignalTime.store(src.signalTime, std::memory_order_relaxed); +} + +bool FenceTime::isValid() const { + // We store the valid state in the constructors and return it here. + // This lets release code remember the valid state even after the + // underlying fence is destroyed. + return mState != State::INVALID; +} + +nsecs_t FenceTime::getSignalTime() { + // See if we already have a cached value we can return. + nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed); + if (signalTime != Fence::SIGNAL_TIME_PENDING) { + return signalTime; + } + + // Hold a reference to the fence on the stack in case the class' + // reference is removed by another thread. This prevents the + // fence from being destroyed until the end of this method, where + // we conveniently do not have the lock held. + sp<Fence> fence; + { + // With the lock acquired this time, see if we have the cached + // value or if we need to poll the fence. + std::lock_guard<std::mutex> lock(mMutex); + if (!mFence.get()) { + // Another thread set the signal time just before we added the + // reference to mFence. + return mSignalTime.load(std::memory_order_relaxed); + } + fence = mFence; + } + + // Make the system call without the lock held. + signalTime = fence->getSignalTime(); + + // Allow tests to override SIGNAL_TIME_INVALID behavior, since tests + // use invalid underlying Fences without real file descriptors. + if (CC_UNLIKELY(mState == State::FORCED_VALID_FOR_TEST)) { + if (signalTime == Fence::SIGNAL_TIME_INVALID) { + signalTime = Fence::SIGNAL_TIME_PENDING; + } + } + + // Make the signal time visible to everyone if it is no longer pending + // and remove the class' reference to the fence. + if (signalTime != Fence::SIGNAL_TIME_PENDING) { + std::lock_guard<std::mutex> lock(mMutex); + mFence.clear(); + mSignalTime.store(signalTime, std::memory_order_relaxed); + } + + return signalTime; +} + +nsecs_t FenceTime::getCachedSignalTime() const { + // memory_order_acquire since we don't have a lock fallback path + // that will do an acquire. + return mSignalTime.load(std::memory_order_acquire); +} + +FenceTime::Snapshot FenceTime::getSnapshot() const { + // Quick check without the lock. + nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed); + if (signalTime != Fence::SIGNAL_TIME_PENDING) { + return Snapshot(signalTime); + } + + // Do the full check with the lock. + std::lock_guard<std::mutex> lock(mMutex); + signalTime = mSignalTime.load(std::memory_order_relaxed); + if (signalTime != Fence::SIGNAL_TIME_PENDING) { + return Snapshot(signalTime); + } + return Snapshot(mFence); +} + +// For tests only. If forceValidForTest is true, then getSignalTime will +// never return SIGNAL_TIME_INVALID and isValid will always return true. +FenceTime::FenceTime(const sp<Fence>& fence, bool forceValidForTest) + : mState(forceValidForTest ? + State::FORCED_VALID_FOR_TEST : State::INVALID), + mFence(fence), + mSignalTime(mState == State::INVALID ? + Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) { +} + +void FenceTime::signalForTest(nsecs_t signalTime) { + // To be realistic, this should really set a hidden value that + // gets picked up in the next call to getSignalTime, but this should + // be good enough. + std::lock_guard<std::mutex> lock(mMutex); + mFence.clear(); + mSignalTime.store(signalTime, std::memory_order_relaxed); +} + +// ============================================================================ +// FenceTime::Snapshot +// ============================================================================ +FenceTime::Snapshot::Snapshot(const sp<Fence>& srcFence) + : state(State::FENCE), fence(srcFence) { +} + +FenceTime::Snapshot::Snapshot(nsecs_t srcSignalTime) + : state(State::SIGNAL_TIME), signalTime(srcSignalTime) { +} + +size_t FenceTime::Snapshot::getFlattenedSize() const { + constexpr size_t min = sizeof(state); + switch (state) { + case State::EMPTY: + return min; + case State::FENCE: + return min + fence->getFlattenedSize(); + case State::SIGNAL_TIME: + return min + sizeof(signalTime); + } + return 0; +} + +size_t FenceTime::Snapshot::getFdCount() const { + return state == State::FENCE ? fence->getFdCount() : 0u; +} + +status_t FenceTime::Snapshot::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, state); + switch (state) { + case State::EMPTY: + return NO_ERROR; + case State::FENCE: + return fence->flatten(buffer, size, fds, count); + case State::SIGNAL_TIME: + FlattenableUtils::write(buffer, size, signalTime); + return NO_ERROR; + } + + return NO_ERROR; +} + +status_t FenceTime::Snapshot::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { + if (size < sizeof(state)) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, state); + switch (state) { + case State::EMPTY: + return NO_ERROR; + case State::FENCE: + fence = new Fence; + return fence->unflatten(buffer, size, fds, count); + case State::SIGNAL_TIME: + if (size < sizeof(signalTime)) { + return NO_MEMORY; + } + FlattenableUtils::read(buffer, size, signalTime); + return NO_ERROR; + } + + return NO_ERROR; +} + +// ============================================================================ +// FenceTimeline +// ============================================================================ +void FenceTimeline::push(const std::shared_ptr<FenceTime>& fence) { + std::lock_guard<std::mutex> lock(mMutex); + while (mQueue.size() >= MAX_ENTRIES) { + // This is a sanity check to make sure the queue doesn't grow unbounded. + // MAX_ENTRIES should be big enough not to trigger this path. + // In case this path is taken though, users of FenceTime must make sure + // not to rely solely on FenceTimeline to get the final timestamp and + // should eventually call Fence::getSignalTime on their own. + std::shared_ptr<FenceTime> front = mQueue.front().lock(); + if (front) { + // Make a last ditch effort to get the signalTime here since + // we are removing it from the timeline. + front->getSignalTime(); + } + mQueue.pop(); + } + mQueue.push(fence); +} + +void FenceTimeline::updateSignalTimes() { + while (!mQueue.empty()) { + std::lock_guard<std::mutex> lock(mMutex); + std::shared_ptr<FenceTime> fence = mQueue.front().lock(); + if (!fence) { + // The shared_ptr no longer exists and no one cares about the + // timestamp anymore. + mQueue.pop(); + continue; + } else if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) { + // The fence has signaled and we've removed the sp<Fence> ref. + mQueue.pop(); + continue; + } else { + // The fence didn't signal yet. Break since the later ones + // shouldn't have signaled either. + break; + } + } +} + +// ============================================================================ +// FenceToFenceTimeMap +// ============================================================================ +std::shared_ptr<FenceTime> FenceToFenceTimeMap::createFenceTimeForTest( + const sp<Fence>& fence) { + std::lock_guard<std::mutex> lock(mMutex); + // Always garbage collecting isn't efficient, but this is only for testing. + garbageCollectLocked(); + std::shared_ptr<FenceTime> fenceTime(new FenceTime(fence, true)); + mMap[fence.get()].push_back(fenceTime); + return fenceTime; +} + +void FenceToFenceTimeMap::signalAllForTest( + const sp<Fence>& fence, nsecs_t signalTime) { + bool signaled = false; + + std::lock_guard<std::mutex> lock(mMutex); + auto it = mMap.find(fence.get()); + if (it != mMap.end()) { + for (auto& weakFenceTime : it->second) { + std::shared_ptr<FenceTime> fenceTime = weakFenceTime.lock(); + if (!fenceTime) { + continue; + } + ALOGE_IF(!fenceTime->isValid(), + "signalAllForTest: Signaling invalid fence."); + fenceTime->signalForTest(signalTime); + signaled = true; + } + } + + ALOGE_IF(!signaled, "signalAllForTest: Nothing to signal."); +} + +void FenceToFenceTimeMap::garbageCollectLocked() { + for (auto& it : mMap) { + // Erase all expired weak pointers from the vector. + auto& vect = it.second; + vect.erase( + std::remove_if(vect.begin(), vect.end(), + [](const std::weak_ptr<FenceTime>& ft) { + return ft.expired(); + }), + vect.end()); + + // Also erase the map entry if the vector is now empty. + if (vect.empty()) { + mMap.erase(it.first); + } + } +} + +} // namespace android diff --git a/libs/ui/Gralloc1.cpp b/libs/ui/Gralloc1.cpp deleted file mode 100644 index 4c73ce4535..0000000000 --- a/libs/ui/Gralloc1.cpp +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 - -#include <ui/Gralloc1.h> - -#include <vector> - -#undef LOG_TAG -#define LOG_TAG GRALLOC1_LOG_TAG - -namespace android { - -namespace Gralloc1 { - -Descriptor::~Descriptor() -{ - int32_t intError = mShimDevice.mFunctions.destroyDescriptor( - mShimDevice.mDevice, mDeviceId); - auto error = static_cast<gralloc1_error_t>(intError); - if (error != GRALLOC1_ERROR_NONE) { - ALOGE("destroyDescriptor failed: %d", intError); - } -} - -gralloc1_error_t Descriptor::setDimensions(uint32_t width, uint32_t height) -{ - int32_t intError = mShimDevice.mFunctions.setDimensions(mShimDevice.mDevice, - mDeviceId, width, height); - auto error = static_cast<gralloc1_error_t>(intError); - if (error != GRALLOC1_ERROR_NONE) { - return error; - } - mWidth = width; - mHeight = height; - return error; -} - -template <typename ApiType> -struct Setter { - typedef int32_t (*Type)(gralloc1_device_t*, gralloc1_buffer_descriptor_t, - ApiType); -}; - -template <typename ApiType, typename ValueType> -static inline gralloc1_error_t setHelper( - typename Setter<ApiType>::Type setter, gralloc1_device_t* device, - gralloc1_buffer_descriptor_t id, ValueType newValue, - ValueType* cacheVariable) -{ - int32_t intError = setter(device, id, static_cast<ApiType>(newValue)); - auto error = static_cast<gralloc1_error_t>(intError); - if (error != GRALLOC1_ERROR_NONE) { - return error; - } - *cacheVariable = newValue; - return error; -} - -gralloc1_error_t Descriptor::setFormat(android_pixel_format_t format) -{ - return setHelper<int32_t>(mShimDevice.mFunctions.setFormat.pfn, - mShimDevice.mDevice, mDeviceId, format, &mFormat); -} - -gralloc1_error_t Descriptor::setProducerUsage(gralloc1_producer_usage_t usage) -{ - return setHelper<uint64_t>(mShimDevice.mFunctions.setProducerUsage.pfn, - mShimDevice.mDevice, mDeviceId, usage, &mProducerUsage); -} - -gralloc1_error_t Descriptor::setConsumerUsage(gralloc1_consumer_usage_t usage) -{ - return setHelper<uint64_t>(mShimDevice.mFunctions.setConsumerUsage.pfn, - mShimDevice.mDevice, mDeviceId, usage, &mConsumerUsage); -} - -Device::Device(gralloc1_device_t* device) - : mDevice(device), - mCapabilities(loadCapabilities()), - mFunctions() -{ - if (!loadFunctions()) { - ALOGE("Failed to load a required function, aborting"); - abort(); - } -} - -bool Device::hasCapability(gralloc1_capability_t capability) const -{ - return mCapabilities.count(capability) > 0; -} - -std::string Device::dump() -{ - uint32_t length = 0; - mFunctions.dump(mDevice, &length, nullptr); - - std::vector<char> output; - output.resize(length); - mFunctions.dump(mDevice, &length, output.data()); - - return std::string(output.cbegin(), output.cend()); -} - -std::shared_ptr<Descriptor> Device::createDescriptor() -{ - gralloc1_buffer_descriptor_t descriptorId; - int32_t intError = mFunctions.createDescriptor(mDevice, &descriptorId); - auto error = static_cast<gralloc1_error_t>(intError); - if (error != GRALLOC1_ERROR_NONE) { - return nullptr; - } - auto descriptor = std::make_shared<Descriptor>(*this, descriptorId); - return descriptor; -} - -gralloc1_error_t Device::getStride(buffer_handle_t buffer, uint32_t* outStride) -{ - int32_t intError = mFunctions.getStride(mDevice, buffer, outStride); - return static_cast<gralloc1_error_t>(intError); -} - -static inline bool allocationSucceded(gralloc1_error_t error) -{ - return error == GRALLOC1_ERROR_NONE || error == GRALLOC1_ERROR_NOT_SHARED; -} - -gralloc1_error_t Device::allocate( - const std::vector<std::shared_ptr<const Descriptor>>& descriptors, - std::vector<buffer_handle_t>* outBuffers) -{ - if (mFunctions.allocate.pfn == nullptr) { - // Allocation is not supported on this device - return GRALLOC1_ERROR_UNSUPPORTED; - } - - std::vector<gralloc1_buffer_descriptor_t> deviceIds; - for (const auto& descriptor : descriptors) { - deviceIds.emplace_back(descriptor->getDeviceId()); - } - - std::vector<buffer_handle_t> buffers(descriptors.size()); - int32_t intError = mFunctions.allocate(mDevice, - static_cast<uint32_t>(descriptors.size()), deviceIds.data(), - buffers.data()); - auto error = static_cast<gralloc1_error_t>(intError); - if (allocationSucceded(error)) { - *outBuffers = std::move(buffers); - } - - return error; -} - -gralloc1_error_t Device::allocate( - const std::shared_ptr<const Descriptor>& descriptor, - gralloc1_backing_store_t id, buffer_handle_t* outBuffer) -{ - gralloc1_error_t error = GRALLOC1_ERROR_NONE; - - if (hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) { - buffer_handle_t buffer = nullptr; - int32_t intError = mFunctions.allocateWithId(mDevice, - descriptor->getDeviceId(), id, &buffer); - error = static_cast<gralloc1_error_t>(intError); - if (allocationSucceded(error)) { - *outBuffer = buffer; - } - } else { - std::vector<std::shared_ptr<const Descriptor>> descriptors; - descriptors.emplace_back(descriptor); - std::vector<buffer_handle_t> buffers; - error = allocate(descriptors, &buffers); - if (allocationSucceded(error)) { - *outBuffer = buffers[0]; - } - } - - return error; -} - -gralloc1_error_t Device::retain(buffer_handle_t buffer) -{ - int32_t intError = mFunctions.retain(mDevice, buffer); - return static_cast<gralloc1_error_t>(intError); -} - -gralloc1_error_t Device::retain(const GraphicBuffer* buffer) -{ - if (hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) { - return mFunctions.retainGraphicBuffer(mDevice, buffer); - } else { - return retain(buffer->getNativeBuffer()->handle); - } -} - -gralloc1_error_t Device::release(buffer_handle_t buffer) -{ - int32_t intError = mFunctions.release(mDevice, buffer); - return static_cast<gralloc1_error_t>(intError); -} - -gralloc1_error_t Device::getNumFlexPlanes(buffer_handle_t buffer, - uint32_t* outNumPlanes) -{ - uint32_t numPlanes = 0; - int32_t intError = mFunctions.getNumFlexPlanes(mDevice, buffer, &numPlanes); - auto error = static_cast<gralloc1_error_t>(intError); - if (error == GRALLOC1_ERROR_NONE) { - *outNumPlanes = numPlanes; - } - return error; -} - -gralloc1_error_t Device::lock(buffer_handle_t buffer, - gralloc1_producer_usage_t producerUsage, - gralloc1_consumer_usage_t consumerUsage, - const gralloc1_rect_t* accessRegion, void** outData, - const sp<Fence>& acquireFence) -{ - ALOGV("Calling lock(%p)", buffer); - return lockHelper(mFunctions.lock.pfn, buffer, producerUsage, - consumerUsage, accessRegion, outData, acquireFence); -} - -gralloc1_error_t Device::lockFlex(buffer_handle_t buffer, - gralloc1_producer_usage_t producerUsage, - gralloc1_consumer_usage_t consumerUsage, - const gralloc1_rect_t* accessRegion, - struct android_flex_layout* outData, - const sp<Fence>& acquireFence) -{ - ALOGV("Calling lockFlex(%p)", buffer); - return lockHelper(mFunctions.lockFlex.pfn, buffer, producerUsage, - consumerUsage, accessRegion, outData, acquireFence); -} - -gralloc1_error_t Device::lockYCbCr(buffer_handle_t buffer, - gralloc1_producer_usage_t producerUsage, - gralloc1_consumer_usage_t consumerUsage, - const gralloc1_rect_t* accessRegion, - struct android_ycbcr* outData, - const sp<Fence>& acquireFence) -{ - ALOGV("Calling lockYCbCr(%p)", buffer); - return lockHelper(mFunctions.lockYCbCr.pfn, buffer, producerUsage, - consumerUsage, accessRegion, outData, acquireFence); -} - -gralloc1_error_t Device::unlock(buffer_handle_t buffer, sp<Fence>* outFence) -{ - int32_t fenceFd = -1; - int32_t intError = mFunctions.unlock(mDevice, buffer, &fenceFd); - auto error = static_cast<gralloc1_error_t>(intError); - if (error == GRALLOC1_ERROR_NONE) { - *outFence = new Fence(fenceFd); - } - return error; -} - -std::unordered_set<gralloc1_capability_t> Device::loadCapabilities() -{ - std::vector<int32_t> intCapabilities; - uint32_t numCapabilities = 0; - mDevice->getCapabilities(mDevice, &numCapabilities, nullptr); - - intCapabilities.resize(numCapabilities); - mDevice->getCapabilities(mDevice, &numCapabilities, intCapabilities.data()); - - std::unordered_set<gralloc1_capability_t> capabilities; - for (const auto intCapability : intCapabilities) { - capabilities.emplace(static_cast<gralloc1_capability_t>(intCapability)); - } - return capabilities; -} - -bool Device::loadFunctions() -{ - // Functions which must always be present - if (!mFunctions.dump.load(mDevice, true)) { - return false; - } - if (!mFunctions.createDescriptor.load(mDevice, true)) { - return false; - } - if (!mFunctions.destroyDescriptor.load(mDevice, true)) { - return false; - } - if (!mFunctions.setConsumerUsage.load(mDevice, true)) { - return false; - } - if (!mFunctions.setDimensions.load(mDevice, true)) { - return false; - } - if (!mFunctions.setFormat.load(mDevice, true)) { - return false; - } - if (!mFunctions.setProducerUsage.load(mDevice, true)) { - return false; - } - if (!mFunctions.getBackingStore.load(mDevice, true)) { - return false; - } - if (!mFunctions.getConsumerUsage.load(mDevice, true)) { - return false; - } - if (!mFunctions.getDimensions.load(mDevice, true)) { - return false; - } - if (!mFunctions.getFormat.load(mDevice, true)) { - return false; - } - if (!mFunctions.getProducerUsage.load(mDevice, true)) { - return false; - } - if (!mFunctions.getStride.load(mDevice, true)) { - return false; - } - if (!mFunctions.retain.load(mDevice, true)) { - return false; - } - if (!mFunctions.release.load(mDevice, true)) { - return false; - } - if (!mFunctions.getNumFlexPlanes.load(mDevice, true)) { - return false; - } - if (!mFunctions.lock.load(mDevice, true)) { - return false; - } - if (!mFunctions.lockFlex.load(mDevice, true)) { - return false; - } - if (!mFunctions.unlock.load(mDevice, true)) { - return false; - } - - if (hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) { - // These should always be present on the adapter - if (!mFunctions.retainGraphicBuffer.load(mDevice, true)) { - return false; - } - if (!mFunctions.lockYCbCr.load(mDevice, true)) { - return false; - } - - // allocateWithId may not be present if we're only able to map in this - // process - mFunctions.allocateWithId.load(mDevice, false); - } else { - // allocate may not be present if we're only able to map in this process - mFunctions.allocate.load(mDevice, false); - } - - return true; -} - -std::unique_ptr<Gralloc1On0Adapter> Loader::mAdapter = nullptr; - -Loader::Loader() - : mDevice(nullptr) -{ - hw_module_t const* module; - int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); - uint8_t majorVersion = (module->module_api_version >> 8) & 0xFF; - uint8_t minorVersion = module->module_api_version & 0xFF; - gralloc1_device_t* device = nullptr; - if (majorVersion == 1) { - gralloc1_open(module, &device); - } else { - if (!mAdapter) { - mAdapter = std::make_unique<Gralloc1On0Adapter>(module); - } - device = mAdapter->getDevice(); - } - mDevice = std::make_unique<Gralloc1::Device>(device); -} - -Loader::~Loader() {} - -std::unique_ptr<Device> Loader::getDevice() -{ - return std::move(mDevice); -} - -} // namespace android::Gralloc1 - -} // namespace android diff --git a/libs/ui/Gralloc1On0Adapter.cpp b/libs/ui/Gralloc1On0Adapter.cpp deleted file mode 100644 index ec7df31f9c..0000000000 --- a/libs/ui/Gralloc1On0Adapter.cpp +++ /dev/null @@ -1,479 +0,0 @@ -/* - * Copyright 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#undef LOG_TAG -#define LOG_TAG "Gralloc1On0Adapter" -//#define LOG_NDEBUG 0 - -#include <hardware/gralloc.h> - -#include <ui/Gralloc1On0Adapter.h> - -#include <utils/Log.h> - -#include <inttypes.h> - -template <typename PFN, typename T> -static gralloc1_function_pointer_t asFP(T function) -{ - static_assert(std::is_same<PFN, T>::value, "Incompatible function pointer"); - return reinterpret_cast<gralloc1_function_pointer_t>(function); -} - -namespace android { - -Gralloc1On0Adapter::Gralloc1On0Adapter(const hw_module_t* module) - : mModule(reinterpret_cast<const gralloc_module_t*>(module)), - mMinorVersion(mModule->common.module_api_version & 0xFF), - mDevice(nullptr) -{ - ALOGV("Constructing"); - getCapabilities = getCapabilitiesHook; - getFunction = getFunctionHook; - int error = ::gralloc_open(&(mModule->common), &mDevice); - if (error) { - ALOGE("Failed to open gralloc0 module: %d", error); - } - ALOGV("Opened gralloc0 device %p", mDevice); -} - -Gralloc1On0Adapter::~Gralloc1On0Adapter() -{ - ALOGV("Destructing"); - if (mDevice) { - ALOGV("Closing gralloc0 device %p", mDevice); - ::gralloc_close(mDevice); - } -} - -void Gralloc1On0Adapter::doGetCapabilities(uint32_t* outCount, - int32_t* outCapabilities) -{ - if (outCapabilities == nullptr) { - *outCount = 1; - return; - } - if (*outCount >= 1) { - *outCapabilities = GRALLOC1_CAPABILITY_ON_ADAPTER; - *outCount = 1; - } -} - -gralloc1_function_pointer_t Gralloc1On0Adapter::doGetFunction( - int32_t intDescriptor) -{ - constexpr auto lastDescriptor = - static_cast<int32_t>(GRALLOC1_LAST_ADAPTER_FUNCTION); - if (intDescriptor < 0 || intDescriptor > lastDescriptor) { - ALOGE("Invalid function descriptor"); - return nullptr; - } - - auto descriptor = - static_cast<gralloc1_function_descriptor_t>(intDescriptor); - switch (descriptor) { - case GRALLOC1_FUNCTION_DUMP: - return asFP<GRALLOC1_PFN_DUMP>(dumpHook); - case GRALLOC1_FUNCTION_CREATE_DESCRIPTOR: - return asFP<GRALLOC1_PFN_CREATE_DESCRIPTOR>(createDescriptorHook); - case GRALLOC1_FUNCTION_DESTROY_DESCRIPTOR: - return asFP<GRALLOC1_PFN_DESTROY_DESCRIPTOR>(destroyDescriptorHook); - case GRALLOC1_FUNCTION_SET_CONSUMER_USAGE: - return asFP<GRALLOC1_PFN_SET_CONSUMER_USAGE>(setConsumerUsageHook); - case GRALLOC1_FUNCTION_SET_DIMENSIONS: - return asFP<GRALLOC1_PFN_SET_DIMENSIONS>(setDimensionsHook); - case GRALLOC1_FUNCTION_SET_FORMAT: - return asFP<GRALLOC1_PFN_SET_FORMAT>(setFormatHook); - case GRALLOC1_FUNCTION_SET_PRODUCER_USAGE: - return asFP<GRALLOC1_PFN_SET_PRODUCER_USAGE>(setProducerUsageHook); - case GRALLOC1_FUNCTION_GET_BACKING_STORE: - return asFP<GRALLOC1_PFN_GET_BACKING_STORE>( - bufferHook<decltype(&Buffer::getBackingStore), - &Buffer::getBackingStore, gralloc1_backing_store_t*>); - case GRALLOC1_FUNCTION_GET_CONSUMER_USAGE: - return asFP<GRALLOC1_PFN_GET_CONSUMER_USAGE>(getConsumerUsageHook); - case GRALLOC1_FUNCTION_GET_DIMENSIONS: - return asFP<GRALLOC1_PFN_GET_DIMENSIONS>( - bufferHook<decltype(&Buffer::getDimensions), - &Buffer::getDimensions, uint32_t*, uint32_t*>); - case GRALLOC1_FUNCTION_GET_FORMAT: - return asFP<GRALLOC1_PFN_GET_FORMAT>( - bufferHook<decltype(&Buffer::getFormat), - &Buffer::getFormat, int32_t*>); - case GRALLOC1_FUNCTION_GET_PRODUCER_USAGE: - return asFP<GRALLOC1_PFN_GET_PRODUCER_USAGE>(getProducerUsageHook); - case GRALLOC1_FUNCTION_GET_STRIDE: - return asFP<GRALLOC1_PFN_GET_STRIDE>( - bufferHook<decltype(&Buffer::getStride), - &Buffer::getStride, uint32_t*>); - case GRALLOC1_FUNCTION_ALLOCATE: - // Not provided, since we'll use ALLOCATE_WITH_ID - return nullptr; - case GRALLOC1_FUNCTION_ALLOCATE_WITH_ID: - if (mDevice != nullptr) { - return asFP<GRALLOC1_PFN_ALLOCATE_WITH_ID>(allocateWithIdHook); - } else { - return nullptr; - } - case GRALLOC1_FUNCTION_RETAIN: - return asFP<GRALLOC1_PFN_RETAIN>( - managementHook<&Gralloc1On0Adapter::retain>); - case GRALLOC1_FUNCTION_RELEASE: - return asFP<GRALLOC1_PFN_RELEASE>( - managementHook<&Gralloc1On0Adapter::release>); - case GRALLOC1_FUNCTION_RETAIN_GRAPHIC_BUFFER: - return asFP<GRALLOC1_PFN_RETAIN_GRAPHIC_BUFFER>( - retainGraphicBufferHook); - case GRALLOC1_FUNCTION_GET_NUM_FLEX_PLANES: - return asFP<GRALLOC1_PFN_GET_NUM_FLEX_PLANES>( - bufferHook<decltype(&Buffer::getNumFlexPlanes), - &Buffer::getNumFlexPlanes, uint32_t*>); - case GRALLOC1_FUNCTION_LOCK: - return asFP<GRALLOC1_PFN_LOCK>( - lockHook<void*, &Gralloc1On0Adapter::lock>); - case GRALLOC1_FUNCTION_LOCK_FLEX: - return asFP<GRALLOC1_PFN_LOCK_FLEX>( - lockHook<struct android_flex_layout, - &Gralloc1On0Adapter::lockFlex>); - case GRALLOC1_FUNCTION_LOCK_YCBCR: - return asFP<GRALLOC1_PFN_LOCK_YCBCR>( - lockHook<struct android_ycbcr, - &Gralloc1On0Adapter::lockYCbCr>); - case GRALLOC1_FUNCTION_UNLOCK: - return asFP<GRALLOC1_PFN_UNLOCK>(unlockHook); - case GRALLOC1_FUNCTION_INVALID: - ALOGE("Invalid function descriptor"); - return nullptr; - } - - ALOGE("Unknown function descriptor: %d", intDescriptor); - return nullptr; -} - -void Gralloc1On0Adapter::dump(uint32_t* outSize, char* outBuffer) -{ - ALOGV("dump(%u (%p), %p", outSize ? *outSize : 0, outSize, outBuffer); - - if (!mDevice->dump) { - // dump is optional on gralloc0 implementations - *outSize = 0; - return; - } - - if (!outBuffer) { - constexpr int32_t BUFFER_LENGTH = 4096; - char buffer[BUFFER_LENGTH] = {}; - mDevice->dump(mDevice, buffer, BUFFER_LENGTH); - buffer[BUFFER_LENGTH - 1] = 0; // Ensure the buffer is null-terminated - size_t actualLength = std::strlen(buffer); - mCachedDump.resize(actualLength); - std::copy_n(buffer, actualLength, mCachedDump.begin()); - *outSize = static_cast<uint32_t>(actualLength); - } else { - *outSize = std::min(*outSize, - static_cast<uint32_t>(mCachedDump.size())); - outBuffer = std::copy_n(mCachedDump.cbegin(), *outSize, outBuffer); - } -} - -gralloc1_error_t Gralloc1On0Adapter::createDescriptor( - gralloc1_buffer_descriptor_t* outDescriptor) -{ - auto descriptorId = sNextBufferDescriptorId++; - std::lock_guard<std::mutex> lock(mDescriptorMutex); - mDescriptors.emplace(descriptorId, - std::make_shared<Descriptor>(this, descriptorId)); - - ALOGV("Created descriptor %" PRIu64, descriptorId); - - *outDescriptor = descriptorId; - return GRALLOC1_ERROR_NONE; -} - -gralloc1_error_t Gralloc1On0Adapter::destroyDescriptor( - gralloc1_buffer_descriptor_t descriptor) -{ - ALOGV("Destroying descriptor %" PRIu64, descriptor); - - std::lock_guard<std::mutex> lock(mDescriptorMutex); - if (mDescriptors.count(descriptor) == 0) { - return GRALLOC1_ERROR_BAD_DESCRIPTOR; - } - - mDescriptors.erase(descriptor); - return GRALLOC1_ERROR_NONE; -} - -Gralloc1On0Adapter::Buffer::Buffer(buffer_handle_t handle, - gralloc1_backing_store_t store, const Descriptor& descriptor, - uint32_t stride, bool wasAllocated) - : mHandle(handle), - mReferenceCount(1), - mStore(store), - mDescriptor(descriptor), - mStride(stride), - mWasAllocated(wasAllocated) {} - -gralloc1_error_t Gralloc1On0Adapter::allocate( - const std::shared_ptr<Descriptor>& descriptor, - gralloc1_backing_store_t store, - buffer_handle_t* outBufferHandle) -{ - ALOGV("allocate(%" PRIu64 ", %#" PRIx64 ")", descriptor->id, store); - - // If this function is being called, it's because we handed out its function - // pointer, which only occurs when mDevice has been loaded successfully and - // we are permitted to allocate - - int usage = static_cast<int>(descriptor->producerUsage) | - static_cast<int>(descriptor->consumerUsage); - buffer_handle_t handle = nullptr; - int stride = 0; - ALOGV("Calling alloc(%p, %u, %u, %i, %u)", mDevice, descriptor->width, - descriptor->height, descriptor->format, usage); - auto error = mDevice->alloc(mDevice, - static_cast<int>(descriptor->width), - static_cast<int>(descriptor->height), descriptor->format, - usage, &handle, &stride); - if (error != 0) { - ALOGE("gralloc0 allocation failed: %d (%s)", error, - strerror(-error)); - return GRALLOC1_ERROR_NO_RESOURCES; - } - - *outBufferHandle = handle; - auto buffer = std::make_shared<Buffer>(handle, store, *descriptor, stride, - true); - - std::lock_guard<std::mutex> lock(mBufferMutex); - mBuffers.emplace(handle, std::move(buffer)); - - return GRALLOC1_ERROR_NONE; -} - -gralloc1_error_t Gralloc1On0Adapter::allocateWithIdHook( - gralloc1_device_t* device, gralloc1_buffer_descriptor_t descriptorId, - gralloc1_backing_store_t store, buffer_handle_t* outBuffer) -{ - auto adapter = getAdapter(device); - - auto descriptor = adapter->getDescriptor(descriptorId); - if (!descriptor) { - return GRALLOC1_ERROR_BAD_DESCRIPTOR; - } - - buffer_handle_t bufferHandle = nullptr; - auto error = adapter->allocate(descriptor, store, &bufferHandle); - if (error != GRALLOC1_ERROR_NONE) { - return error; - } - - *outBuffer = bufferHandle; - return error; -} - -gralloc1_error_t Gralloc1On0Adapter::retain( - const std::shared_ptr<Buffer>& buffer) -{ - std::lock_guard<std::mutex> lock(mBufferMutex); - buffer->retain(); - return GRALLOC1_ERROR_NONE; -} - -gralloc1_error_t Gralloc1On0Adapter::release( - const std::shared_ptr<Buffer>& buffer) -{ - std::lock_guard<std::mutex> lock(mBufferMutex); - if (!buffer->release()) { - return GRALLOC1_ERROR_NONE; - } - - buffer_handle_t handle = buffer->getHandle(); - if (buffer->wasAllocated()) { - ALOGV("Calling free(%p)", handle); - int result = mDevice->free(mDevice, handle); - if (result != 0) { - ALOGE("gralloc0 free failed: %d", result); - } - } else { - ALOGV("Calling unregisterBuffer(%p)", handle); - int result = mModule->unregisterBuffer(mModule, handle); - if (result != 0) { - ALOGE("gralloc0 unregister failed: %d", result); - } - } - - mBuffers.erase(handle); - return GRALLOC1_ERROR_NONE; -} - -gralloc1_error_t Gralloc1On0Adapter::retain( - const android::GraphicBuffer* graphicBuffer) -{ - ALOGV("retainGraphicBuffer(%p, %#" PRIx64 ")", - graphicBuffer->getNativeBuffer()->handle, graphicBuffer->getId()); - - buffer_handle_t handle = graphicBuffer->getNativeBuffer()->handle; - std::lock_guard<std::mutex> lock(mBufferMutex); - if (mBuffers.count(handle) != 0) { - mBuffers[handle]->retain(); - return GRALLOC1_ERROR_NONE; - } - - ALOGV("Calling registerBuffer(%p)", handle); - int result = mModule->registerBuffer(mModule, handle); - if (result != 0) { - ALOGE("gralloc0 register failed: %d", result); - return GRALLOC1_ERROR_NO_RESOURCES; - } - - Descriptor descriptor{this, sNextBufferDescriptorId++}; - descriptor.setDimensions(graphicBuffer->getWidth(), - graphicBuffer->getHeight()); - descriptor.setFormat(graphicBuffer->getPixelFormat()); - descriptor.setProducerUsage( - static_cast<gralloc1_producer_usage_t>(graphicBuffer->getUsage())); - descriptor.setConsumerUsage( - static_cast<gralloc1_consumer_usage_t>(graphicBuffer->getUsage())); - auto buffer = std::make_shared<Buffer>(handle, - static_cast<gralloc1_backing_store_t>(graphicBuffer->getId()), - descriptor, graphicBuffer->getStride(), false); - mBuffers.emplace(handle, std::move(buffer)); - return GRALLOC1_ERROR_NONE; -} - -gralloc1_error_t Gralloc1On0Adapter::lock( - const std::shared_ptr<Buffer>& buffer, - gralloc1_producer_usage_t producerUsage, - gralloc1_consumer_usage_t consumerUsage, - const gralloc1_rect_t& accessRegion, void** outData, - const sp<Fence>& acquireFence) -{ - if (mMinorVersion >= 3) { - int result = mModule->lockAsync(mModule, buffer->getHandle(), - static_cast<int32_t>(producerUsage | consumerUsage), - accessRegion.left, accessRegion.top, accessRegion.width, - accessRegion.height, outData, acquireFence->dup()); - if (result != 0) { - return GRALLOC1_ERROR_UNSUPPORTED; - } - } else { - acquireFence->waitForever("Gralloc1On0Adapter::lock"); - int result = mModule->lock(mModule, buffer->getHandle(), - static_cast<int32_t>(producerUsage | consumerUsage), - accessRegion.left, accessRegion.top, accessRegion.width, - accessRegion.height, outData); - ALOGV("gralloc0 lock returned %d", result); - if (result != 0) { - return GRALLOC1_ERROR_UNSUPPORTED; - } - } - return GRALLOC1_ERROR_NONE; -} - -gralloc1_error_t Gralloc1On0Adapter::lockFlex( - const std::shared_ptr<Buffer>& /*buffer*/, - gralloc1_producer_usage_t /*producerUsage*/, - gralloc1_consumer_usage_t /*consumerUsage*/, - const gralloc1_rect_t& /*accessRegion*/, - struct android_flex_layout* /*outData*/, - const sp<Fence>& /*acquireFence*/) -{ - // TODO - return GRALLOC1_ERROR_UNSUPPORTED; -} - -gralloc1_error_t Gralloc1On0Adapter::lockYCbCr( - const std::shared_ptr<Buffer>& buffer, - gralloc1_producer_usage_t producerUsage, - gralloc1_consumer_usage_t consumerUsage, - const gralloc1_rect_t& accessRegion, struct android_ycbcr* outData, - const sp<Fence>& acquireFence) -{ - if (mMinorVersion >= 3 && mModule->lockAsync_ycbcr) { - int result = mModule->lockAsync_ycbcr(mModule, buffer->getHandle(), - static_cast<int>(producerUsage | consumerUsage), - accessRegion.left, accessRegion.top, accessRegion.width, - accessRegion.height, outData, acquireFence->dup()); - if (result != 0) { - return GRALLOC1_ERROR_UNSUPPORTED; - } - } else if (mModule->lock_ycbcr) { - acquireFence->waitForever("Gralloc1On0Adapter::lockYCbCr"); - int result = mModule->lock_ycbcr(mModule, buffer->getHandle(), - static_cast<int>(producerUsage | consumerUsage), - accessRegion.left, accessRegion.top, accessRegion.width, - accessRegion.height, outData); - ALOGV("gralloc0 lockYCbCr returned %d", result); - if (result != 0) { - return GRALLOC1_ERROR_UNSUPPORTED; - } - } else { - return GRALLOC1_ERROR_UNSUPPORTED; - } - - return GRALLOC1_ERROR_NONE; -} - -gralloc1_error_t Gralloc1On0Adapter::unlock( - const std::shared_ptr<Buffer>& buffer, - sp<Fence>* outReleaseFence) -{ - if (mMinorVersion >= 3) { - int fenceFd = -1; - int result = mModule->unlockAsync(mModule, buffer->getHandle(), - &fenceFd); - if (result != 0) { - close(fenceFd); - ALOGE("gralloc0 unlockAsync failed: %d", result); - } else { - *outReleaseFence = new Fence(fenceFd); - } - } else { - int result = mModule->unlock(mModule, buffer->getHandle()); - if (result != 0) { - ALOGE("gralloc0 unlock failed: %d", result); - } - } - return GRALLOC1_ERROR_NONE; -} - -std::shared_ptr<Gralloc1On0Adapter::Descriptor> -Gralloc1On0Adapter::getDescriptor(gralloc1_buffer_descriptor_t descriptorId) -{ - std::lock_guard<std::mutex> lock(mDescriptorMutex); - if (mDescriptors.count(descriptorId) == 0) { - return nullptr; - } - - return mDescriptors[descriptorId]; -} - -std::shared_ptr<Gralloc1On0Adapter::Buffer> Gralloc1On0Adapter::getBuffer( - buffer_handle_t bufferHandle) -{ - std::lock_guard<std::mutex> lock(mBufferMutex); - if (mBuffers.count(bufferHandle) == 0) { - return nullptr; - } - - return mBuffers[bufferHandle]; -} - -std::atomic<gralloc1_buffer_descriptor_t> - Gralloc1On0Adapter::sNextBufferDescriptorId(1); - -} // namespace android diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp new file mode 100644 index 0000000000..87dbaf47d3 --- /dev/null +++ b/libs/ui/Gralloc2.cpp @@ -0,0 +1,253 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Gralloc2" + +#include <hwbinder/IPCThreadState.h> +#include <ui/Gralloc2.h> + +#include <log/log.h> +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wzero-length-array" +#include <sync/sync.h> +#pragma clang diagnostic pop + +namespace android { + +namespace Gralloc2 { + +static constexpr Error kTransactionError = Error::NO_RESOURCES; + +Mapper::Mapper() +{ + mMapper = IMapper::getService(); + if (mMapper == nullptr || mMapper->isRemote()) { + LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode"); + } +} + +Error Mapper::createDescriptor( + const IMapper::BufferDescriptorInfo& descriptorInfo, + BufferDescriptor* outDescriptor) const +{ + Error error; + auto ret = mMapper->createDescriptor(descriptorInfo, + [&](const auto& tmpError, const auto& tmpDescriptor) + { + error = tmpError; + if (error != Error::NONE) { + return; + } + + *outDescriptor = tmpDescriptor; + }); + + return (ret.isOk()) ? error : kTransactionError; +} + +Error Mapper::importBuffer(const hardware::hidl_handle& rawHandle, + buffer_handle_t* outBufferHandle) const +{ + Error error; + auto ret = mMapper->importBuffer(rawHandle, + [&](const auto& tmpError, const auto& tmpBuffer) + { + error = tmpError; + if (error != Error::NONE) { + return; + } + + *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer); + }); + + return (ret.isOk()) ? error : kTransactionError; +} + +void Mapper::freeBuffer(buffer_handle_t bufferHandle) const +{ + auto buffer = const_cast<native_handle_t*>(bufferHandle); + auto ret = mMapper->freeBuffer(buffer); + + auto error = (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError; + ALOGE_IF(error != Error::NONE, "freeBuffer(%p) failed with %d", + buffer, error); +} + +Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, + const IMapper::Rect& accessRegion, + int acquireFence, void** outData) const +{ + auto buffer = const_cast<native_handle_t*>(bufferHandle); + + // put acquireFence in a hidl_handle + hardware::hidl_handle acquireFenceHandle; + NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0); + if (acquireFence >= 0) { + auto h = native_handle_init(acquireFenceStorage, 1, 0); + h->data[0] = acquireFence; + acquireFenceHandle = h; + } + + Error error; + auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle, + [&](const auto& tmpError, const auto& tmpData) + { + error = tmpError; + if (error != Error::NONE) { + return; + } + + *outData = tmpData; + }); + + // we own acquireFence even on errors + if (acquireFence >= 0) { + close(acquireFence); + } + + return (ret.isOk()) ? error : kTransactionError; +} + +Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, + const IMapper::Rect& accessRegion, + int acquireFence, YCbCrLayout* outLayout) const +{ + auto buffer = const_cast<native_handle_t*>(bufferHandle); + + // put acquireFence in a hidl_handle + hardware::hidl_handle acquireFenceHandle; + NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0); + if (acquireFence >= 0) { + auto h = native_handle_init(acquireFenceStorage, 1, 0); + h->data[0] = acquireFence; + acquireFenceHandle = h; + } + + Error error; + auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion, + acquireFenceHandle, + [&](const auto& tmpError, const auto& tmpLayout) + { + error = tmpError; + if (error != Error::NONE) { + return; + } + + *outLayout = tmpLayout; + }); + + // we own acquireFence even on errors + if (acquireFence >= 0) { + close(acquireFence); + } + + return (ret.isOk()) ? error : kTransactionError; +} + +int Mapper::unlock(buffer_handle_t bufferHandle) const +{ + auto buffer = const_cast<native_handle_t*>(bufferHandle); + + int releaseFence = -1; + Error error; + auto ret = mMapper->unlock(buffer, + [&](const auto& tmpError, const auto& tmpReleaseFence) + { + error = tmpError; + if (error != Error::NONE) { + return; + } + + auto fenceHandle = tmpReleaseFence.getNativeHandle(); + if (fenceHandle && fenceHandle->numFds == 1) { + int fd = dup(fenceHandle->data[0]); + if (fd >= 0) { + releaseFence = fd; + } else { + ALOGD("failed to dup unlock release fence"); + sync_wait(fenceHandle->data[0], -1); + } + } + }); + + if (!ret.isOk()) { + error = kTransactionError; + } + + if (error != Error::NONE) { + ALOGE("unlock(%p) failed with %d", buffer, error); + } + + return releaseFence; +} + +Allocator::Allocator(const Mapper& mapper) + : mMapper(mapper) +{ + mAllocator = IAllocator::getService(); + if (mAllocator == nullptr) { + LOG_ALWAYS_FATAL("gralloc-alloc is missing"); + } +} + +std::string Allocator::dumpDebugInfo() const +{ + std::string debugInfo; + + mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { + debugInfo = tmpDebugInfo.c_str(); + }); + + return debugInfo; +} + +Error Allocator::allocate(BufferDescriptor descriptor, uint32_t count, + uint32_t* outStride, buffer_handle_t* outBufferHandles) const +{ + Error error; + auto ret = mAllocator->allocate(descriptor, count, + [&](const auto& tmpError, const auto& tmpStride, + const auto& tmpBuffers) { + error = tmpError; + if (tmpError != Error::NONE) { + return; + } + + // import buffers + for (uint32_t i = 0; i < count; i++) { + error = mMapper.importBuffer(tmpBuffers[i], + &outBufferHandles[i]); + if (error != Error::NONE) { + for (uint32_t j = 0; j < i; j++) { + mMapper.freeBuffer(outBufferHandles[j]); + outBufferHandles[j] = nullptr; + } + return; + } + } + + *outStride = tmpStride; + }); + + // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now + hardware::IPCThreadState::self()->flushCommands(); + + return (ret.isOk()) ? error : kTransactionError; +} + +} // namespace Gralloc2 + +} // namespace android diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 97b948d979..ee85c9bad9 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -16,17 +16,15 @@ #define LOG_TAG "GraphicBuffer" -#include <stdlib.h> -#include <stdint.h> -#include <sys/types.h> +#include <ui/GraphicBuffer.h> -#include <utils/Errors.h> -#include <utils/Log.h> +#include <cutils/atomic.h> -#include <ui/GraphicBuffer.h> +#include <grallocusage/GrallocUsageConversion.h> + +#include <ui/Gralloc2.h> #include <ui/GraphicBufferAllocator.h> #include <ui/GraphicBufferMapper.h> -#include <ui/PixelFormat.h> namespace android { @@ -41,6 +39,10 @@ static uint64_t getUniqueId() { return id; } +sp<GraphicBuffer> GraphicBuffer::from(ANativeWindowBuffer* anwb) { + return static_cast<GraphicBuffer *>(anwb); +} + GraphicBuffer::GraphicBuffer() : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()), mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0) @@ -50,51 +52,46 @@ GraphicBuffer::GraphicBuffer() stride = format = usage = 0; + layerCount = 0; handle = NULL; } +// deprecated GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inUsage, std::string requestorName) - : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()), - mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0) + : GraphicBuffer(inWidth, inHeight, inFormat, 1, static_cast<uint64_t>(inUsage), + requestorName) { - width = - height = - stride = - format = - usage = 0; - handle = NULL; - mInitCheck = initSize(inWidth, inHeight, inFormat, inUsage, - std::move(requestorName)); } GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, - PixelFormat inFormat, uint32_t inUsage, uint32_t inStride, - native_handle_t* inHandle, bool keepOwnership) - : BASE(), mOwner(keepOwnership ? ownHandle : ownNone), - mBufferMapper(GraphicBufferMapper::get()), - mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0) + PixelFormat inFormat, uint32_t inLayerCount, uint64_t usage, + std::string requestorName) + : GraphicBuffer() { - width = static_cast<int>(inWidth); - height = static_cast<int>(inHeight); - stride = static_cast<int>(inStride); - format = inFormat; - usage = static_cast<int>(inUsage); - handle = inHandle; + mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount, + usage, std::move(requestorName)); } -GraphicBuffer::GraphicBuffer(ANativeWindowBuffer* buffer, bool keepOwnership) - : BASE(), mOwner(keepOwnership ? ownHandle : ownNone), - mBufferMapper(GraphicBufferMapper::get()), - mInitCheck(NO_ERROR), mWrappedBuffer(buffer), mId(getUniqueId()), - mGenerationNumber(0) +// deprecated +GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, + PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage, + uint32_t inStride, native_handle_t* inHandle, bool keepOwnership) + : GraphicBuffer(inHandle, keepOwnership ? TAKE_HANDLE : WRAP_HANDLE, + inWidth, inHeight, inFormat, inLayerCount, static_cast<uint64_t>(inUsage), + inStride) { - width = buffer->width; - height = buffer->height; - stride = buffer->stride; - format = buffer->format; - usage = buffer->usage; - handle = buffer->handle; +} + +GraphicBuffer::GraphicBuffer(const native_handle_t* handle, + HandleWrapMethod method, uint32_t width, uint32_t height, + PixelFormat format, uint32_t layerCount, + uint64_t usage, + uint32_t stride) + : GraphicBuffer() +{ + mInitCheck = initWithHandle(handle, method, width, height, format, + layerCount, usage, stride); } GraphicBuffer::~GraphicBuffer() @@ -107,15 +104,12 @@ GraphicBuffer::~GraphicBuffer() void GraphicBuffer::free_handle() { if (mOwner == ownHandle) { - mBufferMapper.unregisterBuffer(handle); - native_handle_close(handle); - native_handle_delete(const_cast<native_handle*>(handle)); + mBufferMapper.freeBuffer(handle); } else if (mOwner == ownData) { GraphicBufferAllocator& allocator(GraphicBufferAllocator::get()); allocator.free(handle); } handle = NULL; - mWrappedBuffer = 0; } status_t GraphicBuffer::initCheck() const { @@ -135,7 +129,7 @@ ANativeWindowBuffer* GraphicBuffer::getNativeBuffer() const } status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight, - PixelFormat inFormat, uint32_t inUsage) + PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage) { if (mOwner != ownData) return INVALID_OPERATION; @@ -144,6 +138,7 @@ status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight, static_cast<int>(inWidth) == width && static_cast<int>(inHeight) == height && inFormat == format && + inLayerCount == layerCount && static_cast<int>(inUsage) == usage) return NO_ERROR; @@ -152,36 +147,78 @@ status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight, allocator.free(handle); handle = 0; } - return initSize(inWidth, inHeight, inFormat, inUsage, "[Reallocation]"); + return initWithSize(inWidth, inHeight, inFormat, inLayerCount, + inUsage, "[Reallocation]"); } bool GraphicBuffer::needsReallocation(uint32_t inWidth, uint32_t inHeight, - PixelFormat inFormat, uint32_t inUsage) + PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage) { if (static_cast<int>(inWidth) != width) return true; if (static_cast<int>(inHeight) != height) return true; if (inFormat != format) return true; + if (inLayerCount != layerCount) return true; if ((static_cast<uint32_t>(usage) & inUsage) != inUsage) return true; return false; } -status_t GraphicBuffer::initSize(uint32_t inWidth, uint32_t inHeight, - PixelFormat inFormat, uint32_t inUsage, std::string requestorName) +status_t GraphicBuffer::initWithSize(uint32_t inWidth, uint32_t inHeight, + PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage, + std::string requestorName) { GraphicBufferAllocator& allocator = GraphicBufferAllocator::get(); uint32_t outStride = 0; - status_t err = allocator.allocate(inWidth, inHeight, inFormat, inUsage, - &handle, &outStride, mId, std::move(requestorName)); + status_t err = allocator.allocate(inWidth, inHeight, inFormat, inLayerCount, + inUsage, &handle, &outStride, mId, + std::move(requestorName)); if (err == NO_ERROR) { width = static_cast<int>(inWidth); height = static_cast<int>(inHeight); format = inFormat; + layerCount = inLayerCount; usage = static_cast<int>(inUsage); stride = static_cast<int>(outStride); } return err; } +status_t GraphicBuffer::initWithHandle(const native_handle_t* handle, + HandleWrapMethod method, uint32_t width, uint32_t height, + PixelFormat format, uint32_t layerCount, uint64_t usage, + uint32_t stride) +{ + ANativeWindowBuffer::width = static_cast<int>(width); + ANativeWindowBuffer::height = static_cast<int>(height); + ANativeWindowBuffer::stride = static_cast<int>(stride); + ANativeWindowBuffer::format = format; + ANativeWindowBuffer::usage = static_cast<int>(usage); + + ANativeWindowBuffer::layerCount = layerCount; + + mOwner = (method == WRAP_HANDLE) ? ownNone : ownHandle; + + if (method == TAKE_UNREGISTERED_HANDLE || method == CLONE_HANDLE) { + buffer_handle_t importedHandle; + status_t err = mBufferMapper.importBuffer(handle, &importedHandle); + if (err != NO_ERROR) { + initWithHandle(nullptr, WRAP_HANDLE, 0, 0, 0, 0, 0, 0); + + return err; + } + + if (method == TAKE_UNREGISTERED_HANDLE) { + native_handle_close(handle); + native_handle_delete(const_cast<native_handle_t*>(handle)); + } + + handle = importedHandle; + } + + ANativeWindowBuffer::handle = handle; + + return NO_ERROR; +} + status_t GraphicBuffer::lock(uint32_t inUsage, void** vaddr) { const Rect lockBounds(width, height); @@ -239,6 +276,12 @@ status_t GraphicBuffer::lockAsync(uint32_t inUsage, void** vaddr, int fenceFd) status_t GraphicBuffer::lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd) { + return lockAsync(inUsage, inUsage, rect, vaddr, fenceFd); +} + +status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage, + uint64_t inConsumerUsage, const Rect& rect, void** vaddr, int fenceFd) +{ if (rect.left < 0 || rect.right > width || rect.top < 0 || rect.bottom > height) { ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)", @@ -246,8 +289,8 @@ status_t GraphicBuffer::lockAsync(uint32_t inUsage, const Rect& rect, width, height); return BAD_VALUE; } - status_t res = getBufferMapper().lockAsync(handle, inUsage, rect, vaddr, - fenceFd); + status_t res = getBufferMapper().lockAsync(handle, inProducerUsage, + inConsumerUsage, rect, vaddr, fenceFd); return res; } @@ -281,7 +324,7 @@ status_t GraphicBuffer::unlockAsync(int *fenceFd) } size_t GraphicBuffer::getFlattenedSize() const { - return static_cast<size_t>(11 + (handle ? handle->numInts : 0)) * sizeof(int); + return static_cast<size_t>(12 + (handle ? handle->numInts : 0)) * sizeof(int); } size_t GraphicBuffer::getFdCount() const { @@ -301,19 +344,20 @@ status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& buf[2] = height; buf[3] = stride; buf[4] = format; - buf[5] = usage; - buf[6] = static_cast<int32_t>(mId >> 32); - buf[7] = static_cast<int32_t>(mId & 0xFFFFFFFFull); - buf[8] = static_cast<int32_t>(mGenerationNumber); - buf[9] = 0; + buf[5] = static_cast<int32_t>(layerCount); + buf[6] = usage; + buf[7] = static_cast<int32_t>(mId >> 32); + buf[8] = static_cast<int32_t>(mId & 0xFFFFFFFFull); + buf[9] = static_cast<int32_t>(mGenerationNumber); buf[10] = 0; + buf[11] = 0; if (handle) { - buf[9] = handle->numFds; - buf[10] = handle->numInts; + buf[10] = handle->numFds; + buf[11] = handle->numInts; memcpy(fds, handle->data, static_cast<size_t>(handle->numFds) * sizeof(int)); - memcpy(&buf[11], handle->data + handle->numFds, + memcpy(&buf[12], handle->data + handle->numFds, static_cast<size_t>(handle->numInts) * sizeof(int)); } @@ -329,28 +373,28 @@ status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& status_t GraphicBuffer::unflatten( void const*& buffer, size_t& size, int const*& fds, size_t& count) { - if (size < 11 * sizeof(int)) return NO_MEMORY; + if (size < 12 * sizeof(int)) return NO_MEMORY; int const* buf = static_cast<int const*>(buffer); if (buf[0] != 'GBFR') return BAD_TYPE; - const size_t numFds = static_cast<size_t>(buf[9]); - const size_t numInts = static_cast<size_t>(buf[10]); + const size_t numFds = static_cast<size_t>(buf[10]); + const size_t numInts = static_cast<size_t>(buf[11]); // Limit the maxNumber to be relatively small. The number of fds or ints // should not come close to this number, and the number itself was simply // chosen to be high enough to not cause issues and low enough to prevent // overflow problems. const size_t maxNumber = 4096; - if (numFds >= maxNumber || numInts >= (maxNumber - 11)) { - width = height = stride = format = usage = 0; + if (numFds >= maxNumber || numInts >= (maxNumber - 12)) { + width = height = stride = format = layerCount = usage = 0; handle = NULL; ALOGE("unflatten: numFds or numInts is too large: %zd, %zd", numFds, numInts); return BAD_VALUE; } - const size_t sizeNeeded = (11 + numInts) * sizeof(int); + const size_t sizeNeeded = (12 + numInts) * sizeof(int); if (size < sizeNeeded) return NO_MEMORY; size_t fdCountNeeded = numFds; @@ -366,39 +410,45 @@ status_t GraphicBuffer::unflatten( height = buf[2]; stride = buf[3]; format = buf[4]; - usage = buf[5]; + layerCount = static_cast<uintptr_t>(buf[5]); + usage = buf[6]; native_handle* h = native_handle_create( static_cast<int>(numFds), static_cast<int>(numInts)); if (!h) { - width = height = stride = format = usage = 0; + width = height = stride = format = layerCount = usage = 0; handle = NULL; ALOGE("unflatten: native_handle_create failed"); return NO_MEMORY; } memcpy(h->data, fds, numFds * sizeof(int)); - memcpy(h->data + numFds, &buf[11], numInts * sizeof(int)); + memcpy(h->data + numFds, &buf[12], numInts * sizeof(int)); handle = h; } else { - width = height = stride = format = usage = 0; + width = height = stride = format = layerCount = usage = 0; handle = NULL; } - mId = static_cast<uint64_t>(buf[6]) << 32; - mId |= static_cast<uint32_t>(buf[7]); + mId = static_cast<uint64_t>(buf[7]) << 32; + mId |= static_cast<uint32_t>(buf[8]); - mGenerationNumber = static_cast<uint32_t>(buf[8]); + mGenerationNumber = static_cast<uint32_t>(buf[9]); mOwner = ownHandle; if (handle != 0) { - status_t err = mBufferMapper.registerBuffer(this); + buffer_handle_t importedHandle; + status_t err = mBufferMapper.importBuffer(handle, &importedHandle); if (err != NO_ERROR) { - width = height = stride = format = usage = 0; + width = height = stride = format = layerCount = usage = 0; handle = NULL; ALOGE("unflatten: registerBuffer failed: %s (%d)", strerror(-err), err); return err; } + + native_handle_close(handle); + native_handle_delete(const_cast<native_handle_t*>(handle)); + handle = importedHandle; } buffer = static_cast<void const*>(static_cast<uint8_t const*>(buffer) + sizeNeeded); diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index 75dd8df9f4..eaba1ed1aa 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -18,13 +18,19 @@ #define LOG_TAG "GraphicBufferAllocator" #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <ui/GraphicBufferAllocator.h> + +#include <stdio.h> + +#include <grallocusage/GrallocUsageConversion.h> + #include <log/log.h> #include <utils/Singleton.h> #include <utils/String8.h> #include <utils/Trace.h> -#include <ui/GraphicBufferAllocator.h> -#include <ui/Gralloc1On0Adapter.h> +#include <ui/Gralloc2.h> +#include <ui/GraphicBufferMapper.h> namespace android { // --------------------------------------------------------------------------- @@ -36,8 +42,11 @@ KeyedVector<buffer_handle_t, GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList; GraphicBufferAllocator::GraphicBufferAllocator() - : mLoader(std::make_unique<Gralloc1::Loader>()), - mDevice(mLoader->getDevice()) {} + : mMapper(GraphicBufferMapper::getInstance()), + mAllocator(std::make_unique<Gralloc2::Allocator>( + mMapper.getGrallocMapper())) +{ +} GraphicBufferAllocator::~GraphicBufferAllocator() {} @@ -54,22 +63,25 @@ void GraphicBufferAllocator::dump(String8& result) const for (size_t i=0 ; i<c ; i++) { const alloc_rec_t& rec(list.valueAt(i)); if (rec.size) { - snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %8X | 0x%08x | %s\n", + snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 + " | %s\n", list.keyAt(i), rec.size/1024.0, - rec.width, rec.stride, rec.height, rec.format, rec.usage, - rec.requestorName.c_str()); + rec.width, rec.stride, rec.height, rec.layerCount, rec.format, + rec.usage, rec.requestorName.c_str()); } else { - snprintf(buffer, SIZE, "%10p: unknown | %4u (%4u) x %4u | %8X | 0x%08x | %s\n", + snprintf(buffer, SIZE, "%10p: unknown | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 + " | %s\n", list.keyAt(i), - rec.width, rec.stride, rec.height, rec.format, rec.usage, - rec.requestorName.c_str()); + rec.width, rec.stride, rec.height, rec.layerCount, rec.format, + rec.usage, rec.requestorName.c_str()); } result.append(buffer); total += rec.size; } snprintf(buffer, SIZE, "Total allocated (estimate): %.2f KB\n", total/1024.0); result.append(buffer); - std::string deviceDump = mDevice->dump(); + + std::string deviceDump = mAllocator->dumpDebugInfo(); result.append(deviceDump.c_str(), deviceDump.size()); } @@ -81,8 +93,9 @@ void GraphicBufferAllocator::dumpToSystemLog() } status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, - PixelFormat format, uint32_t usage, buffer_handle_t* handle, - uint32_t* stride, uint64_t graphicBufferId, std::string requestorName) + PixelFormat format, uint32_t layerCount, uint64_t usage, + buffer_handle_t* handle, uint32_t* stride, + uint64_t /*graphicBufferId*/, std::string requestorName) { ATRACE_CALL(); @@ -91,46 +104,19 @@ status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, if (!width || !height) width = height = 1; - // Filter out any usage bits that should not be passed to the gralloc module - usage &= GRALLOC_USAGE_ALLOC_MASK; - - auto descriptor = mDevice->createDescriptor(); - auto error = descriptor->setDimensions(width, height); - if (error != GRALLOC1_ERROR_NONE) { - ALOGE("Failed to set dimensions to (%u, %u): %d", width, height, error); - return BAD_VALUE; - } - error = descriptor->setFormat(static_cast<android_pixel_format_t>(format)); - if (error != GRALLOC1_ERROR_NONE) { - ALOGE("Failed to set format to %d: %d", format, error); - return BAD_VALUE; - } - error = descriptor->setProducerUsage( - static_cast<gralloc1_producer_usage_t>(usage)); - if (error != GRALLOC1_ERROR_NONE) { - ALOGE("Failed to set producer usage to %u: %d", usage, error); - return BAD_VALUE; - } - error = descriptor->setConsumerUsage( - static_cast<gralloc1_consumer_usage_t>(usage)); - if (error != GRALLOC1_ERROR_NONE) { - ALOGE("Failed to set consumer usage to %u: %d", usage, error); - return BAD_VALUE; - } - - error = mDevice->allocate(descriptor, graphicBufferId, handle); - if (error != GRALLOC1_ERROR_NONE) { - ALOGE("Failed to allocate (%u x %u) format %d usage %u: %d", - width, height, format, usage, error); - return NO_MEMORY; - } + // Ensure that layerCount is valid. + if (layerCount < 1) + layerCount = 1; - error = mDevice->getStride(*handle, stride); - if (error != GRALLOC1_ERROR_NONE) { - ALOGW("Failed to get stride from buffer: %d", error); - } + Gralloc2::IMapper::BufferDescriptorInfo info = {}; + info.width = width; + info.height = height; + info.layerCount = layerCount; + info.format = static_cast<Gralloc2::PixelFormat>(format); + info.usage = usage; - if (error == NO_ERROR) { + Gralloc2::Error error = mAllocator->allocate(info, stride, handle); + if (error == Gralloc2::Error::NONE) { Mutex::Autolock _l(sLock); KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); uint32_t bpp = bytesPerPixel(format); @@ -139,23 +125,29 @@ status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, rec.height = height; rec.stride = *stride; rec.format = format; + rec.layerCount = layerCount; rec.usage = usage; rec.size = static_cast<size_t>(height * (*stride) * bpp); rec.requestorName = std::move(requestorName); list.add(*handle, rec); - } - return NO_ERROR; + return NO_ERROR; + } else { + ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " + "usage %" PRIx64 ": %d", + width, height, layerCount, format, usage, + error); + return NO_MEMORY; + } } status_t GraphicBufferAllocator::free(buffer_handle_t handle) { ATRACE_CALL(); - auto error = mDevice->release(handle); - if (error != GRALLOC1_ERROR_NONE) { - ALOGE("Failed to free buffer: %d", error); - } + // We allocated a buffer from the allocator and imported it into the + // mapper to get the handle. We just need to free the handle now. + mMapper.freeBuffer(handle); Mutex::Autolock _l(sLock); KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp index 481d43ce42..b9fa6400f5 100644 --- a/libs/ui/GraphicBufferMapper.cpp +++ b/libs/ui/GraphicBufferMapper.cpp @@ -18,8 +18,9 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 -#include <stdint.h> -#include <errno.h> +#include <ui/GraphicBufferMapper.h> + +#include <grallocusage/GrallocUsageConversion.h> // We would eliminate the non-conforming zero-length array, but we can't since // this is effectively included from the Linux kernel @@ -28,13 +29,11 @@ #include <sync/sync.h> #pragma clang diagnostic pop -#include <utils/Errors.h> #include <utils/Log.h> #include <utils/Trace.h> -#include <ui/Gralloc1On0Adapter.h> -#include <ui/GraphicBufferMapper.h> -#include <ui/Rect.h> +#include <ui/Gralloc2.h> +#include <ui/GraphicBuffer.h> #include <system/graphics.h> @@ -44,46 +43,35 @@ namespace android { ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper ) GraphicBufferMapper::GraphicBufferMapper() - : mLoader(std::make_unique<Gralloc1::Loader>()), - mDevice(mLoader->getDevice()) {} - - - -status_t GraphicBufferMapper::registerBuffer(buffer_handle_t handle) + : mMapper(std::make_unique<const Gralloc2::Mapper>()) { - ATRACE_CALL(); - - gralloc1_error_t error = mDevice->retain(handle); - ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d", - handle, error); - - return error; } -status_t GraphicBufferMapper::registerBuffer(const GraphicBuffer* buffer) +status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle, + buffer_handle_t* outHandle) { ATRACE_CALL(); - gralloc1_error_t error = mDevice->retain(buffer); - ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d", - buffer->getNativeBuffer()->handle, error); + Gralloc2::Error error = mMapper->importBuffer( + hardware::hidl_handle(rawHandle), outHandle); - return error; + ALOGW_IF(error != Gralloc2::Error::NONE, "importBuffer(%p) failed: %d", + rawHandle, error); + + return static_cast<status_t>(error); } -status_t GraphicBufferMapper::unregisterBuffer(buffer_handle_t handle) +status_t GraphicBufferMapper::freeBuffer(buffer_handle_t handle) { ATRACE_CALL(); - gralloc1_error_t error = mDevice->release(handle); - ALOGW_IF(error != GRALLOC1_ERROR_NONE, "unregisterBuffer(%p): failed %d", - handle, error); + mMapper->freeBuffer(handle); - return error; + return NO_ERROR; } -static inline gralloc1_rect_t asGralloc1Rect(const Rect& rect) { - gralloc1_rect_t outRect{}; +static inline Gralloc2::IMapper::Rect asGralloc2Rect(const Rect& rect) { + Gralloc2::IMapper::Rect outRect{}; outRect.left = rect.left; outRect.top = rect.top; outRect.width = rect.width(); @@ -117,18 +105,24 @@ status_t GraphicBufferMapper::unlock(buffer_handle_t handle) status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd) { + return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd); +} + +status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, + uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds, + void** vaddr, int fenceFd) +{ ATRACE_CALL(); - gralloc1_rect_t accessRegion = asGralloc1Rect(bounds); - sp<Fence> fence = new Fence(fenceFd); - gralloc1_error_t error = mDevice->lock(handle, - static_cast<gralloc1_producer_usage_t>(usage), - static_cast<gralloc1_consumer_usage_t>(usage), - &accessRegion, vaddr, fence); - ALOGW_IF(error != GRALLOC1_ERROR_NONE, "lock(%p, ...) failed: %d", handle, - error); + const uint64_t usage = static_cast<uint64_t>( + android_convertGralloc1To0Usage(producerUsage, consumerUsage)); + Gralloc2::Error error = mMapper->lock(handle, usage, + asGralloc2Rect(bounds), fenceFd, vaddr); - return error; + ALOGW_IF(error != Gralloc2::Error::NONE, "lock(%p, ...) failed: %d", + handle, error); + + return static_cast<status_t>(error); } static inline bool isValidYCbCrPlane(const android_flex_plane_t& plane) { @@ -159,132 +153,28 @@ status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle, { ATRACE_CALL(); - gralloc1_rect_t accessRegion = asGralloc1Rect(bounds); - sp<Fence> fence = new Fence(fenceFd); - - if (mDevice->hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) { - gralloc1_error_t error = mDevice->lockYCbCr(handle, - static_cast<gralloc1_producer_usage_t>(usage), - static_cast<gralloc1_consumer_usage_t>(usage), - &accessRegion, ycbcr, fence); - ALOGW_IF(error != GRALLOC1_ERROR_NONE, "lockYCbCr(%p, ...) failed: %d", - handle, error); - return error; + Gralloc2::YCbCrLayout layout; + Gralloc2::Error error = mMapper->lock(handle, usage, + asGralloc2Rect(bounds), fenceFd, &layout); + if (error == Gralloc2::Error::NONE) { + ycbcr->y = layout.y; + ycbcr->cb = layout.cb; + ycbcr->cr = layout.cr; + ycbcr->ystride = static_cast<size_t>(layout.yStride); + ycbcr->cstride = static_cast<size_t>(layout.cStride); + ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep); } - uint32_t numPlanes = 0; - gralloc1_error_t error = mDevice->getNumFlexPlanes(handle, &numPlanes); - if (error != GRALLOC1_ERROR_NONE) { - ALOGV("Failed to retrieve number of flex planes: %d", error); - return error; - } - if (numPlanes < 3) { - ALOGV("Not enough planes for YCbCr (%u found)", numPlanes); - return GRALLOC1_ERROR_UNSUPPORTED; - } - - std::vector<android_flex_plane_t> planes(numPlanes); - android_flex_layout_t flexLayout{}; - flexLayout.num_planes = numPlanes; - flexLayout.planes = planes.data(); - - error = mDevice->lockFlex(handle, - static_cast<gralloc1_producer_usage_t>(usage), - static_cast<gralloc1_consumer_usage_t>(usage), - &accessRegion, &flexLayout, fence); - if (error != GRALLOC1_ERROR_NONE) { - ALOGW("lockFlex(%p, ...) failed: %d", handle, error); - return error; - } - if (flexLayout.format != FLEX_FORMAT_YCbCr) { - ALOGV("Unable to convert flex-format buffer to YCbCr"); - unlock(handle); - return GRALLOC1_ERROR_UNSUPPORTED; - } - - // Find planes - auto yPlane = planes.cend(); - auto cbPlane = planes.cend(); - auto crPlane = planes.cend(); - for (auto planeIter = planes.cbegin(); planeIter != planes.cend(); - ++planeIter) { - if (planeIter->component == FLEX_COMPONENT_Y) { - yPlane = planeIter; - } else if (planeIter->component == FLEX_COMPONENT_Cb) { - cbPlane = planeIter; - } else if (planeIter->component == FLEX_COMPONENT_Cr) { - crPlane = planeIter; - } - } - if (yPlane == planes.cend()) { - ALOGV("Unable to find Y plane"); - unlock(handle); - return GRALLOC1_ERROR_UNSUPPORTED; - } - if (cbPlane == planes.cend()) { - ALOGV("Unable to find Cb plane"); - unlock(handle); - return GRALLOC1_ERROR_UNSUPPORTED; - } - if (crPlane == planes.cend()) { - ALOGV("Unable to find Cr plane"); - unlock(handle); - return GRALLOC1_ERROR_UNSUPPORTED; - } - - // Validate planes - if (!isValidYCbCrPlane(*yPlane)) { - ALOGV("Y plane is invalid"); - unlock(handle); - return GRALLOC1_ERROR_UNSUPPORTED; - } - if (!isValidYCbCrPlane(*cbPlane)) { - ALOGV("Cb plane is invalid"); - unlock(handle); - return GRALLOC1_ERROR_UNSUPPORTED; - } - if (!isValidYCbCrPlane(*crPlane)) { - ALOGV("Cr plane is invalid"); - unlock(handle); - return GRALLOC1_ERROR_UNSUPPORTED; - } - if (cbPlane->v_increment != crPlane->v_increment) { - ALOGV("Cb and Cr planes have different step (%d vs. %d)", - cbPlane->v_increment, crPlane->v_increment); - unlock(handle); - return GRALLOC1_ERROR_UNSUPPORTED; - } - if (cbPlane->h_increment != crPlane->h_increment) { - ALOGV("Cb and Cr planes have different stride (%d vs. %d)", - cbPlane->h_increment, crPlane->h_increment); - unlock(handle); - return GRALLOC1_ERROR_UNSUPPORTED; - } - - // Pack plane data into android_ycbcr struct - ycbcr->y = yPlane->top_left; - ycbcr->cb = cbPlane->top_left; - ycbcr->cr = crPlane->top_left; - ycbcr->ystride = static_cast<size_t>(yPlane->v_increment); - ycbcr->cstride = static_cast<size_t>(cbPlane->v_increment); - ycbcr->chroma_step = static_cast<size_t>(cbPlane->h_increment); - - return error; + return static_cast<status_t>(error); } status_t GraphicBufferMapper::unlockAsync(buffer_handle_t handle, int *fenceFd) { ATRACE_CALL(); - sp<Fence> fence = Fence::NO_FENCE; - gralloc1_error_t error = mDevice->unlock(handle, &fence); - if (error != GRALLOC1_ERROR_NONE) { - ALOGE("unlock(%p) failed: %d", handle, error); - return error; - } + *fenceFd = mMapper->unlock(handle); - *fenceFd = fence->dup(); - return error; + return NO_ERROR; } // --------------------------------------------------------------------------- diff --git a/libs/gui/GraphicsEnv.cpp b/libs/ui/GraphicsEnv.cpp index 68f0f988e2..8182c07001 100644 --- a/libs/gui/GraphicsEnv.cpp +++ b/libs/ui/GraphicsEnv.cpp @@ -16,13 +16,18 @@ //#define LOG_NDEBUG 1 #define LOG_TAG "GraphicsEnv" -#include <gui/GraphicsEnv.h> +#include <ui/GraphicsEnv.h> #include <mutex> #include <log/log.h> #include <nativeloader/dlext_namespaces.h> +// TODO(b/37049319) Get this from a header once one exists +extern "C" { + android_namespace_t* android_get_exported_namespace(const char*); +} + namespace android { /*static*/ GraphicsEnv& GraphicsEnv::getInstance() { @@ -43,33 +48,19 @@ void GraphicsEnv::setDriverPath(const std::string path) { android_namespace_t* GraphicsEnv::getDriverNamespace() { static std::once_flag once; std::call_once(once, [this]() { - // TODO; In the next version of Android, all graphics drivers will be - // loaded into a custom namespace. To minimize risk for this release, - // only updated drivers use a custom namespace. - // - // Additionally, the custom namespace will be - // ANDROID_NAMESPACE_TYPE_ISOLATED, and will only have access to a - // subset of the system. if (mDriverPath.empty()) return; - - char defaultPath[PATH_MAX]; - android_get_LD_LIBRARY_PATH(defaultPath, sizeof(defaultPath)); - size_t defaultPathLen = strlen(defaultPath); - - std::string path; - path.reserve(mDriverPath.size() + 1 + defaultPathLen); - path.append(mDriverPath); - path.push_back(':'); - path.append(defaultPath, defaultPathLen); - - mDriverNamespace = android_create_namespace( - "gfx driver", - nullptr, // ld_library_path - path.c_str(), // default_library_path - ANDROID_NAMESPACE_TYPE_SHARED, - nullptr, // permitted_when_isolated_path - nullptr); // parent + // If the sphal namespace isn't configured for a device, don't support updatable drivers. + // We need a parent namespace to inherit the default search path from. + auto sphalNamespace = android_get_exported_namespace("sphal"); + if (!sphalNamespace) return; + mDriverNamespace = android_create_namespace("gfx driver", + nullptr, // ld_library_path + mDriverPath.c_str(), // default_library_path + ANDROID_NAMESPACE_TYPE_SHARED | + ANDROID_NAMESPACE_TYPE_ISOLATED, + nullptr, // permitted_when_isolated_path + sphalNamespace); }); return mDriverNamespace; } diff --git a/libs/ui/HdrCapabilities.cpp b/libs/ui/HdrCapabilities.cpp index 511f68ab4c..39adc5e929 100644 --- a/libs/ui/HdrCapabilities.cpp +++ b/libs/ui/HdrCapabilities.cpp @@ -16,44 +16,76 @@ #include <ui/HdrCapabilities.h> -#include <binder/Parcel.h> - namespace android { -status_t HdrCapabilities::writeToParcel(Parcel* parcel) const -{ - status_t result = parcel->writeInt32Vector(mSupportedHdrTypes); - if (result != OK) { - return result; - } - result = parcel->writeFloat(mMaxLuminance); - if (result != OK) { - return result; +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundefined-reinterpret-cast" +#endif + +HdrCapabilities::~HdrCapabilities() = default; +HdrCapabilities::HdrCapabilities(HdrCapabilities&& other) = default; +HdrCapabilities& HdrCapabilities::operator=(HdrCapabilities&& other) = default; + + +size_t HdrCapabilities::getFlattenedSize() const { + return sizeof(mMaxLuminance) + + sizeof(mMaxAverageLuminance) + + sizeof(mMinLuminance) + + sizeof(int32_t) + + mSupportedHdrTypes.size() * sizeof(int32_t); +} + +status_t HdrCapabilities::flatten(void* buffer, size_t size) const { + + if (size < getFlattenedSize()) { + return NO_MEMORY; } - result = parcel->writeFloat(mMaxAverageLuminance); - if (result != OK) { - return result; + + int32_t* const buf = static_cast<int32_t*>(buffer); + reinterpret_cast<float&>(buf[0]) = mMaxLuminance; + reinterpret_cast<float&>(buf[1]) = mMaxAverageLuminance; + reinterpret_cast<float&>(buf[2]) = mMinLuminance; + buf[3] = static_cast<int32_t>(mSupportedHdrTypes.size()); + for (size_t i = 0, c = mSupportedHdrTypes.size(); i < c; ++i) { + buf[4 + i] = mSupportedHdrTypes[i]; } - result = parcel->writeFloat(mMinLuminance); - return result; + return NO_ERROR; } -status_t HdrCapabilities::readFromParcel(const Parcel* parcel) -{ - status_t result = parcel->readInt32Vector(&mSupportedHdrTypes); - if (result != OK) { - return result; +status_t HdrCapabilities::unflatten(void const* buffer, size_t size) { + + size_t minSize = sizeof(mMaxLuminance) + + sizeof(mMaxAverageLuminance) + + sizeof(mMinLuminance) + + sizeof(int32_t); + + if (size < minSize) { + return NO_MEMORY; } - result = parcel->readFloat(&mMaxLuminance); - if (result != OK) { - return result; + + int32_t const * const buf = static_cast<int32_t const *>(buffer); + const size_t itemCount = size_t(buf[3]); + + // check the buffer is large enough + if (size < minSize + itemCount * sizeof(int32_t)) { + return BAD_VALUE; } - result = parcel->readFloat(&mMaxAverageLuminance); - if (result != OK) { - return result; + + mMaxLuminance = reinterpret_cast<float const&>(buf[0]); + mMaxAverageLuminance = reinterpret_cast<float const&>(buf[1]); + mMinLuminance = reinterpret_cast<float const&>(buf[2]); + if (itemCount) { + mSupportedHdrTypes.reserve(itemCount); + for (size_t i = 0; i < itemCount; ++i) { + mSupportedHdrTypes[i] = buf[4 + i]; + } } - result = parcel->readFloat(&mMinLuminance); - return result; + return NO_ERROR; } +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + } // namespace android diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp index cab1dde3fa..e88fdd5e84 100644 --- a/libs/ui/PixelFormat.cpp +++ b/libs/ui/PixelFormat.cpp @@ -22,9 +22,12 @@ namespace android { uint32_t bytesPerPixel(PixelFormat format) { switch (format) { + case PIXEL_FORMAT_RGBA_FP16: + return 8; case PIXEL_FORMAT_RGBA_8888: case PIXEL_FORMAT_RGBX_8888: case PIXEL_FORMAT_BGRA_8888: + case PIXEL_FORMAT_RGBA_1010102: return 4; case PIXEL_FORMAT_RGB_888: return 3; @@ -38,9 +41,12 @@ uint32_t bytesPerPixel(PixelFormat format) { uint32_t bitsPerPixel(PixelFormat format) { switch (format) { + case PIXEL_FORMAT_RGBA_FP16: + return 64; case PIXEL_FORMAT_RGBA_8888: case PIXEL_FORMAT_RGBX_8888: case PIXEL_FORMAT_BGRA_8888: + case PIXEL_FORMAT_RGBA_1010102: return 32; case PIXEL_FORMAT_RGB_888: return 24; diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index 8cdab8ccf8..6733505090 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -21,11 +21,7 @@ cc_test { } cc_test { - name: "vec_test", - srcs: ["vec_test.cpp"], -} - -cc_test { - name: "mat_test", - srcs: ["mat_test.cpp"], + name: "colorspace_test", + shared_libs: ["libui"], + srcs: ["colorspace_test.cpp"], } diff --git a/libs/ui/tests/colorspace_test.cpp b/libs/ui/tests/colorspace_test.cpp new file mode 100644 index 0000000000..0a4873c8d3 --- /dev/null +++ b/libs/ui/tests/colorspace_test.cpp @@ -0,0 +1,183 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ColorSpaceTest" + +#include <math.h> +#include <stdlib.h> + +#include <ui/ColorSpace.h> + +#include <gtest/gtest.h> + +namespace android { + +class ColorSpaceTest : public testing::Test { +protected: +}; + +TEST_F(ColorSpaceTest, XYZ) { + mat3 sRGBToXYZ(transpose(mat3{ + 0.412391f, 0.357584f, 0.180481f, + 0.212639f, 0.715169f, 0.072192f, + 0.019331f, 0.119195f, 0.950532f + })); + + mat3 XYZtoSRGB(inverse(sRGBToXYZ)); + + ColorSpace sRGB("sRGB", sRGBToXYZ); + + EXPECT_EQ(sRGBToXYZ, sRGB.getRGBtoXYZ()); + EXPECT_EQ(XYZtoSRGB, sRGB.getXYZtoRGB()); +} + +TEST_F(ColorSpaceTest, XYZPrimaries) { + mat3 sRGBToXYZ(transpose(mat3{ + 0.412391f, 0.357584f, 0.180481f, + 0.212639f, 0.715169f, 0.072192f, + 0.019331f, 0.119195f, 0.950532f + })); + + ColorSpace sRGB("sRGB", sRGBToXYZ); + + EXPECT_NEAR(0.640f, sRGB.getPrimaries()[0].x, 1e-5f); + EXPECT_NEAR(0.330f, sRGB.getPrimaries()[0].y, 1e-5f); + + EXPECT_NEAR(0.300f, sRGB.getPrimaries()[1].x, 1e-5f); + EXPECT_NEAR(0.600f, sRGB.getPrimaries()[1].y, 1e-5f); + + EXPECT_NEAR(0.150f, sRGB.getPrimaries()[2].x, 1e-5f); + EXPECT_NEAR(0.060f, sRGB.getPrimaries()[2].y, 1e-5f); +} + +TEST_F(ColorSpaceTest, XYZWhitePoint) { + mat3 sRGBToXYZ(transpose(mat3{ + 0.412391f, 0.357584f, 0.180481f, + 0.212639f, 0.715169f, 0.072192f, + 0.019331f, 0.119195f, 0.950532f + })); + + ColorSpace sRGB("sRGB", sRGBToXYZ); + + EXPECT_NEAR(0.3127f, sRGB.getWhitePoint().x, 1e-5f); + EXPECT_NEAR(0.3290f, sRGB.getWhitePoint().y, 1e-5f); +} + +TEST_F(ColorSpaceTest, XYZFromPrimaries) { + mat3 sRGBToXYZ(transpose(mat3{ + 0.412391f, 0.357584f, 0.180481f, + 0.212639f, 0.715169f, 0.072192f, + 0.019331f, 0.119195f, 0.950532f + })); + + ColorSpace sRGB1("sRGB", sRGBToXYZ); + ColorSpace sRGB2( + "sRGB", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f} + ); + + for (size_t i = 0; i < 3; i++) { + for (size_t j= 0; j < 3; j++) { + ASSERT_NEAR(sRGB1.getRGBtoXYZ()[i][j], sRGB2.getRGBtoXYZ()[i][j], 1e-5f); + } + } + + for (size_t i = 0; i < 3; i++) { + for (size_t j= 0; j < 3; j++) { + ASSERT_NEAR(sRGB2.getXYZtoRGB()[i][j], sRGB2.getXYZtoRGB()[i][j], 1e-5f); + } + } +} + +TEST_F(ColorSpaceTest, TransferFunctions) { + ColorSpace sRGB = ColorSpace::sRGB(); + + EXPECT_NEAR(0.0f, sRGB.getEOTF()(0.0f), 1e-6f); + EXPECT_NEAR(0.0f, sRGB.getOETF()(0.0f), 1e-6f); + EXPECT_NEAR(1.0f, sRGB.getEOTF()(1.0f), 1e-6f); + EXPECT_NEAR(1.0f, sRGB.getOETF()(1.0f), 1e-6f); + + for (float v = 0.0f; v <= 0.5f; v += 1e-3f) { + ASSERT_TRUE(v >= sRGB.getEOTF()(v)); + ASSERT_TRUE(v <= sRGB.getOETF()(v)); + } + + float previousEOTF = std::numeric_limits<float>::lowest(); + float previousOETF = std::numeric_limits<float>::lowest(); + for (float v = 0.0f; v <= 1.0f; v += 1e-3f) { + ASSERT_TRUE(previousEOTF < sRGB.getEOTF()(v)); + previousEOTF = sRGB.getEOTF()(v); + ASSERT_TRUE(previousOETF < sRGB.getOETF()(v)); + previousOETF = sRGB.getOETF()(v); + } + + ColorSpace sRGB2( + "sRGB", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f} + // linear transfer functions + ); + for (float v = 0.0f; v <= 1.0f; v += 1e-3f) { + ASSERT_EQ(v, sRGB2.getEOTF()(v)); + ASSERT_EQ(v, sRGB2.getOETF()(v)); + } +} + +TEST_F(ColorSpaceTest, Clamping) { + // Pick a color outside of sRGB + float3 c(ColorSpace::BT2020().rgbToXYZ(float3{0, 1, 0})); + + // The color will be clamped + float3 sRGB(ColorSpace::sRGB().xyzToRGB(c)); + EXPECT_TRUE(sRGB > float3{0.0} && sRGB < float3{1.0}); + + // The color will not be clamped + float3 extendedSRGB(ColorSpace::linearExtendedSRGB().xyzToRGB(c)); + EXPECT_TRUE(extendedSRGB.g > 1.0f); +} + +TEST_F(ColorSpaceTest, Connect) { + // No chromatic adaptation + auto r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::AdobeRGB()) + .transform({1.0f, 0.5f, 0.0f}); + EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f}))); + + // Test with chromatic adaptation + r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::ProPhotoRGB()) + .transform({1.0f, 0.0f, 0.0f}); + EXPECT_TRUE(all(lessThan(abs(r - float3{0.70226f, 0.2757f, 0.1036f}), float3{1e-4f}))); +} + +TEST_F(ColorSpaceTest, LUT) { + auto lut = ColorSpace::createLUT(17, ColorSpace::sRGB(), ColorSpace::AdobeRGB()); + EXPECT_TRUE(lut != nullptr); + + // {1.0f, 0.5f, 0.0f} + auto r = lut.get()[0 * 17 * 17 + 8 * 17 + 16]; + EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f}))); + + // {1.0f, 1.0f, 0.5f} + r = lut.get()[8 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped + EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 0.5290f}), float3{1e-4f}))); + + // {1.0f, 1.0f, 1.0f} + r = lut.get()[16 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped + EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 1.0f}), float3{1e-4f}))); + +} + +}; // namespace android diff --git a/libs/ui/tests/mat_test.cpp b/libs/ui/tests/mat_test.cpp deleted file mode 100644 index a2c63ac7ca..0000000000 --- a/libs/ui/tests/mat_test.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "RegionTest" - -#include <stdlib.h> -#include <ui/Region.h> -#include <ui/Rect.h> -#include <gtest/gtest.h> - -#include <ui/mat4.h> - -namespace android { - -class MatTest : public testing::Test { -protected: -}; - -TEST_F(MatTest, Basics) { - mat4 m0; - EXPECT_EQ(sizeof(mat4), sizeof(float)*16); -} - -TEST_F(MatTest, ComparisonOps) { - mat4 m0; - mat4 m1(2); - - EXPECT_TRUE(m0 == m0); - EXPECT_TRUE(m0 != m1); - EXPECT_FALSE(m0 != m0); - EXPECT_FALSE(m0 == m1); -} - -TEST_F(MatTest, Constructors) { - mat4 m0; - ASSERT_EQ(m0[0].x, 1); - ASSERT_EQ(m0[0].y, 0); - ASSERT_EQ(m0[0].z, 0); - ASSERT_EQ(m0[0].w, 0); - ASSERT_EQ(m0[1].x, 0); - ASSERT_EQ(m0[1].y, 1); - ASSERT_EQ(m0[1].z, 0); - ASSERT_EQ(m0[1].w, 0); - ASSERT_EQ(m0[2].x, 0); - ASSERT_EQ(m0[2].y, 0); - ASSERT_EQ(m0[2].z, 1); - ASSERT_EQ(m0[2].w, 0); - ASSERT_EQ(m0[3].x, 0); - ASSERT_EQ(m0[3].y, 0); - ASSERT_EQ(m0[3].z, 0); - ASSERT_EQ(m0[3].w, 1); - - mat4 m1(2); - mat4 m2(vec4(2)); - mat4 m3(m2); - - EXPECT_EQ(m1, m2); - EXPECT_EQ(m2, m3); - EXPECT_EQ(m3, m1); - - mat4 m4(vec4(1), vec4(2), vec4(3), vec4(4)); -} - -TEST_F(MatTest, ArithmeticOps) { - mat4 m0; - mat4 m1(2); - mat4 m2(vec4(2)); - - m1 += m2; - EXPECT_EQ(mat4(4), m1); - - m2 -= m1; - EXPECT_EQ(mat4(-2), m2); - - m1 *= 2; - EXPECT_EQ(mat4(8), m1); - - m1 /= 2; - EXPECT_EQ(mat4(4), m1); - - m0 = -m0; - EXPECT_EQ(mat4(-1), m0); -} - -TEST_F(MatTest, UnaryOps) { - const mat4 identity; - mat4 m0; - - ++m0; - EXPECT_EQ(mat4( vec4(2,1,1,1), vec4(1,2,1,1), vec4(1,1,2,1), vec4(1,1,1,2) ), m0); - EXPECT_EQ(mat4( -vec4(2,1,1,1), -vec4(1,2,1,1), -vec4(1,1,2,1), -vec4(1,1,1,2) ), -m0); - - --m0; - EXPECT_EQ(identity, m0); -} - -TEST_F(MatTest, MiscOps) { - const mat4 identity; - mat4 m0; - EXPECT_EQ(4, trace(m0)); - - mat4 m1(vec4(1,2,3,4), vec4(5,6,7,8), vec4(9,10,11,12), vec4(13,14,15,16)); - mat4 m2(vec4(1,5,9,13), vec4(2,6,10,14), vec4(3,7,11,15), vec4(4,8,12,16)); - EXPECT_EQ(m1, transpose(m2)); - EXPECT_EQ(m2, transpose(m1)); - EXPECT_EQ(vec4(1,6,11,16), diag(m1)); - - EXPECT_EQ(identity, inverse(identity)); - - mat4 m3(vec4(4,3,0,0), vec4(3,2,0,0), vec4(0,0,1,0), vec4(0,0,0,1)); - mat4 m3i(inverse(m3)); - EXPECT_FLOAT_EQ(-2, m3i[0][0]); - EXPECT_FLOAT_EQ( 3, m3i[0][1]); - EXPECT_FLOAT_EQ( 3, m3i[1][0]); - EXPECT_FLOAT_EQ(-4, m3i[1][1]); - - mat4 m3ii(inverse(m3i)); - EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]); - EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]); - EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]); - EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]); - - EXPECT_EQ(m1, m1*identity); -} - -}; // namespace android diff --git a/libs/ui/tools/Android.bp b/libs/ui/tools/Android.bp new file mode 100644 index 0000000000..fb46c2b20c --- /dev/null +++ b/libs/ui/tools/Android.bp @@ -0,0 +1,35 @@ +// +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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_defaults { + name: "libui_tools_default", + clang_cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], +} + +cc_binary { + name: "lutgen", + cppflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + shared_libs: ["libui"], + srcs: ["lutgen.cpp"], +} diff --git a/libs/ui/tools/lutgen.cpp b/libs/ui/tools/lutgen.cpp new file mode 100644 index 0000000000..97b0822238 --- /dev/null +++ b/libs/ui/tools/lutgen.cpp @@ -0,0 +1,196 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <algorithm> +#include <fstream> +#include <iomanip> +#include <iostream> +#include <string> + +#include <getopt.h> + +#include <ui/ColorSpace.h> + +using namespace android; +using namespace std; + +uint32_t gSize = 32; +ColorSpace gColorSpaceSrc = ColorSpace::DisplayP3(); +ColorSpace gColorSpaceDst = ColorSpace::extendedSRGB(); +string gNameSrc = "DisplayP3"; +string gNameDst = "extendedSRGB"; + +static void printHelp() { + cout << "lutgen -d SIZE -s SOURCE -t TARGET <lut file>" << endl; + cout << endl; + cout << "Generate a 3D LUT to convert between two color spaces." << endl; + cout << endl; + cout << "If <lut file> ends in .inc, data is generated without the array declaration." << endl; + cout << endl; + cout << "Options:" << endl; + cout << " --help, -h" << endl; + cout << " print this message" << endl; + cout << " --dimension=, -d" << endl; + cout << " the dimension of the 3D LUT. Example: 17 for a 17x17x17 LUT. 32 by default" << endl; + cout << " --source=COLORSPACE, -s" << endl; + cout << " the source color space, see below for available names. DisplayP3 by default" << endl; + cout << " --target=COLORSPACE, -t" << endl; + cout << " the target color space, see below for available names. extendedSRGB by default" << endl; + cout << endl; + cout << "Colorspace names:" << endl; + cout << " sRGB" << endl; + cout << " linearSRGB" << endl; + cout << " extendedSRGB" << endl; + cout << " linearExtendedSRGB" << endl; + cout << " NTSC" << endl; + cout << " BT709" << endl; + cout << " BT2020" << endl; + cout << " AdobeRGB" << endl; + cout << " ProPhotoRGB" << endl; + cout << " DisplayP3" << endl; + cout << " DCIP3" << endl; + cout << " ACES" << endl; + cout << " ACEScg" << endl; +} + +static const ColorSpace findColorSpace(const string& name) { + if (name == "linearSRGB") return ColorSpace::linearSRGB(); + if (name == "extendedSRGB") return ColorSpace::extendedSRGB(); + if (name == "linearExtendedSRGB") return ColorSpace::linearExtendedSRGB(); + if (name == "NTSC") return ColorSpace::NTSC(); + if (name == "BT709") return ColorSpace::BT709(); + if (name == "BT2020") return ColorSpace::BT2020(); + if (name == "AdobeRGB") return ColorSpace::AdobeRGB(); + if (name == "ProPhotoRGB") return ColorSpace::ProPhotoRGB(); + if (name == "DisplayP3") return ColorSpace::DisplayP3(); + if (name == "DCIP3") return ColorSpace::DCIP3(); + if (name == "ACES") return ColorSpace::ACES(); + if (name == "ACEScg") return ColorSpace::ACEScg(); + return ColorSpace::sRGB(); +} + +static int handleCommandLineArgments(int argc, char* argv[]) { + static constexpr const char* OPTSTR = "h:d:s:t:"; + static const struct option OPTIONS[] = { + { "help", no_argument, 0, 'h' }, + { "dimension", required_argument, 0, 'd' }, + { "source", required_argument, 0, 's' }, + { "target", required_argument, 0, 't' }, + { 0, 0, 0, 0 } // termination of the option list + }; + + int opt; + int index = 0; + + while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &index)) >= 0) { + string arg(optarg ? optarg : ""); + switch (opt) { + default: + case 'h': + printHelp(); + exit(0); + break; + case 'd': + gSize = max(2, min(stoi(arg), 256)); + break; + case 's': + gNameSrc = arg; + gColorSpaceSrc = findColorSpace(arg); + break; + case 't': + gNameDst = arg; + gColorSpaceDst = findColorSpace(arg); + break; + } + } + + return optind; +} + +int main(int argc, char* argv[]) { + int optionIndex = handleCommandLineArgments(argc, argv); + int numArgs = argc - optionIndex; + + if (numArgs < 1) { + printHelp(); + return 1; + } + + bool isInclude = false; + + string filename(argv[optionIndex]); + size_t index = filename.find_last_of('.'); + + if (index != string::npos) { + string extension(filename.substr(index + 1)); + isInclude = extension == "inc"; + } + + ofstream outputStream(filename, ios::trunc); + if (outputStream.good()) { + auto lut = ColorSpace::createLUT(gSize, gColorSpaceSrc, gColorSpaceDst); + auto data = lut.get(); + + outputStream << "// generated with lutgen " << filename.c_str() << endl; + outputStream << "// 3D LUT stored as an RGB16F texture, in GL order" << endl; + outputStream << "// Size is " << gSize << "x" << gSize << "x" << gSize << endl; + + string src(gNameSrc); + string dst(gNameDst); + + if (!isInclude) { + transform(src.begin(), src.end(), src.begin(), ::toupper); + transform(dst.begin(), dst.end(), dst.begin(), ::toupper); + + outputStream << "const size_t LUT_" << src << "_TO_" << dst << "_SIZE = " << gSize << endl; + outputStream << "const uint16_t LUT_" << src << "_TO_" << dst << "[] = {"; + } else { + outputStream << "// From " << src << " to " << dst << endl; + } + + for (size_t z = 0; z < gSize; z++) { + for (size_t y = 0; y < gSize; y++) { + for (size_t x = 0; x < gSize; x++) { + if (x % 4 == 0) outputStream << endl << " "; + + half3 rgb = half3(*data++); + + const uint16_t r = rgb.r.getBits(); + const uint16_t g = rgb.g.getBits(); + const uint16_t b = rgb.b.getBits(); + + outputStream << "0x" << setfill('0') << setw(4) << hex << r << ", "; + outputStream << "0x" << setfill('0') << setw(4) << hex << g << ", "; + outputStream << "0x" << setfill('0') << setw(4) << hex << b << ", "; + } + } + } + + if (!isInclude) { + outputStream << endl << "}; // end LUT" << endl; + } + + outputStream << endl; + outputStream.flush(); + outputStream.close(); + } else { + cerr << "Could not write to file: " << filename << endl; + return 1; + + } + + return 0; +} diff --git a/libs/vr/.clang-format b/libs/vr/.clang-format new file mode 100644 index 0000000000..04d7970bbe --- /dev/null +++ b/libs/vr/.clang-format @@ -0,0 +1,5 @@ +BasedOnStyle: Google +DerivePointerAlignment: false +PointerAlignment: Left +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false diff --git a/libs/vr/Android.bp b/libs/vr/Android.bp new file mode 100644 index 0000000000..e8176cf6b6 --- /dev/null +++ b/libs/vr/Android.bp @@ -0,0 +1,3 @@ +subdirs = [ + "*", +] diff --git a/libs/vr/CPPLINT.cfg b/libs/vr/CPPLINT.cfg new file mode 100644 index 0000000000..87fb641c28 --- /dev/null +++ b/libs/vr/CPPLINT.cfg @@ -0,0 +1,2 @@ +set noparent +filter=-build/include_order,-legal/copyright,-build/include,-build/c++11,+build/include_alpha diff --git a/libs/vr/libbroadcastring/Android.bp b/libs/vr/libbroadcastring/Android.bp new file mode 100644 index 0000000000..13af470a26 --- /dev/null +++ b/libs/vr/libbroadcastring/Android.bp @@ -0,0 +1,35 @@ +cc_library_static { + name: "libbroadcastring", + clang: true, + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + export_include_dirs: ["include"], + shared_libs: [ + "libbase", + ], + export_shared_lib_headers: [ + "libbase", + ], +} + +cc_test { + name: "broadcast_ring_tests", + clang: true, + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + srcs: [ + "broadcast_ring_test.cc", + ], + static_libs: [ + "libbroadcastring", + ], + shared_libs: [ + "libbase", + ], +} diff --git a/libs/vr/libbroadcastring/broadcast_ring_test.cc b/libs/vr/libbroadcastring/broadcast_ring_test.cc new file mode 100644 index 0000000000..dfdd4ef0db --- /dev/null +++ b/libs/vr/libbroadcastring/broadcast_ring_test.cc @@ -0,0 +1,866 @@ +#include "libbroadcastring/broadcast_ring.h" + +#include <stdlib.h> +#include <memory> +#include <thread> // NOLINT +#include <sys/mman.h> + +#include <gtest/gtest.h> + +namespace android { +namespace dvr { +namespace { + +template <uint32_t N> +struct alignas(8) Aligned { + char v[N]; +}; + +template <uint32_t N> +struct alignas(8) Sized { + Sized() { Clear(); } + explicit Sized(char c) { Fill(c); } + char v[sizeof(Aligned<N>)]; + void Clear() { memset(v, 0, sizeof(v)); } + void Fill(char c) { memset(v, c, sizeof(v)); } + static Sized Pattern(uint8_t c) { + Sized sized; + for (size_t i = 0; i < sizeof(v); ++i) { + sized.v[i] = static_cast<char>(c + i); + } + return sized; + } + bool operator==(const Sized& right) const { + static_assert(sizeof(*this) == sizeof(v), "Size mismatch"); + return !memcmp(v, right.v, sizeof(v)); + } + template <typename SmallerSized> + SmallerSized Truncate() const { + SmallerSized val; + static_assert(sizeof(val.v) <= sizeof(v), "Cannot truncate to larger size"); + memcpy(val.v, v, sizeof(val.v)); + return val; + } +}; + +char FillChar(int val) { return static_cast<char>(val); } + +struct FakeMmap { + explicit FakeMmap(size_t size) : size(size), data(new char[size]) {} + size_t size; + std::unique_ptr<char[]> data; + void* mmap() { return static_cast<void*>(data.get()); } +}; + +template <typename Ring> +FakeMmap CreateRing(Ring* ring, uint32_t count) { + FakeMmap mmap(Ring::MemorySize(count)); + *ring = Ring::Create(mmap.mmap(), mmap.size, count); + return mmap; +} + +template <typename RecordType, bool StaticSize = false, + uint32_t StaticCount = 0, uint32_t MaxReserved = 1, + uint32_t MinAvailable = 0> +struct Traits { + using Record = RecordType; + static constexpr bool kUseStaticRecordSize = StaticSize; + static constexpr uint32_t kStaticRecordCount = StaticCount; + static constexpr uint32_t kMaxReservedRecords = MaxReserved; + static constexpr uint32_t kMinAvailableRecords = MinAvailable; + static constexpr uint32_t kMinRecordCount = MaxReserved + MinAvailable; +}; + +template <typename Record, bool StaticSize = false, uint32_t MaxReserved = 1, + uint32_t MinAvailable = 7> +struct TraitsDynamic + : public Traits<Record, StaticSize, 0, MaxReserved, MinAvailable> { + using Ring = BroadcastRing<Record, TraitsDynamic>; + static uint32_t MinCount() { return MaxReserved + MinAvailable; } +}; + +template <typename Record, uint32_t StaticCount = 1, bool StaticSize = true, + uint32_t MaxReserved = 1, uint32_t MinAvailable = 0> +struct TraitsStatic + : public Traits<Record, true, StaticCount, MaxReserved, MinAvailable> { + using Ring = BroadcastRing<Record, TraitsStatic>; + static uint32_t MinCount() { return StaticCount; } +}; + +using Dynamic_8_NxM = TraitsDynamic<Sized<8>>; +using Dynamic_16_NxM = TraitsDynamic<Sized<16>>; +using Dynamic_32_NxM = TraitsDynamic<Sized<32>>; +using Dynamic_32_32xM = TraitsDynamic<Sized<32>, true>; +using Dynamic_16_NxM_1plus0 = TraitsDynamic<Sized<16>, false, 1, 0>; +using Dynamic_16_NxM_1plus1 = TraitsDynamic<Sized<16>, false, 1, 1>; +using Dynamic_16_NxM_5plus11 = TraitsDynamic<Sized<16>, false, 5, 11>; +using Dynamic_256_NxM_1plus0 = TraitsDynamic<Sized<256>, false, 1, 0>; + +using Static_8_8x1 = TraitsStatic<Sized<8>, 1>; +using Static_8_8x16 = TraitsStatic<Sized<8>, 16>; +using Static_16_16x8 = TraitsStatic<Sized<16>, 8>; +using Static_16_16x16 = TraitsStatic<Sized<16>, 16>; +using Static_16_16x32 = TraitsStatic<Sized<16>, 32>; +using Static_32_Nx8 = TraitsStatic<Sized<32>, 8, false>; + +using TraitsList = ::testing::Types<Dynamic_8_NxM, // + Dynamic_16_NxM, // + Dynamic_32_NxM, // + Dynamic_32_32xM, // + Dynamic_16_NxM_1plus0, // + Dynamic_16_NxM_1plus1, // + Dynamic_16_NxM_5plus11, // + Dynamic_256_NxM_1plus0, // + Static_8_8x1, // + Static_8_8x16, // + Static_16_16x8, // + Static_16_16x16, // + Static_16_16x32, // + Static_32_Nx8>; + +} // namespace + +template <typename T> +class BroadcastRingTest : public ::testing::Test {}; + +TYPED_TEST_CASE(BroadcastRingTest, TraitsList); + +TYPED_TEST(BroadcastRingTest, Geometry) { + using Record = typename TypeParam::Record; + using Ring = typename TypeParam::Ring; + Ring ring; + auto mmap = CreateRing(&ring, Ring::Traits::MinCount()); + EXPECT_EQ(Ring::Traits::MinCount(), ring.record_count()); + EXPECT_EQ(sizeof(Record), ring.record_size()); +} + +TYPED_TEST(BroadcastRingTest, PutGet) { + using Record = typename TypeParam::Record; + using Ring = typename TypeParam::Ring; + Ring ring; + auto mmap = CreateRing(&ring, Ring::Traits::MinCount()); + const uint32_t oldest_sequence_at_start = ring.GetOldestSequence(); + const uint32_t next_sequence_at_start = ring.GetNextSequence(); + { + uint32_t sequence = oldest_sequence_at_start; + Record record; + EXPECT_FALSE(ring.Get(&sequence, &record)); + EXPECT_EQ(oldest_sequence_at_start, sequence); + EXPECT_EQ(Record(), record); + } + const Record original_record(0x1a); + ring.Put(original_record); + { + uint32_t sequence = next_sequence_at_start; + Record record; + EXPECT_TRUE(ring.Get(&sequence, &record)); + EXPECT_EQ(next_sequence_at_start, sequence); + EXPECT_EQ(original_record, record); + } + { + uint32_t sequence = next_sequence_at_start + 1; + Record record; + EXPECT_FALSE(ring.Get(&sequence, &record)); + EXPECT_EQ(next_sequence_at_start + 1, sequence); + EXPECT_EQ(Record(), record); + } +} + +TYPED_TEST(BroadcastRingTest, FillOnce) { + using Record = typename TypeParam::Record; + using Ring = typename TypeParam::Ring; + Ring ring; + auto mmap = CreateRing(&ring, Ring::Traits::MinCount()); + const uint32_t next_sequence_at_start = ring.GetNextSequence(); + for (uint32_t i = 0; i < ring.record_count(); ++i) + ring.Put(Record(FillChar(i))); + for (uint32_t i = 0; i < ring.record_count(); ++i) { + const uint32_t expected_sequence = next_sequence_at_start + i; + const Record expected_record(FillChar(i)); + { + uint32_t sequence = ring.GetOldestSequence() + i; + Record record; + EXPECT_TRUE(ring.Get(&sequence, &record)); + EXPECT_EQ(expected_sequence, sequence); + EXPECT_EQ(expected_record, record); + } + } + { + uint32_t sequence = ring.GetOldestSequence() + ring.record_count(); + Record record; + EXPECT_FALSE(ring.Get(&sequence, &record)); + } +} + +TYPED_TEST(BroadcastRingTest, FillTwice) { + using Record = typename TypeParam::Record; + using Ring = typename TypeParam::Ring; + Ring ring; + auto mmap = CreateRing(&ring, Ring::Traits::MinCount()); + const uint32_t next_sequence_at_start = ring.GetNextSequence(); + for (uint32_t i = 0; i < 2 * ring.record_count(); ++i) { + const Record newest_record(FillChar(i)); + ring.Put(newest_record); + + const uint32_t newest_sequence = next_sequence_at_start + i; + const uint32_t records_available = std::min(i + 1, ring.record_count()); + const uint32_t oldest_sequence = newest_sequence - records_available + 1; + EXPECT_EQ(newest_sequence, ring.GetNewestSequence()); + EXPECT_EQ(oldest_sequence, ring.GetOldestSequence()); + EXPECT_EQ(newest_sequence + 1, ring.GetNextSequence()); + + for (uint32_t j = 0; j < records_available; ++j) { + const uint32_t sequence_jth_newest = newest_sequence - j; + const Record record_jth_newest(FillChar(i - j)); + + { + uint32_t sequence = sequence_jth_newest; + Record record; + EXPECT_TRUE(ring.Get(&sequence, &record)); + EXPECT_EQ(sequence_jth_newest, sequence); + EXPECT_EQ(record_jth_newest, record); + } + + { + uint32_t sequence = sequence_jth_newest; + Record record; + EXPECT_TRUE(ring.GetNewest(&sequence, &record)); + EXPECT_EQ(newest_sequence, sequence); + EXPECT_EQ(newest_record, record); + } + } + + const Record oldest_record( + FillChar(i + (oldest_sequence - newest_sequence))); + const uint32_t sequence_0th_overwritten = oldest_sequence - 1; + const uint32_t sequence_0th_future = newest_sequence + 1; + const uint32_t sequence_1st_future = newest_sequence + 2; + + { + uint32_t sequence = sequence_0th_overwritten; + Record record; + EXPECT_TRUE(ring.Get(&sequence, &record)); + EXPECT_EQ(oldest_sequence, sequence); + EXPECT_EQ(oldest_record, record); + } + + { + uint32_t sequence = sequence_0th_overwritten; + Record record; + EXPECT_TRUE(ring.GetNewest(&sequence, &record)); + EXPECT_EQ(newest_sequence, sequence); + EXPECT_EQ(newest_record, record); + } + + { + uint32_t sequence = sequence_0th_future; + Record record; + EXPECT_FALSE(ring.Get(&sequence, &record)); + EXPECT_EQ(sequence_0th_future, sequence); + EXPECT_EQ(Record(), record); + } + + { + uint32_t sequence = sequence_0th_future; + Record record; + EXPECT_FALSE(ring.GetNewest(&sequence, &record)); + EXPECT_EQ(sequence_0th_future, sequence); + EXPECT_EQ(Record(), record); + } + + { + uint32_t sequence = sequence_1st_future; + Record record; + EXPECT_TRUE(ring.Get(&sequence, &record)); + EXPECT_EQ(oldest_sequence, sequence); + EXPECT_EQ(oldest_record, record); + } + + { + uint32_t sequence = sequence_1st_future; + Record record; + EXPECT_TRUE(ring.GetNewest(&sequence, &record)); + EXPECT_EQ(newest_sequence, sequence); + EXPECT_EQ(newest_record, record); + } + } +} + +TYPED_TEST(BroadcastRingTest, Import) { + using Record = typename TypeParam::Record; + using Ring = typename TypeParam::Ring; + Ring ring; + auto mmap = CreateRing(&ring, Ring::Traits::MinCount()); + + const uint32_t sequence_0 = ring.GetNextSequence(); + const uint32_t sequence_1 = ring.GetNextSequence() + 1; + const Record record_0 = Record::Pattern(0x00); + const Record record_1 = Record::Pattern(0x80); + ring.Put(record_0); + ring.Put(record_1); + + { + Ring imported_ring; + bool import_ok; + std::tie(imported_ring, import_ok) = Ring::Import(mmap.mmap(), mmap.size); + EXPECT_TRUE(import_ok); + EXPECT_EQ(ring.record_size(), imported_ring.record_size()); + EXPECT_EQ(ring.record_count(), imported_ring.record_count()); + + if (ring.record_count() != 1) { + uint32_t sequence = sequence_0; + Record imported_record; + EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record)); + EXPECT_EQ(sequence_0, sequence); + EXPECT_EQ(record_0, imported_record); + } + + { + uint32_t sequence = sequence_1; + Record imported_record; + EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record)); + EXPECT_EQ(sequence_1, sequence); + EXPECT_EQ(record_1, imported_record); + } + } +} + +TEST(BroadcastRingTest, ShouldFailImportIfStaticSizeMismatch) { + using OriginalRing = typename Static_16_16x16::Ring; + using RecordSizeMismatchRing = typename Static_8_8x16::Ring; + using RecordCountMismatchRing = typename Static_16_16x8::Ring; + + OriginalRing original_ring; + auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount()); + + { + using ImportedRing = RecordSizeMismatchRing; + ImportedRing imported_ring; + bool import_ok; + std::tie(imported_ring, import_ok) = + ImportedRing::Import(mmap.mmap(), mmap.size); + EXPECT_FALSE(import_ok); + auto mmap_imported = + CreateRing(&imported_ring, ImportedRing::Traits::MinCount()); + EXPECT_NE(original_ring.record_size(), imported_ring.record_size()); + EXPECT_EQ(original_ring.record_count(), imported_ring.record_count()); + } + + { + using ImportedRing = RecordCountMismatchRing; + ImportedRing imported_ring; + bool import_ok; + std::tie(imported_ring, import_ok) = + ImportedRing::Import(mmap.mmap(), mmap.size); + EXPECT_FALSE(import_ok); + auto mmap_imported = + CreateRing(&imported_ring, ImportedRing::Traits::MinCount()); + EXPECT_EQ(original_ring.record_size(), imported_ring.record_size()); + EXPECT_NE(original_ring.record_count(), imported_ring.record_count()); + } +} + +TEST(BroadcastRingTest, ShouldFailImportIfDynamicSizeGrows) { + using OriginalRing = typename Dynamic_8_NxM::Ring; + using RecordSizeGrowsRing = typename Dynamic_16_NxM::Ring; + + OriginalRing original_ring; + auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount()); + + { + using ImportedRing = RecordSizeGrowsRing; + ImportedRing imported_ring; + bool import_ok; + std::tie(imported_ring, import_ok) = + ImportedRing::Import(mmap.mmap(), mmap.size); + EXPECT_FALSE(import_ok); + auto mmap_imported = + CreateRing(&imported_ring, ImportedRing::Traits::MinCount()); + EXPECT_LT(original_ring.record_size(), imported_ring.record_size()); + EXPECT_EQ(original_ring.record_count(), imported_ring.record_count()); + } +} + +TEST(BroadcastRingTest, ShouldFailImportIfCountTooSmall) { + using OriginalRing = typename Dynamic_16_NxM_1plus0::Ring; + using MinCountRing = typename Dynamic_16_NxM_1plus1::Ring; + + OriginalRing original_ring; + auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount()); + + { + using ImportedRing = MinCountRing; + ImportedRing imported_ring; + bool import_ok; + std::tie(imported_ring, import_ok) = + ImportedRing::Import(mmap.mmap(), mmap.size); + EXPECT_FALSE(import_ok); + auto mmap_imported = + CreateRing(&imported_ring, ImportedRing::Traits::MinCount()); + EXPECT_EQ(original_ring.record_size(), imported_ring.record_size()); + EXPECT_LT(original_ring.record_count(), imported_ring.record_count()); + } +} + +TEST(BroadcastRingTest, ShouldFailImportIfMmapTooSmall) { + using OriginalRing = typename Dynamic_16_NxM::Ring; + + OriginalRing original_ring; + auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount()); + + { + using ImportedRing = OriginalRing; + ImportedRing imported_ring; + bool import_ok; + const size_t kMinSize = + ImportedRing::MemorySize(original_ring.record_count()); + std::tie(imported_ring, import_ok) = ImportedRing::Import(mmap.mmap(), 0); + EXPECT_FALSE(import_ok); + std::tie(imported_ring, import_ok) = + ImportedRing::Import(mmap.mmap(), kMinSize - 1); + EXPECT_FALSE(import_ok); + std::tie(imported_ring, import_ok) = + ImportedRing::Import(mmap.mmap(), kMinSize); + EXPECT_TRUE(import_ok); + EXPECT_EQ(original_ring.record_size(), imported_ring.record_size()); + EXPECT_EQ(original_ring.record_count(), imported_ring.record_count()); + } +} + +TEST(BroadcastRingTest, ShouldImportIfDynamicSizeShrinks) { + using OriginalRing = typename Dynamic_16_NxM::Ring; + using RecordSizeShrinksRing = typename Dynamic_8_NxM::Ring; + + OriginalRing original_ring; + auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount()); + + using OriginalRecord = typename OriginalRing::Record; + const uint32_t original_sequence_0 = original_ring.GetNextSequence(); + const uint32_t original_sequence_1 = original_ring.GetNextSequence() + 1; + const OriginalRecord original_record_0 = OriginalRecord::Pattern(0x00); + const OriginalRecord original_record_1 = OriginalRecord::Pattern(0x80); + original_ring.Put(original_record_0); + original_ring.Put(original_record_1); + + { + using ImportedRing = RecordSizeShrinksRing; + using ImportedRecord = typename ImportedRing::Record; + ImportedRing imported_ring; + bool import_ok; + std::tie(imported_ring, import_ok) = + ImportedRing::Import(mmap.mmap(), mmap.size); + EXPECT_TRUE(import_ok); + EXPECT_EQ(original_ring.record_size(), imported_ring.record_size()); + EXPECT_EQ(original_ring.record_count(), imported_ring.record_count()); + EXPECT_GT(sizeof(OriginalRecord), sizeof(ImportedRecord)); + + { + uint32_t sequence = original_sequence_0; + ImportedRecord shrunk_record; + EXPECT_TRUE(imported_ring.Get(&sequence, &shrunk_record)); + EXPECT_EQ(original_sequence_0, sequence); + EXPECT_EQ(original_record_0.Truncate<ImportedRecord>(), shrunk_record); + } + + { + uint32_t sequence = original_sequence_1; + ImportedRecord shrunk_record; + EXPECT_TRUE(imported_ring.Get(&sequence, &shrunk_record)); + EXPECT_EQ(original_sequence_1, sequence); + EXPECT_EQ(original_record_1.Truncate<ImportedRecord>(), shrunk_record); + } + } +} + +TEST(BroadcastRingTest, ShouldImportIfCompatibleDynamicToStatic) { + using OriginalRing = typename Dynamic_16_NxM::Ring; + using ImportedRing = typename Static_16_16x16::Ring; + using OriginalRecord = typename OriginalRing::Record; + using ImportedRecord = typename ImportedRing::Record; + using StaticRing = ImportedRing; + + OriginalRing original_ring; + auto mmap = CreateRing(&original_ring, StaticRing::Traits::MinCount()); + + const uint32_t original_sequence_0 = original_ring.GetNextSequence(); + const uint32_t original_sequence_1 = original_ring.GetNextSequence() + 1; + const OriginalRecord original_record_0 = OriginalRecord::Pattern(0x00); + const OriginalRecord original_record_1 = OriginalRecord::Pattern(0x80); + original_ring.Put(original_record_0); + original_ring.Put(original_record_1); + + { + ImportedRing imported_ring; + bool import_ok; + std::tie(imported_ring, import_ok) = + ImportedRing::Import(mmap.mmap(), mmap.size); + EXPECT_TRUE(import_ok); + EXPECT_EQ(original_ring.record_size(), imported_ring.record_size()); + EXPECT_EQ(original_ring.record_count(), imported_ring.record_count()); + + { + uint32_t sequence = original_sequence_0; + ImportedRecord imported_record; + EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record)); + EXPECT_EQ(original_sequence_0, sequence); + EXPECT_EQ(original_record_0, imported_record); + } + + { + uint32_t sequence = original_sequence_1; + ImportedRecord imported_record; + EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record)); + EXPECT_EQ(original_sequence_1, sequence); + EXPECT_EQ(original_record_1, imported_record); + } + } +} + +TEST(BroadcastRingTest, ShouldImportIfCompatibleStaticToDynamic) { + using OriginalRing = typename Static_16_16x16::Ring; + using ImportedRing = typename Dynamic_16_NxM::Ring; + using OriginalRecord = typename OriginalRing::Record; + using ImportedRecord = typename ImportedRing::Record; + using StaticRing = OriginalRing; + + OriginalRing original_ring; + auto mmap = CreateRing(&original_ring, StaticRing::Traits::MinCount()); + + const uint32_t original_sequence_0 = original_ring.GetNextSequence(); + const uint32_t original_sequence_1 = original_ring.GetNextSequence() + 1; + const OriginalRecord original_record_0 = OriginalRecord::Pattern(0x00); + const OriginalRecord original_record_1 = OriginalRecord::Pattern(0x80); + original_ring.Put(original_record_0); + original_ring.Put(original_record_1); + + { + ImportedRing imported_ring; + bool import_ok; + std::tie(imported_ring, import_ok) = + ImportedRing::Import(mmap.mmap(), mmap.size); + EXPECT_TRUE(import_ok); + EXPECT_EQ(original_ring.record_size(), imported_ring.record_size()); + EXPECT_EQ(original_ring.record_count(), imported_ring.record_count()); + + { + uint32_t sequence = original_sequence_0; + ImportedRecord imported_record; + EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record)); + EXPECT_EQ(original_sequence_0, sequence); + EXPECT_EQ(original_record_0, imported_record); + } + + { + uint32_t sequence = original_sequence_1; + ImportedRecord imported_record; + EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record)); + EXPECT_EQ(original_sequence_1, sequence); + EXPECT_EQ(original_record_1, imported_record); + } + } +} + +TEST(BroadcastRingTest, ShouldImportIfReadonlyMmap) { + using Ring = Dynamic_32_NxM::Ring; + using Record = Ring::Record; + + uint32_t record_count = Ring::Traits::MinCount(); + size_t ring_size = Ring::MemorySize(record_count); + + size_t page_size = sysconf(_SC_PAGESIZE); + size_t mmap_size = (ring_size + (page_size - 1)) & ~(page_size - 1); + ASSERT_GE(mmap_size, ring_size); + + void* mmap_base = mmap(nullptr, mmap_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + ASSERT_NE(MAP_FAILED, mmap_base); + + Ring ring = Ring::Create(mmap_base, mmap_size, record_count); + for (uint32_t i = 0; i < record_count; ++i) ring.Put(Record(FillChar(i))); + + ASSERT_EQ(0, mprotect(mmap_base, mmap_size, PROT_READ)); + + { + Ring imported_ring; + bool import_ok; + std::tie(imported_ring, import_ok) = Ring::Import(mmap_base, mmap_size); + EXPECT_TRUE(import_ok); + EXPECT_EQ(ring.record_size(), imported_ring.record_size()); + EXPECT_EQ(ring.record_count(), imported_ring.record_count()); + + uint32_t oldest_sequence = imported_ring.GetOldestSequence(); + for (uint32_t i = 0; i < record_count; ++i) { + uint32_t sequence = oldest_sequence + i; + Record record; + EXPECT_TRUE(imported_ring.Get(&sequence, &record)); + EXPECT_EQ(Record(FillChar(i)), record); + } + } + + ASSERT_EQ(0, munmap(mmap_base, mmap_size)); +} + +TEST(BroadcastRingTest, ShouldDieIfPutReadonlyMmap) { + using Ring = Dynamic_32_NxM::Ring; + using Record = Ring::Record; + + uint32_t record_count = Ring::Traits::MinCount(); + size_t ring_size = Ring::MemorySize(record_count); + + size_t page_size = sysconf(_SC_PAGESIZE); + size_t mmap_size = (ring_size + (page_size - 1)) & ~(page_size - 1); + ASSERT_GE(mmap_size, ring_size); + + void* mmap_base = mmap(nullptr, mmap_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + ASSERT_NE(MAP_FAILED, mmap_base); + + Ring ring = Ring::Create(mmap_base, mmap_size, record_count); + for (uint32_t i = 0; i < record_count; ++i) ring.Put(Record(FillChar(i))); + + ASSERT_EQ(0, mprotect(mmap_base, mmap_size, PROT_READ)); + + EXPECT_DEATH_IF_SUPPORTED({ ring.Put(Record(7)); }, ""); + + ASSERT_EQ(0, munmap(mmap_base, mmap_size)); +} + +TEST(BroadcastRingTest, ShouldDieIfCreationMmapTooSmall) { + using Ring = Dynamic_32_NxM::Ring; + using Record = Ring::Record; + + uint32_t record_count = Ring::Traits::MinCount(); + size_t ring_size = Ring::MemorySize(record_count); + FakeMmap mmap(ring_size); + + EXPECT_DEATH_IF_SUPPORTED({ + Ring ring = Ring::Create(mmap.mmap(), ring_size - 1, record_count); + }, ""); + + Ring ring = Ring::Create(mmap.mmap(), ring_size, record_count); + + ring.Put(Record(3)); + + { + uint32_t sequence = ring.GetNewestSequence(); + Record record; + EXPECT_TRUE(ring.Get(&sequence, &record)); + EXPECT_EQ(Record(3), record); + } +} + +TEST(BroadcastRingTest, ShouldDieIfCreationMmapMisaligned) { + using Ring = Static_8_8x1::Ring; + using Record = Ring::Record; + + constexpr int kAlign = Ring::mmap_alignment(); + constexpr int kMisalign = kAlign / 2; + size_t ring_size = Ring::MemorySize(); + std::unique_ptr<char[]> buf(new char[ring_size + kMisalign]); + + EXPECT_DEATH_IF_SUPPORTED( + { Ring ring = Ring::Create(buf.get() + kMisalign, ring_size); }, ""); + + Ring ring = Ring::Create(buf.get(), ring_size); + + ring.Put(Record(3)); + + { + uint32_t sequence = ring.GetNewestSequence(); + Record record; + EXPECT_TRUE(ring.Get(&sequence, &record)); + EXPECT_EQ(Record(3), record); + } +} + +template <typename Ring> +std::unique_ptr<std::thread> CopyTask(std::atomic<bool>* quit, void* in_base, + size_t in_size, void* out_base, + size_t out_size) { + return std::unique_ptr<std::thread>( + new std::thread([quit, in_base, in_size, out_base, out_size]() { + using Record = typename Ring::Record; + + bool import_ok; + Ring in_ring; + Ring out_ring; + std::tie(in_ring, import_ok) = Ring::Import(in_base, in_size); + ASSERT_TRUE(import_ok); + std::tie(out_ring, import_ok) = Ring::Import(out_base, out_size); + ASSERT_TRUE(import_ok); + + uint32_t sequence = in_ring.GetOldestSequence(); + while (!std::atomic_load_explicit(quit, std::memory_order_relaxed)) { + Record record; + if (in_ring.Get(&sequence, &record)) { + out_ring.Put(record); + sequence++; + } + } + })); +} + +TEST(BroadcastRingTest, ThreadedCopySingle) { + using Ring = Dynamic_32_NxM::Ring; + using Record = Ring::Record; + Ring in_ring; + auto in_mmap = CreateRing(&in_ring, Ring::Traits::MinCount()); + + Ring out_ring; + auto out_mmap = CreateRing(&out_ring, Ring::Traits::MinCount()); + + std::atomic<bool> quit(false); + std::unique_ptr<std::thread> copy_task = CopyTask<Ring>( + &quit, out_mmap.mmap(), out_mmap.size, in_mmap.mmap(), in_mmap.size); + + const Record out_record(0x1c); + out_ring.Put(out_record); + + uint32_t in_sequence = in_ring.GetOldestSequence(); + Record in_record; + while (!in_ring.Get(&in_sequence, &in_record)) { + // Do nothing. + } + + EXPECT_EQ(out_record, in_record); + std::atomic_store_explicit(&quit, true, std::memory_order_relaxed); + copy_task->join(); +} + +TEST(BroadcastRingTest, ThreadedCopyLossless) { + using Ring = Dynamic_32_NxM::Ring; + using Record = Ring::Record; + Ring in_ring; + auto in_mmap = CreateRing(&in_ring, Ring::Traits::MinCount()); + + Ring out_ring; + auto out_mmap = CreateRing(&out_ring, Ring::Traits::MinCount()); + + std::atomic<bool> quit(false); + std::unique_ptr<std::thread> copy_task = CopyTask<Ring>( + &quit, out_mmap.mmap(), out_mmap.size, in_mmap.mmap(), in_mmap.size); + + constexpr uint32_t kRecordsToProcess = 10000; + uint32_t out_records = 0; + uint32_t in_records = 0; + uint32_t in_sequence = in_ring.GetNextSequence(); + while (out_records < kRecordsToProcess || in_records < kRecordsToProcess) { + if (out_records < kRecordsToProcess && + out_records - in_records < out_ring.record_count()) { + const Record out_record(FillChar(out_records)); + out_ring.Put(out_record); + out_records++; + } + + Record in_record; + while (in_ring.Get(&in_sequence, &in_record)) { + EXPECT_EQ(Record(FillChar(in_records)), in_record); + in_records++; + in_sequence++; + } + } + + EXPECT_EQ(kRecordsToProcess, out_records); + EXPECT_EQ(kRecordsToProcess, in_records); + + std::atomic_store_explicit(&quit, true, std::memory_order_relaxed); + copy_task->join(); +} + +TEST(BroadcastRingTest, ThreadedCopyLossy) { + using Ring = Dynamic_32_NxM::Ring; + using Record = Ring::Record; + Ring in_ring; + auto in_mmap = CreateRing(&in_ring, Ring::Traits::MinCount()); + + Ring out_ring; + auto out_mmap = CreateRing(&out_ring, Ring::Traits::MinCount()); + + std::atomic<bool> quit(false); + std::unique_ptr<std::thread> copy_task = CopyTask<Ring>( + &quit, out_mmap.mmap(), out_mmap.size, in_mmap.mmap(), in_mmap.size); + + constexpr uint32_t kRecordsToProcess = 100000; + uint32_t out_records = 0; + uint32_t in_records = 0; + uint32_t in_sequence = in_ring.GetNextSequence(); + while (out_records < kRecordsToProcess) { + const Record out_record(FillChar(out_records)); + out_ring.Put(out_record); + out_records++; + + Record in_record; + if (in_ring.GetNewest(&in_sequence, &in_record)) { + EXPECT_EQ(Record(in_record.v[0]), in_record); + in_records++; + in_sequence++; + } + } + + EXPECT_EQ(kRecordsToProcess, out_records); + EXPECT_GE(kRecordsToProcess, in_records); + + std::atomic_store_explicit(&quit, true, std::memory_order_relaxed); + copy_task->join(); +} + +template <typename Ring> +std::unique_ptr<std::thread> CheckFillTask(std::atomic<bool>* quit, + void* in_base, size_t in_size) { + return std::unique_ptr<std::thread>( + new std::thread([quit, in_base, in_size]() { + using Record = typename Ring::Record; + + bool import_ok; + Ring in_ring; + std::tie(in_ring, import_ok) = Ring::Import(in_base, in_size); + ASSERT_TRUE(import_ok); + + uint32_t sequence = in_ring.GetOldestSequence(); + while (!std::atomic_load_explicit(quit, std::memory_order_relaxed)) { + Record record; + if (in_ring.Get(&sequence, &record)) { + ASSERT_EQ(Record(record.v[0]), record); + sequence++; + } + } + })); +} + +template <typename Ring> +void ThreadedOverwriteTorture() { + using Record = typename Ring::Record; + + // Maximize overwrites by having few records. + const int kMinRecordCount = 1; + const int kMaxRecordCount = 4; + + for (int count = kMinRecordCount; count <= kMaxRecordCount; count *= 2) { + Ring out_ring; + auto out_mmap = CreateRing(&out_ring, count); + + std::atomic<bool> quit(false); + std::unique_ptr<std::thread> check_task = + CheckFillTask<Ring>(&quit, out_mmap.mmap(), out_mmap.size); + + constexpr int kIterations = 10000; + for (int i = 0; i < kIterations; ++i) { + const Record record(FillChar(i)); + out_ring.Put(record); + } + + std::atomic_store_explicit(&quit, true, std::memory_order_relaxed); + check_task->join(); + } +} + +TEST(BroadcastRingTest, ThreadedOverwriteTortureSmall) { + ThreadedOverwriteTorture<Dynamic_16_NxM_1plus0::Ring>(); +} + +TEST(BroadcastRingTest, ThreadedOverwriteTortureLarge) { + ThreadedOverwriteTorture<Dynamic_256_NxM_1plus0::Ring>(); +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h b/libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h new file mode 100644 index 0000000000..69cb64826e --- /dev/null +++ b/libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h @@ -0,0 +1,668 @@ +#ifndef ANDROID_DVR_BROADCAST_RING_H_ +#define ANDROID_DVR_BROADCAST_RING_H_ + +#include <inttypes.h> +#include <stddef.h> +#include <stdio.h> +#include <atomic> +#include <limits> +#include <tuple> +#include <type_traits> +#include <utility> + +#include "android-base/logging.h" + +#if ATOMIC_LONG_LOCK_FREE != 2 || ATOMIC_INT_LOCK_FREE != 2 +#error "This file requires lock free atomic uint32_t and long" +#endif + +namespace android { +namespace dvr { + +struct DefaultRingTraits { + // Set this to false to allow compatibly expanding the record size. + static constexpr bool kUseStaticRecordSize = false; + + // Set this to a nonzero value to fix the number of records in the ring. + static constexpr uint32_t kStaticRecordCount = 0; + + // Set this to the max number of records that can be written simultaneously. + static constexpr uint32_t kMaxReservedRecords = 1; + + // Set this to the min number of records that must be readable. + static constexpr uint32_t kMinAvailableRecords = 1; +}; + +// Nonblocking ring suitable for concurrent single-writer, multi-reader access. +// +// Readers never block the writer and thus this is a nondeterministically lossy +// transport in the absence of external synchronization. Don't use this as a +// transport when deterministic behavior is required. +// +// Readers may have a read-only mapping; each reader's state is a single local +// sequence number. +// +// The implementation takes care to avoid data races on record access. +// Inconsistent data can only be returned if at least 2^32 records are written +// during the read-side critical section. +// +// In addition, both readers and the writer are careful to avoid accesses +// outside the bounds of the mmap area passed in during initialization even if +// there is a misbehaving or malicious task with write access to the mmap area. +// +// When dynamic record size is enabled, readers use the record size in the ring +// header when indexing the ring, so that it is possible to extend the record +// type without breaking the read-side ABI. +// +// Avoid calling Put() in a tight loop; there should be significantly more time +// between successive puts than it takes to read one record from memory to +// ensure Get() completes quickly. This requirement should not be difficult to +// achieve for most practical uses; 4kB puts at 10,000Hz is well below the +// scaling limit on current mobile chips. +// +// Example Writer Usage: +// +// using Record = MyRecordType; +// using Ring = BroadcastRing<Record>; +// +// uint32_t record_count = kMyDesiredCount; +// uint32_t ring_size = Ring::MemorySize(record_count); +// +// size_t page_size = sysconf(_SC_PAGESIZE); +// uint32_t mmap_size = (ring_size + (page_size - 1)) & ~(page_size - 1); +// +// // Allocate & map via your preferred mechanism, e.g. +// int fd = open("/dev/shm/ring_test", O_CREAT|O_RDWR|O_CLOEXEC, 0600); +// CHECK(fd >= 0); +// CHECK(!ftruncate(fd, ring_size)); +// void *mmap_base = mmap(nullptr, mmap_size, PROT_READ|PROT_WRITE, +// MAP_SHARED, fd, 0); +// CHECK(mmap_base != MAP_FAILED); +// close(fd); +// +// Ring ring = Ring::Create(mmap_base, mmap_size, record_count); +// +// while (!done) +// ring.Put(BuildNextRecordBlocking()); +// +// CHECK(!munmap(mmap_base, mmap_size)); +// +// Example Reader Usage: +// +// using Record = MyRecordType; +// using Ring = BroadcastRing<Record>; +// +// // Map via your preferred mechanism, e.g. +// int fd = open("/dev/shm/ring_test", O_RDONLY|O_CLOEXEC); +// CHECK(fd >= 0); +// struct stat st; +// CHECK(!fstat(fd, &st)); +// size_t mmap_size = st.st_size; +// void *mmap_base = mmap(nullptr, mmap_size, PROT_READ, +// MAP_SHARED, fd, 0); +// CHECK(mmap_base != MAP_FAILED); +// close(fd); +// +// Ring ring; +// bool import_ok; +// std::tie(ring, import_ok) = Ring::Import(mmap_base, mmap_size); +// CHECK(import_ok); +// +// uint32_t sequence; +// +// // Choose starting point (using "0" is unpredictable but not dangerous) +// sequence = ring.GetOldestSequence(); // The oldest available +// sequence = ring.GetNewestSequence(); // The newest available +// sequence = ring.GetNextSequence(); // The next one produced +// +// while (!done) { +// Record record; +// +// if (you_want_to_process_all_available_records) { +// while (ring.Get(&sequence, &record)) { +// ProcessRecord(sequence, record); +// sequence++; +// } +// } else if (you_want_to_skip_to_the_newest_record) { +// if (ring.GetNewest(&sequence, &record)) { +// ProcessRecord(sequence, record); +// sequence++; +// } +// } +// +// DoSomethingExpensiveOrBlocking(); +// } +// +// CHECK(!munmap(mmap_base, mmap_size)); +// +template <typename RecordType, typename BaseTraits = DefaultRingTraits> +class BroadcastRing { + public: + using Record = RecordType; + struct Traits : public BaseTraits { + // Must have enough space for writers, plus enough space for readers. + static constexpr int kMinRecordCount = + BaseTraits::kMaxReservedRecords + BaseTraits::kMinAvailableRecords; + + // Count of zero means dynamic, non-zero means static. + static constexpr bool kUseStaticRecordCount = + (BaseTraits::kStaticRecordCount != 0); + + // If both record size and count are static then the overall size is too. + static constexpr bool kIsStaticSize = + BaseTraits::kUseStaticRecordSize && kUseStaticRecordCount; + }; + + static constexpr bool IsPowerOfTwo(uint32_t size) { + return (size & (size - 1)) == 0; + } + + // Sanity check the options provided in Traits. + static_assert(Traits::kMinRecordCount >= 1, "Min record count too small"); + static_assert(!Traits::kUseStaticRecordCount || + Traits::kStaticRecordCount >= Traits::kMinRecordCount, + "Static record count is too small"); + static_assert(!Traits::kStaticRecordCount || + IsPowerOfTwo(Traits::kStaticRecordCount), + "Static record count is not a power of two"); + static_assert(std::is_standard_layout<Record>::value, + "Record type must be standard layout"); + + BroadcastRing() {} + + // Creates a new ring at |mmap| with |record_count| records. + // + // There must be at least |MemorySize(record_count)| bytes of space already + // allocated at |mmap|. The ring does not take ownership. + // + // Use this function for dynamically sized rings. + static BroadcastRing Create(void* mmap, size_t mmap_size, + uint32_t record_count) { + BroadcastRing ring(mmap); + CHECK(ring.ValidateGeometry(mmap_size, sizeof(Record), record_count)); + ring.InitializeHeader(sizeof(Record), record_count); + return ring; + } + + // Creates a new ring at |mmap|. + // + // There must be at least |MemorySize()| bytes of space already allocated at + // |mmap|. The ring does not take ownership. + // + // Use this function for statically sized rings. + static BroadcastRing Create(void* mmap, size_t mmap_size) { + static_assert(Traits::kUseStaticRecordCount, + "Wrong Create() function called for dynamic record count"); + return Create(mmap, mmap_size, Traits::kStaticRecordCount); + } + + // Imports an existing ring at |mmap|. + // + // Import may fail if the ring parameters in the mmap header are not sensible. + // In this case the returned boolean is false; make sure to check this value. + static std::tuple<BroadcastRing, bool> Import(void* mmap, size_t mmap_size) { + BroadcastRing ring(mmap); + uint32_t record_size = 0; + uint32_t record_count = 0; + if (mmap_size >= sizeof(Header)) { + record_size = std::atomic_load_explicit(&ring.header_mmap()->record_size, + std::memory_order_relaxed); + record_count = std::atomic_load_explicit( + &ring.header_mmap()->record_count, std::memory_order_relaxed); + } + bool ok = ring.ValidateGeometry(mmap_size, record_size, record_count); + return std::make_tuple(ring, ok); + } + + ~BroadcastRing() {} + + // Calculates the space necessary for a ring of size |record_count|. + // + // Use this function for dynamically sized rings. + static constexpr size_t MemorySize(uint32_t record_count) { + return sizeof(Header) + sizeof(Record) * record_count; + } + + // Calculates the space necessary for a statically sized ring. + // + // Use this function for statically sized rings. + static constexpr size_t MemorySize() { + static_assert( + Traits::kUseStaticRecordCount, + "Wrong MemorySize() function called for dynamic record count"); + return MemorySize(Traits::kStaticRecordCount); + } + + // Writes a record to the ring. + // + // The oldest record is overwritten unless the ring is not already full. + void Put(const Record& record) { + const int kRecordCount = 1; + Reserve(kRecordCount); + Geometry geometry = GetGeometry(); + PutRecordInternal(&record, record_mmap_writer(geometry.tail_index)); + Publish(kRecordCount); + } + + // Gets sequence number of the oldest currently available record. + uint32_t GetOldestSequence() const { + return std::atomic_load_explicit(&header_mmap()->head, + std::memory_order_relaxed); + } + + // Gets sequence number of the first future record. + // + // If the returned value is passed to Get() and there is no concurrent Put(), + // Get() will return false. + uint32_t GetNextSequence() const { + return std::atomic_load_explicit(&header_mmap()->tail, + std::memory_order_relaxed); + } + + // Gets sequence number of the newest currently available record. + uint32_t GetNewestSequence() const { return GetNextSequence() - 1; } + + // Copies the oldest available record with sequence at least |*sequence| to + // |record|. + // + // Returns false if there is no recent enough record available. + // + // Updates |*sequence| with the sequence number of the record returned. To get + // the following record, increment this number by one. + // + // This function synchronizes with two other operations: + // + // (1) Load-Acquire of |tail| + // + // Together with the store-release in Publish(), this load-acquire + // ensures each store to a record in PutRecordInternal() happens-before + // any corresponding load in GetRecordInternal(). + // + // i.e. the stores for the records with sequence numbers < |tail| have + // completed from our perspective + // + // (2) Acquire Fence between record access & final load of |head| + // + // Together with the release fence in Reserve(), this ensures that if + // GetRecordInternal() loads a value stored in some execution of + // PutRecordInternal(), then the store of |head| in the Reserve() that + // preceeded it happens-before our final load of |head|. + // + // i.e. if we read a record with sequence number >= |final_head| then + // no later store to that record has completed from our perspective + bool Get(uint32_t* sequence /*inout*/, Record* record /*out*/) const { + for (;;) { + uint32_t tail = std::atomic_load_explicit(&header_mmap()->tail, + std::memory_order_acquire); + uint32_t head = std::atomic_load_explicit(&header_mmap()->head, + std::memory_order_relaxed); + + if (tail - head > record_count()) + continue; // Concurrent modification; re-try. + + if (*sequence - head > tail - head) + *sequence = head; // Out of window, skip forward to first available. + + if (*sequence == tail) return false; // No new records available. + + Geometry geometry = + CalculateGeometry(record_count(), record_size(), *sequence, tail); + + // Compute address explicitly in case record_size > sizeof(Record). + RecordStorage* record_storage = record_mmap_reader(geometry.head_index); + + GetRecordInternal(record_storage, record); + + // NB: It is not sufficient to change this to a load-acquire of |head|. + std::atomic_thread_fence(std::memory_order_acquire); + + uint32_t final_head = std::atomic_load_explicit( + &header_mmap()->head, std::memory_order_relaxed); + + if (final_head - head > *sequence - head) + continue; // Concurrent modification; re-try. + + // Note: Combining the above 4 comparisons gives: + // 0 <= final_head - head <= sequence - head < tail - head <= record_count + // + // We can also write this as: + // head <=* final_head <=* sequence <* tail <=* head + record_count + // + // where <* orders by difference from head: x <* y if x - head < y - head. + // This agrees with the order of sequence updates during "put" operations. + return true; + } + } + + // Copies the newest available record with sequence at least |*sequence| to + // |record|. + // + // Returns false if there is no recent enough record available. + // + // Updates |*sequence| with the sequence number of the record returned. To get + // the following record, increment this number by one. + bool GetNewest(uint32_t* sequence, Record* record) const { + uint32_t newest_sequence = GetNewestSequence(); + if (*sequence == newest_sequence + 1) return false; + *sequence = newest_sequence; + return Get(sequence, record); + } + + uint32_t record_count() const { return record_count_internal(); } + uint32_t record_size() const { return record_size_internal(); } + static constexpr uint32_t mmap_alignment() { return alignof(Mmap); } + + private: + struct Header { + // Record size for reading out of the ring. Writers always write the full + // length; readers may need to read a prefix of each record. + std::atomic<uint32_t> record_size; + + // Number of records in the ring. + std::atomic<uint32_t> record_count; + + // Readable region is [head % record_count, tail % record_count). + // + // The region in [tail % record_count, head % record_count) was either never + // populated or is being updated. + // + // These are sequences numbers, not indexes - indexes should be computed + // with a modulus. + // + // To ensure consistency: + // + // (1) Writes advance |head| past any updated records before writing to + // them, and advance |tail| after they are written. + // (2) Readers check |tail| before reading data and |head| after, + // making sure to discard any data that was written to concurrently. + std::atomic<uint32_t> head; + std::atomic<uint32_t> tail; + }; + + // Store using the standard word size. + using StorageType = long; // NOLINT + + // Always require 8 byte alignment so that the same record sizes are legal on + // 32 and 64 bit builds. + static constexpr size_t kRecordAlignment = 8; + static_assert(kRecordAlignment % sizeof(StorageType) == 0, + "Bad record alignment"); + + struct RecordStorage { + // This is accessed with relaxed atomics to prevent data races on the + // contained data, which would be undefined behavior. + std::atomic<StorageType> data[sizeof(Record) / sizeof(StorageType)]; + }; + + static_assert(sizeof(StorageType) * + std::extent<decltype(RecordStorage::data)>() == + sizeof(Record), + "Record length must be a multiple of sizeof(StorageType)"); + + struct Geometry { + // Static geometry. + uint32_t record_count; + uint32_t record_size; + + // Copy of atomic sequence counts. + uint32_t head; + uint32_t tail; + + // First index of readable region. + uint32_t head_index; + + // First index of writable region. + uint32_t tail_index; + + // Number of records in readable region. + uint32_t count; + + // Number of records in writable region. + uint32_t space; + }; + + // Mmap area layout. + // + // Readers should not index directly into |records| as this is not valid when + // dynamic record sizes are used; use record_mmap_reader() instead. + struct Mmap { + Header header; + RecordStorage records[]; + }; + + static_assert(std::is_standard_layout<Mmap>::value, + "Mmap must be standard layout"); + static_assert(sizeof(std::atomic<uint32_t>) == sizeof(uint32_t), + "Lockless atomics contain extra state"); + static_assert(sizeof(std::atomic<StorageType>) == sizeof(StorageType), + "Lockless atomics contain extra state"); + + explicit BroadcastRing(void* mmap) { + CHECK_EQ(0U, reinterpret_cast<uintptr_t>(mmap) % alignof(Mmap)); + data_.mmap = reinterpret_cast<Mmap*>(mmap); + } + + // Initializes the mmap area header for a new ring. + void InitializeHeader(uint32_t record_size, uint32_t record_count) { + constexpr uint32_t kInitialSequence = -256; // Force an early wrap. + std::atomic_store_explicit(&header_mmap()->record_size, record_size, + std::memory_order_relaxed); + std::atomic_store_explicit(&header_mmap()->record_count, record_count, + std::memory_order_relaxed); + std::atomic_store_explicit(&header_mmap()->head, kInitialSequence, + std::memory_order_relaxed); + std::atomic_store_explicit(&header_mmap()->tail, kInitialSequence, + std::memory_order_relaxed); + } + + // Validates ring geometry. + // + // Ring geometry is validated carefully on import and then cached. This allows + // us to avoid out-of-range accesses even if the parameters in the header are + // later changed. + bool ValidateGeometry(size_t mmap_size, uint32_t header_record_size, + uint32_t header_record_count) { + set_record_size(header_record_size); + set_record_count(header_record_count); + + if (record_size() != header_record_size) return false; + if (record_count() != header_record_count) return false; + if (record_count() < Traits::kMinRecordCount) return false; + if (record_size() < sizeof(Record)) return false; + if (record_size() % kRecordAlignment != 0) return false; + if (!IsPowerOfTwo(record_count())) return false; + + size_t memory_size = record_count() * record_size(); + if (memory_size / record_size() != record_count()) return false; + if (memory_size + sizeof(Header) < memory_size) return false; + if (memory_size + sizeof(Header) > mmap_size) return false; + + return true; + } + + // Copies a record into the ring. + // + // This is done with relaxed atomics because otherwise it is racy according to + // the C++ memory model. This is very low overhead once optimized. + static inline void PutRecordInternal(const Record* in, RecordStorage* out) { + StorageType data[sizeof(Record) / sizeof(StorageType)]; + memcpy(data, in, sizeof(*in)); + for (size_t i = 0; i < std::extent<decltype(data)>(); ++i) { + std::atomic_store_explicit(&out->data[i], data[i], + std::memory_order_relaxed); + } + } + + // Copies a record out of the ring. + // + // This is done with relaxed atomics because otherwise it is racy according to + // the C++ memory model. This is very low overhead once optimized. + static inline void GetRecordInternal(RecordStorage* in, Record* out) { + StorageType data[sizeof(Record) / sizeof(StorageType)]; + for (size_t i = 0; i < std::extent<decltype(data)>(); ++i) { + data[i] = + std::atomic_load_explicit(&in->data[i], std::memory_order_relaxed); + } + memcpy(out, &data, sizeof(*out)); + } + + // Converts a record's sequence number into a storage index. + static uint32_t SequenceToIndex(uint32_t sequence, uint32_t record_count) { + return sequence & (record_count - 1); + } + + // Computes readable & writable ranges from ring parameters. + static Geometry CalculateGeometry(uint32_t record_count, uint32_t record_size, + uint32_t head, uint32_t tail) { + Geometry geometry; + geometry.record_count = record_count; + geometry.record_size = record_size; + DCHECK_EQ(0U, geometry.record_size % kRecordAlignment); + geometry.head = head; + geometry.tail = tail; + geometry.head_index = SequenceToIndex(head, record_count); + geometry.tail_index = SequenceToIndex(tail, record_count); + geometry.count = geometry.tail - geometry.head; + DCHECK_LE(geometry.count, record_count); + geometry.space = geometry.record_count - geometry.count; + return geometry; + } + + // Gets the current ring readable & writable regions. + // + // This this is always safe from the writing thread since it is the only + // thread allowed to update the header. + Geometry GetGeometry() const { + return CalculateGeometry( + record_count(), record_size(), + std::atomic_load_explicit(&header_mmap()->head, + std::memory_order_relaxed), + std::atomic_load_explicit(&header_mmap()->tail, + std::memory_order_relaxed)); + } + + // Makes space for at least |reserve_count| records. + // + // There is nothing to prevent overwriting records that have concurrent + // readers. We do however ensure that this situation can be detected: the + // fence ensures the |head| update will be the first update seen by readers, + // and readers check this value after reading and discard data that may have + // been concurrently modified. + void Reserve(uint32_t reserve_count) { + Geometry geometry = GetGeometry(); + DCHECK_LE(reserve_count, Traits::kMaxReservedRecords); + uint32_t needed = + (geometry.space >= reserve_count ? 0 : reserve_count - geometry.space); + + std::atomic_store_explicit(&header_mmap()->head, geometry.head + needed, + std::memory_order_relaxed); + + // NB: It is not sufficient to change this to a store-release of |head|. + std::atomic_thread_fence(std::memory_order_release); + } + + // Makes |publish_count| records visible to readers. + // + // Space must have been reserved by a previous call to Reserve(). + void Publish(uint32_t publish_count) { + Geometry geometry = GetGeometry(); + DCHECK_LE(publish_count, geometry.space); + std::atomic_store_explicit(&header_mmap()->tail, + geometry.tail + publish_count, + std::memory_order_release); + } + + // Helpers to compute addresses in mmap area. + Mmap* mmap() const { return data_.mmap; } + Header* header_mmap() const { return &data_.mmap->header; } + RecordStorage* record_mmap_writer(uint32_t index) const { + DCHECK_EQ(sizeof(Record), record_size()); + return &data_.mmap->records[index]; + } + RecordStorage* record_mmap_reader(uint32_t index) const { + if (Traits::kUseStaticRecordSize) { + return &data_.mmap->records[index]; + } else { + // Calculate the location of a record in the ring without assuming that + // sizeof(Record) == record_size. + return reinterpret_cast<RecordStorage*>( + reinterpret_cast<char*>(data_.mmap->records) + index * record_size()); + } + } + + // The following horrifying template gunk enables us to store just the mmap + // base pointer for compile-time statically sized rings. Dynamically sized + // rings also store the validated copy of the record size & count. + // + // This boils down to: use a compile time constant if available, and otherwise + // load the value that was validated on import from a member variable. + template <typename T = Traits> + typename std::enable_if<T::kUseStaticRecordSize, uint32_t>::type + record_size_internal() const { + return sizeof(Record); + } + + template <typename T = Traits> + typename std::enable_if<!T::kUseStaticRecordSize, uint32_t>::type + record_size_internal() const { + return data_.record_size; + } + + template <typename T = Traits> + typename std::enable_if<T::kUseStaticRecordSize, void>::type set_record_size( + uint32_t /*record_size*/) {} + + template <typename T = Traits> + typename std::enable_if<!T::kUseStaticRecordSize, void>::type set_record_size( + uint32_t record_size) { + data_.record_size = record_size; + } + + template <typename T = Traits> + typename std::enable_if<T::kUseStaticRecordCount, uint32_t>::type + record_count_internal() const { + return Traits::kStaticRecordCount; + } + + template <typename T = Traits> + typename std::enable_if<!T::kUseStaticRecordCount, uint32_t>::type + record_count_internal() const { + return data_.record_count; + } + + template <typename T = Traits> + typename std::enable_if<T::kUseStaticRecordCount, void>::type + set_record_count(uint32_t /*record_count*/) const {} + + template <typename T = Traits> + typename std::enable_if<!T::kUseStaticRecordCount, void>::type + set_record_count(uint32_t record_count) { + data_.record_count = record_count; + } + + // Data we need to store for statically sized rings. + struct DataStaticSize { + Mmap* mmap = nullptr; + }; + + // Data we need to store for dynamically sized rings. + struct DataDynamicSize { + Mmap* mmap = nullptr; + + // These are cached to make sure misbehaving writers cannot cause + // out-of-bounds memory accesses by updating the values in the mmap header. + uint32_t record_size = 0; + uint32_t record_count = 0; + }; + + using DataStaticOrDynamic = + typename std::conditional<Traits::kIsStaticSize, DataStaticSize, + DataDynamicSize>::type; + + DataStaticOrDynamic data_; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_BROADCAST_RING_H_ diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp new file mode 100644 index 0000000000..452bad0bce --- /dev/null +++ b/libs/vr/libbufferhub/Android.bp @@ -0,0 +1,58 @@ +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +sourceFiles = [ + "buffer_hub_client.cpp", + "buffer_hub_rpc.cpp", + "ion_buffer.cpp", +] + +localIncludeFiles = [ + "include", +] + +staticLibraries = [ + "libdvrcommon", + "libpdx_default_transport", +] + +sharedLibraries = [ + "libbase", + "libcutils", + "libhardware", + "liblog", + "libui", + "libutils", +] + +cc_library { + srcs: sourceFiles, + cflags: [ + "-DLOG_TAG=\"libbufferhub\"", + "-DTRACE=0" + ], + export_include_dirs: localIncludeFiles, + static_libs: staticLibraries, + shared_libs: sharedLibraries, + name: "libbufferhub", +} + +cc_test { + tags: ["optional"], + srcs: ["bufferhub_tests.cpp"], + static_libs: ["libbufferhub"] + staticLibraries, + shared_libs: sharedLibraries, + name: "bufferhub_tests", +} + diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp new file mode 100644 index 0000000000..b9a53b0ced --- /dev/null +++ b/libs/vr/libbufferhub/buffer_hub_client.cpp @@ -0,0 +1,412 @@ +#include <private/dvr/buffer_hub_client.h> + +#include <log/log.h> +#include <poll.h> +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <utils/Trace.h> + +#include <mutex> + +#include <pdx/default_transport/client_channel.h> +#include <pdx/default_transport/client_channel_factory.h> + +#include "include/private/dvr/bufferhub_rpc.h" + +using android::pdx::LocalHandle; +using android::pdx::LocalChannelHandle; +using android::pdx::rpc::WrapBuffer; +using android::pdx::Status; + +namespace android { +namespace dvr { + +BufferHubBuffer::BufferHubBuffer(LocalChannelHandle channel_handle) + : Client{pdx::default_transport::ClientChannel::Create( + std::move(channel_handle))}, + id_(-1) {} +BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path) + : Client{pdx::default_transport::ClientChannelFactory::Create( + endpoint_path)}, + id_(-1) {} + +BufferHubBuffer::~BufferHubBuffer() {} + +Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() { + Status<LocalChannelHandle> status = + InvokeRemoteMethod<BufferHubRPC::NewConsumer>(); + ALOGE_IF(!status, + "BufferHub::CreateConsumer: Failed to create consumer channel: %s", + status.GetErrorMessage().c_str()); + return status; +} + +int BufferHubBuffer::ImportBuffer() { + ATRACE_NAME("BufferHubBuffer::ImportBuffer"); + + Status<NativeBufferHandle<LocalHandle>> status = + InvokeRemoteMethod<BufferHubRPC::GetBuffer>(); + if (!status) { + ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffer: %s", + status.GetErrorMessage().c_str()); + return -status.error(); + } else if (status.get().id() < 0) { + ALOGE("BufferHubBuffer::ImportBuffer: Received an invalid id!"); + return -EIO; + } + + auto buffer_handle = status.take(); + + // Stash the buffer id to replace the value in id_. + const int new_id = buffer_handle.id(); + + // Import the buffer. + IonBuffer ion_buffer; + ALOGD_IF( + TRACE, "BufferHubBuffer::ImportBuffer: id=%d FdCount=%zu IntCount=%zu", + buffer_handle.id(), buffer_handle.FdCount(), buffer_handle.IntCount()); + + const int ret = buffer_handle.Import(&ion_buffer); + if (ret < 0) + return ret; + + // If the import succeeds, replace the previous buffer and id. + buffer_ = std::move(ion_buffer); + id_ = new_id; + return 0; +} + +int BufferHubBuffer::Poll(int timeout_ms) { + ATRACE_NAME("BufferHubBuffer::Poll"); + pollfd p = {event_fd(), POLLIN, 0}; + return poll(&p, 1, timeout_ms); +} + +int BufferHubBuffer::Lock(int usage, int x, int y, int width, int height, + void** address) { + return buffer_.Lock(usage, x, y, width, height, address); +} + +int BufferHubBuffer::Unlock() { return buffer_.Unlock(); } + +int BufferHubBuffer::GetBlobReadWritePointer(size_t size, void** addr) { + int width = static_cast<int>(size); + int height = 1; + int ret = Lock(usage(), 0, 0, width, height, addr); + if (ret == 0) + Unlock(); + return ret; +} + +int BufferHubBuffer::GetBlobReadOnlyPointer(size_t size, void** addr) { + return GetBlobReadWritePointer(size, addr); +} + +void BufferHubBuffer::GetBlobFds(int* fds, size_t* fds_count, + size_t max_fds_count) const { + size_t numFds = static_cast<size_t>(native_handle()->numFds); + *fds_count = std::min(max_fds_count, numFds); + std::copy(native_handle()->data, native_handle()->data + *fds_count, fds); +} + +BufferConsumer::BufferConsumer(LocalChannelHandle channel) + : BASE(std::move(channel)) { + const int ret = ImportBuffer(); + if (ret < 0) { + ALOGE("BufferConsumer::BufferConsumer: Failed to import buffer: %s", + strerror(-ret)); + Close(ret); + } +} + +std::unique_ptr<BufferConsumer> BufferConsumer::Import( + LocalChannelHandle channel) { + ATRACE_NAME("BufferConsumer::Import"); + ALOGD_IF(TRACE, "BufferConsumer::Import: channel=%d", channel.value()); + return BufferConsumer::Create(std::move(channel)); +} + +std::unique_ptr<BufferConsumer> BufferConsumer::Import( + Status<LocalChannelHandle> status) { + return Import(status ? status.take() + : LocalChannelHandle{nullptr, -status.error()}); +} + +int BufferConsumer::Acquire(LocalHandle* ready_fence) { + return Acquire(ready_fence, nullptr, 0); +} + +int BufferConsumer::Acquire(LocalHandle* ready_fence, void* meta, + size_t meta_size_bytes) { + ATRACE_NAME("BufferConsumer::Acquire"); + LocalFence fence; + auto return_value = + std::make_pair(std::ref(fence), WrapBuffer(meta, meta_size_bytes)); + auto status = InvokeRemoteMethodInPlace<BufferHubRPC::ConsumerAcquire>( + &return_value, meta_size_bytes); + if (status && ready_fence) + *ready_fence = fence.take(); + return status ? 0 : -status.error(); +} + +int BufferConsumer::Release(const LocalHandle& release_fence) { + ATRACE_NAME("BufferConsumer::Release"); + return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>( + BorrowedFence(release_fence.Borrow()))); +} + +int BufferConsumer::ReleaseAsync() { + ATRACE_NAME("BufferConsumer::ReleaseAsync"); + return ReturnStatusOrError( + SendImpulse(BufferHubRPC::ConsumerRelease::Opcode)); +} + +int BufferConsumer::Discard() { return Release(LocalHandle()); } + +int BufferConsumer::SetIgnore(bool ignore) { + return ReturnStatusOrError( + InvokeRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(ignore)); +} + +BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format, + uint32_t usage, size_t metadata_size) + : BufferProducer(width, height, format, usage, usage, metadata_size) {} + +BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format, + uint64_t producer_usage, uint64_t consumer_usage, + size_t metadata_size) + : BASE(BufferHubRPC::kClientPath) { + ATRACE_NAME("BufferProducer::BufferProducer"); + ALOGD_IF(TRACE, + "BufferProducer::BufferProducer: fd=%d width=%u height=%u format=%u " + "producer_usage=%" PRIx64 " consumer_usage=%" PRIx64 + " metadata_size=%zu", + event_fd(), width, height, format, producer_usage, consumer_usage, + metadata_size); + + // (b/37881101) Deprecate producer/consumer usage + auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>( + width, height, format, (producer_usage | consumer_usage), metadata_size); + if (!status) { + ALOGE( + "BufferProducer::BufferProducer: Failed to create producer buffer: %s", + status.GetErrorMessage().c_str()); + Close(-status.error()); + return; + } + + const int ret = ImportBuffer(); + if (ret < 0) { + ALOGE( + "BufferProducer::BufferProducer: Failed to import producer buffer: %s", + strerror(-ret)); + Close(ret); + } +} + +BufferProducer::BufferProducer(const std::string& name, int user_id, + int group_id, uint32_t width, uint32_t height, + uint32_t format, uint32_t usage, + size_t meta_size_bytes) + : BufferProducer(name, user_id, group_id, width, height, format, usage, + usage, meta_size_bytes) {} + +BufferProducer::BufferProducer(const std::string& name, int user_id, + int group_id, uint32_t width, uint32_t height, + uint32_t format, uint64_t producer_usage, + uint64_t consumer_usage, size_t meta_size_bytes) + : BASE(BufferHubRPC::kClientPath) { + ATRACE_NAME("BufferProducer::BufferProducer"); + ALOGD_IF(TRACE, + "BufferProducer::BufferProducer: fd=%d name=%s user_id=%d " + "group_id=%d width=%u height=%u format=%u producer_usage=%" PRIx64 + " consumer_usage=%" PRIx64 " meta_size_bytes=%zu", + event_fd(), name.c_str(), user_id, group_id, width, height, format, + producer_usage, consumer_usage, meta_size_bytes); + + // (b/37881101) Deprecate producer/consumer usage + auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>( + name, user_id, group_id, width, height, format, + (producer_usage | consumer_usage), meta_size_bytes); + if (!status) { + ALOGE( + "BufferProducer::BufferProducer: Failed to create/get persistent " + "buffer \"%s\": %s", + name.c_str(), status.GetErrorMessage().c_str()); + Close(-status.error()); + return; + } + + const int ret = ImportBuffer(); + if (ret < 0) { + ALOGE( + "BufferProducer::BufferProducer: Failed to import producer buffer " + "\"%s\": %s", + name.c_str(), strerror(-ret)); + Close(ret); + } +} + +BufferProducer::BufferProducer(uint32_t usage, size_t size) + : BufferProducer(usage, usage, size) {} + +BufferProducer::BufferProducer(uint64_t producer_usage, uint64_t consumer_usage, + size_t size) + : BASE(BufferHubRPC::kClientPath) { + ATRACE_NAME("BufferProducer::BufferProducer"); + ALOGD_IF(TRACE, + "BufferProducer::BufferProducer: producer_usage=%" PRIx64 + " consumer_usage=%" PRIx64 " size=%zu", + producer_usage, consumer_usage, size); + const int width = static_cast<int>(size); + const int height = 1; + const int format = HAL_PIXEL_FORMAT_BLOB; + const size_t meta_size_bytes = 0; + + // (b/37881101) Deprecate producer/consumer usage + auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>( + width, height, format, (producer_usage | consumer_usage), + meta_size_bytes); + if (!status) { + ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s", + status.GetErrorMessage().c_str()); + Close(-status.error()); + return; + } + + const int ret = ImportBuffer(); + if (ret < 0) { + ALOGE( + "BufferProducer::BufferProducer: Failed to import producer buffer: %s", + strerror(-ret)); + Close(ret); + } +} + +BufferProducer::BufferProducer(const std::string& name, int user_id, + int group_id, uint32_t usage, size_t size) + : BufferProducer(name, user_id, group_id, usage, usage, size) {} + +BufferProducer::BufferProducer(const std::string& name, int user_id, + int group_id, uint64_t producer_usage, + uint64_t consumer_usage, size_t size) + : BASE(BufferHubRPC::kClientPath) { + ATRACE_NAME("BufferProducer::BufferProducer"); + ALOGD_IF(TRACE, + "BufferProducer::BufferProducer: name=%s user_id=%d group=%d " + "producer_usage=%" PRIx64 " consumer_usage=%" PRIx64 " size=%zu", + name.c_str(), user_id, group_id, producer_usage, consumer_usage, + size); + const int width = static_cast<int>(size); + const int height = 1; + const int format = HAL_PIXEL_FORMAT_BLOB; + const size_t meta_size_bytes = 0; + + // (b/37881101) Deprecate producer/consumer usage + auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>( + name, user_id, group_id, width, height, format, + (producer_usage | consumer_usage), meta_size_bytes); + if (!status) { + ALOGE( + "BufferProducer::BufferProducer: Failed to create persistent " + "buffer \"%s\": %s", + name.c_str(), status.GetErrorMessage().c_str()); + Close(-status.error()); + return; + } + + const int ret = ImportBuffer(); + if (ret < 0) { + ALOGE( + "BufferProducer::BufferProducer: Failed to import producer buffer " + "\"%s\": %s", + name.c_str(), strerror(-ret)); + Close(ret); + } +} + +BufferProducer::BufferProducer(const std::string& name) + : BASE(BufferHubRPC::kClientPath) { + ATRACE_NAME("BufferProducer::BufferProducer"); + ALOGD_IF(TRACE, "BufferProducer::BufferProducer: name=%s", name.c_str()); + + auto status = InvokeRemoteMethod<BufferHubRPC::GetPersistentBuffer>(name); + if (!status) { + ALOGE( + "BufferProducer::BufferProducer: Failed to get producer buffer by name " + "\"%s\": %s", + name.c_str(), status.GetErrorMessage().c_str()); + Close(-status.error()); + return; + } + + const int ret = ImportBuffer(); + if (ret < 0) { + ALOGE( + "BufferProducer::BufferProducer: Failed to import producer buffer " + "\"%s\": %s", + name.c_str(), strerror(-ret)); + Close(ret); + } +} + +BufferProducer::BufferProducer(LocalChannelHandle channel) + : BASE(std::move(channel)) { + const int ret = ImportBuffer(); + if (ret < 0) { + ALOGE( + "BufferProducer::BufferProducer: Failed to import producer buffer: %s", + strerror(-ret)); + Close(ret); + } +} + +int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta, + size_t meta_size_bytes) { + ATRACE_NAME("BufferProducer::Post"); + return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>( + BorrowedFence(ready_fence.Borrow()), WrapBuffer(meta, meta_size_bytes))); +} + +int BufferProducer::Gain(LocalHandle* release_fence) { + ATRACE_NAME("BufferProducer::Gain"); + auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>(); + if (!status) + return -status.error(); + if (release_fence) + *release_fence = status.take().take(); + return 0; +} + +int BufferProducer::GainAsync() { + ATRACE_NAME("BufferProducer::GainAsync"); + return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode)); +} + +std::unique_ptr<BufferProducer> BufferProducer::Import( + LocalChannelHandle channel) { + ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value()); + return BufferProducer::Create(std::move(channel)); +} + +std::unique_ptr<BufferProducer> BufferProducer::Import( + Status<LocalChannelHandle> status) { + return Import(status ? status.take() + : LocalChannelHandle{nullptr, -status.error()}); +} + +int BufferProducer::MakePersistent(const std::string& name, int user_id, + int group_id) { + ATRACE_NAME("BufferProducer::MakePersistent"); + return ReturnStatusOrError( + InvokeRemoteMethod<BufferHubRPC::ProducerMakePersistent>(name, user_id, + group_id)); +} + +int BufferProducer::RemovePersistence() { + ATRACE_NAME("BufferProducer::RemovePersistence"); + return ReturnStatusOrError( + InvokeRemoteMethod<BufferHubRPC::ProducerRemovePersistence>()); +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libbufferhub/buffer_hub_rpc.cpp b/libs/vr/libbufferhub/buffer_hub_rpc.cpp new file mode 100644 index 0000000000..9a67faa8aa --- /dev/null +++ b/libs/vr/libbufferhub/buffer_hub_rpc.cpp @@ -0,0 +1,9 @@ +#include "include/private/dvr/bufferhub_rpc.h" + +namespace android { +namespace dvr { + +constexpr char BufferHubRPC::kClientPath[]; + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libbufferhub/bufferhub_tests.cpp b/libs/vr/libbufferhub/bufferhub_tests.cpp new file mode 100644 index 0000000000..fa61c4a215 --- /dev/null +++ b/libs/vr/libbufferhub/bufferhub_tests.cpp @@ -0,0 +1,226 @@ +#include <gtest/gtest.h> +#include <private/dvr/buffer_hub_client.h> + +#include <mutex> +#include <thread> + +#define RETRY_EINTR(fnc_call) \ + ([&]() -> decltype(fnc_call) { \ + decltype(fnc_call) result; \ + do { \ + result = (fnc_call); \ + } while (result == -1 && errno == EINTR); \ + return result; \ + })() + +using android::dvr::BufferProducer; +using android::dvr::BufferConsumer; +using android::pdx::LocalHandle; + +const int kWidth = 640; +const int kHeight = 480; +const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888; +const int kUsage = 0; +const uint64_t kContext = 42; + +using LibBufferHubTest = ::testing::Test; + +TEST_F(LibBufferHubTest, TestBasicUsage) { + std::unique_ptr<BufferProducer> p = BufferProducer::Create( + kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); + ASSERT_TRUE(p.get() != nullptr); + std::unique_ptr<BufferConsumer> c = + BufferConsumer::Import(p->CreateConsumer()); + ASSERT_TRUE(c.get() != nullptr); + // Check that consumers can spawn other consumers. + std::unique_ptr<BufferConsumer> c2 = + BufferConsumer::Import(c->CreateConsumer()); + ASSERT_TRUE(c2.get() != nullptr); + + EXPECT_EQ(0, p->Post(LocalHandle(), kContext)); + // Both consumers should be triggered. + EXPECT_GE(0, RETRY_EINTR(p->Poll(0))); + EXPECT_LT(0, RETRY_EINTR(c->Poll(10))); + EXPECT_LT(0, RETRY_EINTR(c2->Poll(10))); + + uint64_t context; + LocalHandle fence; + EXPECT_LE(0, c->Acquire(&fence, &context)); + EXPECT_EQ(kContext, context); + EXPECT_GE(0, RETRY_EINTR(c->Poll(0))); + + EXPECT_LE(0, c2->Acquire(&fence, &context)); + EXPECT_EQ(kContext, context); + EXPECT_GE(0, RETRY_EINTR(c2->Poll(0))); + + EXPECT_EQ(0, c->Release(LocalHandle())); + EXPECT_GE(0, RETRY_EINTR(p->Poll(0))); + EXPECT_EQ(0, c2->Discard()); + + EXPECT_LE(0, RETRY_EINTR(p->Poll(0))); + EXPECT_EQ(0, p->Gain(&fence)); + EXPECT_GE(0, RETRY_EINTR(p->Poll(0))); +} + +TEST_F(LibBufferHubTest, TestWithCustomMetadata) { + struct Metadata { + int64_t field1; + int64_t field2; + }; + std::unique_ptr<BufferProducer> p = BufferProducer::Create( + kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); + ASSERT_TRUE(p.get() != nullptr); + std::unique_ptr<BufferConsumer> c = + BufferConsumer::Import(p->CreateConsumer()); + ASSERT_TRUE(c.get() != nullptr); + + Metadata m = {1, 3}; + EXPECT_EQ(0, p->Post(LocalHandle(), m)); + EXPECT_LE(0, RETRY_EINTR(c->Poll(10))); + + LocalHandle fence; + Metadata m2 = {}; + EXPECT_EQ(0, c->Acquire(&fence, &m2)); + EXPECT_EQ(m.field1, m2.field1); + EXPECT_EQ(m.field2, m2.field2); + + EXPECT_EQ(0, c->Release(LocalHandle())); + EXPECT_LT(0, RETRY_EINTR(p->Poll(0))); +} + +TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) { + struct Metadata { + int64_t field1; + int64_t field2; + }; + std::unique_ptr<BufferProducer> p = BufferProducer::Create( + kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); + ASSERT_TRUE(p.get() != nullptr); + std::unique_ptr<BufferConsumer> c = + BufferConsumer::Import(p->CreateConsumer()); + ASSERT_TRUE(c.get() != nullptr); + + int64_t sequence = 3; + EXPECT_NE(0, p->Post(LocalHandle(), sequence)); + EXPECT_GE(0, RETRY_EINTR(c->Poll(10))); +} + +TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) { + struct Metadata { + int64_t field1; + int64_t field2; + }; + std::unique_ptr<BufferProducer> p = BufferProducer::Create( + kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); + ASSERT_TRUE(p.get() != nullptr); + std::unique_ptr<BufferConsumer> c = + BufferConsumer::Import(p->CreateConsumer()); + ASSERT_TRUE(c.get() != nullptr); + + Metadata m = {1, 3}; + EXPECT_EQ(0, p->Post(LocalHandle(), m)); + + LocalHandle fence; + int64_t sequence; + EXPECT_NE(0, c->Acquire(&fence, &sequence)); +} + +TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) { + std::unique_ptr<BufferProducer> p = BufferProducer::Create( + kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); + ASSERT_TRUE(p.get() != nullptr); + std::unique_ptr<BufferConsumer> c = + BufferConsumer::Import(p->CreateConsumer()); + ASSERT_TRUE(c.get() != nullptr); + + int64_t sequence = 3; + EXPECT_EQ(0, p->Post(LocalHandle(), sequence)); + + LocalHandle fence; + EXPECT_EQ(0, c->Acquire(&fence)); +} + +TEST_F(LibBufferHubTest, TestWithNoMeta) { + std::unique_ptr<BufferProducer> p = + BufferProducer::Create(kWidth, kHeight, kFormat, kUsage); + ASSERT_TRUE(p.get() != nullptr); + std::unique_ptr<BufferConsumer> c = + BufferConsumer::Import(p->CreateConsumer()); + ASSERT_TRUE(c.get() != nullptr); + + LocalHandle fence; + + EXPECT_EQ(0, p->Post<void>(LocalHandle())); + EXPECT_EQ(0, c->Acquire(&fence)); +} + +TEST_F(LibBufferHubTest, TestFailureToPostMetaFromABufferWithoutMeta) { + std::unique_ptr<BufferProducer> p = + BufferProducer::Create(kWidth, kHeight, kFormat, kUsage); + ASSERT_TRUE(p.get() != nullptr); + std::unique_ptr<BufferConsumer> c = + BufferConsumer::Import(p->CreateConsumer()); + ASSERT_TRUE(c.get() != nullptr); + + int64_t sequence = 3; + EXPECT_NE(0, p->Post(LocalHandle(), sequence)); +} + +TEST_F(LibBufferHubTest, TestPersistentBufferPersistence) { + auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth, + kHeight, kFormat, kUsage); + ASSERT_NE(nullptr, p); + + // Record the original buffer id for later comparison. + const int buffer_id = p->id(); + + auto c = BufferConsumer::Import(p->CreateConsumer()); + ASSERT_NE(nullptr, c); + + EXPECT_EQ(0, p->Post<void>(LocalHandle())); + + // Close the connection to the producer. This should not affect the consumer. + p = nullptr; + + LocalHandle fence; + EXPECT_EQ(0, c->Acquire(&fence)); + EXPECT_EQ(0, c->Release(LocalHandle())); + + // Attempt to reconnect to the persistent buffer. + p = BufferProducer::Create("TestPersistentBuffer"); + ASSERT_NE(nullptr, p); + EXPECT_EQ(buffer_id, p->id()); + EXPECT_EQ(0, p->Gain(&fence)); +} + +TEST_F(LibBufferHubTest, TestPersistentBufferMismatchParams) { + auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth, + kHeight, kFormat, kUsage); + ASSERT_NE(nullptr, p); + + // Close the connection to the producer. + p = nullptr; + + // Mismatch the params. + p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth * 2, + kHeight, kFormat, kUsage); + ASSERT_EQ(nullptr, p); +} + +TEST_F(LibBufferHubTest, TestRemovePersistentBuffer) { + auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth, + kHeight, kFormat, kUsage); + ASSERT_NE(nullptr, p); + + LocalHandle fence; + auto c = BufferConsumer::Import(p->CreateConsumer()); + ASSERT_NE(nullptr, c); + EXPECT_NE(-EPIPE, c->Acquire(&fence)); + + // Test that removing persistence and closing the producer orphans the + // consumer. + EXPECT_EQ(0, p->RemovePersistence()); + p = nullptr; + + EXPECT_EQ(-EPIPE, c->Release(LocalHandle())); +} diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h new file mode 100644 index 0000000000..be20e72a84 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h @@ -0,0 +1,313 @@ +#ifndef ANDROID_DVR_BUFFER_HUB_CLIENT_H_ +#define ANDROID_DVR_BUFFER_HUB_CLIENT_H_ + +#include <hardware/gralloc.h> +#include <pdx/channel_handle.h> +#include <pdx/client.h> +#include <pdx/file_handle.h> +#include <pdx/status.h> + +#include <vector> + +#include <private/dvr/ion_buffer.h> + +namespace android { +namespace dvr { + +class BufferHubBuffer : public pdx::Client { + public: + using LocalHandle = pdx::LocalHandle; + using LocalChannelHandle = pdx::LocalChannelHandle; + template <typename T> + using Status = pdx::Status<T>; + + // Create a new consumer channel that is attached to the producer. Returns + // a file descriptor for the new channel or a negative error code. + Status<LocalChannelHandle> CreateConsumer(); + + // Polls the fd for |timeout_ms| milliseconds (-1 for infinity). + int Poll(int timeout_ms); + + // Locks the area specified by (x, y, width, height) for a specific usage. If + // the usage is software then |addr| will be updated to point to the address + // of the buffer in virtual memory. The caller should only access/modify the + // pixels in the specified area. anything else is undefined behavior. + int Lock(int usage, int x, int y, int width, int height, void** addr); + + // Must be called after Lock() when the caller has finished changing the + // buffer. + int Unlock(); + + // Gets a blob buffer that was created with BufferProducer::CreateBlob. + // Locking and Unlocking is handled internally. There's no need to Unlock + // after calling this method. + int GetBlobReadWritePointer(size_t size, void** addr); + + // Gets a blob buffer that was created with BufferProducer::CreateBlob. + // Locking and Unlocking is handled internally. There's no need to Unlock + // after calling this method. + int GetBlobReadOnlyPointer(size_t size, void** addr); + + // Returns a dup'd file descriptor for accessing the blob shared memory. The + // caller takes ownership of the file descriptor and must close it or pass on + // ownership. Some GPU API extensions can take file descriptors to bind shared + // memory gralloc buffers to GPU buffer objects. + LocalHandle GetBlobFd() const { + // Current GPU vendor puts the buffer allocation in one FD. If we change GPU + // vendors and this is the wrong fd, late-latching and EDS will very clearly + // stop working and we will need to correct this. The alternative is to use + // a GL context in the pose service to allocate this buffer or to use the + // ION API directly instead of gralloc. + return LocalHandle(dup(native_handle()->data[0])); + } + + // Get up to |max_fds_count| file descriptors for accessing the blob shared + // memory. |fds_count| will contain the actual number of file descriptors. + void GetBlobFds(int* fds, size_t* fds_count, size_t max_fds_count) const; + + using Client::event_fd; + + Status<int> GetEventMask(int events) { + if (auto* client_channel = GetChannel()) { + return client_channel->GetEventMask(events); + } else { + return pdx::ErrorStatus(EINVAL); + } + } + + native_handle_t* native_handle() const { + return const_cast<native_handle_t*>(buffer_.handle()); + } + + IonBuffer* buffer() { return &buffer_; } + const IonBuffer* buffer() const { return &buffer_; } + + int id() const { return id_; } + + // The following methods return settings of the first buffer. Currently, + // it is only possible to create multi-buffer BufferHubBuffers with the same + // settings. + uint32_t width() const { return buffer_.width(); } + uint32_t height() const { return buffer_.height(); } + uint32_t stride() const { return buffer_.stride(); } + uint32_t format() const { return buffer_.format(); } + uint32_t usage() const { return buffer_.usage(); } + uint32_t layer_count() const { return buffer_.layer_count(); } + + // TODO(b/37881101) Clean up producer/consumer usage. + uint64_t producer_usage() const { return buffer_.usage(); } + uint64_t consumer_usage() const { return buffer_.usage(); } + + protected: + explicit BufferHubBuffer(LocalChannelHandle channel); + explicit BufferHubBuffer(const std::string& endpoint_path); + virtual ~BufferHubBuffer(); + + // Initialization helper. + int ImportBuffer(); + + private: + BufferHubBuffer(const BufferHubBuffer&) = delete; + void operator=(const BufferHubBuffer&) = delete; + + // Global id for the buffer that is consistent across processes. It is meant + // for logging and debugging purposes only and should not be used for lookup + // or any other functional purpose as a security precaution. + int id_; + + IonBuffer buffer_; +}; + +// This represents a writable buffer. Calling Post notifies all clients and +// makes the buffer read-only. Call Gain to acquire write access. A buffer +// may have many consumers. +// +// The user of BufferProducer is responsible with making sure that the Post() is +// done with the correct metadata type and size. The user is also responsible +// for making sure that remote ends (BufferConsumers) are also using the correct +// metadata when acquiring the buffer. The API guarantees that a Post() with a +// metadata of wrong size will fail. However, it currently does not do any +// type checking. +// The API also assumes that metadata is a serializable type (plain old data). +class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> { + public: + // Imports a bufferhub producer channel, assuming ownership of its handle. + static std::unique_ptr<BufferProducer> Import(LocalChannelHandle channel); + static std::unique_ptr<BufferProducer> Import( + Status<LocalChannelHandle> status); + + // Post this buffer, passing |ready_fence| to the consumers. The bytes in + // |meta| are passed unaltered to the consumers. The producer must not modify + // the buffer until it is re-gained. + // This returns zero or a negative unix error code. + int Post(const LocalHandle& ready_fence, const void* meta, + size_t meta_size_bytes); + + template <typename Meta, + typename = typename std::enable_if<std::is_void<Meta>::value>::type> + int Post(const LocalHandle& ready_fence) { + return Post(ready_fence, nullptr, 0); + } + template <typename Meta, typename = typename std::enable_if< + !std::is_void<Meta>::value>::type> + int Post(const LocalHandle& ready_fence, const Meta& meta) { + return Post(ready_fence, &meta, sizeof(meta)); + } + + // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it + // must be waited on before using the buffer. If it is not valid then the + // buffer is free for immediate use. This call will only succeed if the buffer + // is in the released state. + // This returns zero or a negative unix error code. + int Gain(LocalHandle* release_fence); + + // Asynchronously marks a released buffer as gained. This method is similar to + // the synchronous version above, except that it does not wait for BufferHub + // to acknowledge success or failure, nor does it transfer a release fence to + // the client. This version may be used in situations where a release fence is + // not needed. Because of the asynchronous nature of the underlying message, + // no error is returned if this method is called when the buffer is in an + // incorrect state. Returns zero if sending the message succeeded, or a + // negative errno code otherwise. + int GainAsync(); + + // Attaches the producer to |name| so that it becomes a persistent buffer that + // may be retrieved by name at a later time. This may be used in cases where a + // shared memory buffer should persist across the life of the producer process + // (i.e. the buffer may be held by clients across a service restart). The + // buffer may be associated with a user and/or group id to restrict access to + // the buffer. If user_id or group_id is -1 then checks for the respective id + // are disabled. If user_id or group_id is 0 then the respective id of the + // calling process is used instead. + int MakePersistent(const std::string& name, int user_id, int group_id); + + // Removes the persistence of the producer. + int RemovePersistence(); + + private: + friend BASE; + + // Constructors are automatically exposed through BufferProducer::Create(...) + // static template methods inherited from ClientBase, which take the same + // arguments as the constructors. + + // Constructs a buffer with the given geometry and parameters. + BufferProducer(uint32_t width, uint32_t height, uint32_t format, + uint32_t usage, size_t metadata_size = 0); + BufferProducer(uint32_t width, uint32_t height, uint32_t format, + uint64_t producer_usage, uint64_t consumer_usage, + size_t metadata_size); + + // Constructs a persistent buffer with the given geometry and parameters and + // binds it to |name| in one shot. If a persistent buffer with the same name + // and settings already exists and matches the given geometry and parameters, + // that buffer is connected to this client instead of creating a new buffer. + // If the name matches but the geometry or settings do not match then + // construction fails and BufferProducer::Create() returns nullptr. + // + // Access to the persistent buffer may be restricted by |user_id| and/or + // |group_id|; these settings are established only when the buffer is first + // created and cannot be changed. A user or group id of -1 disables checks for + // that respective id. A user or group id of 0 is substituted with the + // effective user or group id of the calling process. + BufferProducer(const std::string& name, int user_id, int group_id, + uint32_t width, uint32_t height, uint32_t format, + uint32_t usage, size_t metadata_size = 0); + BufferProducer(const std::string& name, int user_id, int group_id, + uint32_t width, uint32_t height, uint32_t format, + uint64_t producer_usage, uint64_t consumer_usage, + size_t meta_size_bytes); + + // Constructs a blob (flat) buffer with the given usage flags. + BufferProducer(uint32_t usage, size_t size); + BufferProducer(uint64_t producer_usage, uint64_t consumer_usage, size_t size); + + // Constructs a persistent blob (flat) buffer and binds it to |name|. + BufferProducer(const std::string& name, int user_id, int group_id, + uint32_t usage, size_t size); + BufferProducer(const std::string& name, int user_id, int group_id, + uint64_t producer_usage, uint64_t consumer_usage, size_t size); + + // Constructs a channel to persistent buffer by name only. The buffer must + // have been previously created or made persistent. + explicit BufferProducer(const std::string& name); + + // Imports the given file handle to a producer channel, taking ownership. + explicit BufferProducer(LocalChannelHandle channel); +}; + +// This is a connection to a producer buffer, which can be located in another +// application. When that buffer is Post()ed, this fd will be signaled and +// Acquire allows read access. The user is responsible for making sure that +// Acquire is called with the correct metadata structure. The only guarantee the +// API currently provides is that an Acquire() with metadata of the wrong size +// will fail. +class BufferConsumer : public pdx::ClientBase<BufferConsumer, BufferHubBuffer> { + public: + // This call assumes ownership of |fd|. + static std::unique_ptr<BufferConsumer> Import(LocalChannelHandle channel); + static std::unique_ptr<BufferConsumer> Import( + Status<LocalChannelHandle> status); + + // Attempt to retrieve a post event from buffer hub. If successful, + // |ready_fence| will be set to a fence to wait on until the buffer is ready. + // This call will only succeed after the fd is signalled. This call may be + // performed as an alternative to the Acquire() with metadata. In such cases + // the metadata is not read. + // + // This returns zero or negative unix error code. + int Acquire(LocalHandle* ready_fence); + + // Attempt to retrieve a post event from buffer hub. If successful, + // |ready_fence| is set to a fence signaling that the contents of the buffer + // are available. This call will only succeed if the buffer is in the posted + // state. + // Returns zero on success, or a negative errno code otherwise. + int Acquire(LocalHandle* ready_fence, void* meta, size_t meta_size_bytes); + + // Attempt to retrieve a post event from buffer hub. If successful, + // |ready_fence| is set to a fence to wait on until the buffer is ready. This + // call will only succeed after the fd is signaled. This returns zero or a + // negative unix error code. + template <typename Meta> + int Acquire(LocalHandle* ready_fence, Meta* meta) { + return Acquire(ready_fence, meta, sizeof(*meta)); + } + + // This should be called after a successful Acquire call. If the fence is + // valid the fence determines the buffer usage, otherwise the buffer is + // released immediately. + // This returns zero or a negative unix error code. + int Release(const LocalHandle& release_fence); + + // Asynchronously releases a buffer. Similar to the synchronous version above, + // except that it does not wait for BufferHub to reply with success or error, + // nor does it transfer a release fence. This version may be used in + // situations where a release fence is not needed. Because of the asynchronous + // nature of the underlying message, no error is returned if this method is + // called when the buffer is in an incorrect state. Returns zero if sending + // the message succeeded, or a negative errno code otherwise. + int ReleaseAsync(); + + // May be called after or instead of Acquire to indicate that the consumer + // does not need to access the buffer this cycle. This returns zero or a + // negative unix error code. + int Discard(); + + // When set, this consumer is no longer notified when this buffer is + // available. The system behaves as if Discard() is immediately called + // whenever the buffer is posted. If ignore is set to true while a buffer is + // pending, it will act as if Discard() was also called. + // This returns zero or a negative unix error code. + int SetIgnore(bool ignore); + + private: + friend BASE; + + explicit BufferConsumer(LocalChannelHandle channel); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_BUFFER_HUB_CLIENT_H_ diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h new file mode 100644 index 0000000000..ffdc9e2e79 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h @@ -0,0 +1,241 @@ +#ifndef ANDROID_DVR_BUFFERHUB_RPC_H_ +#define ANDROID_DVR_BUFFERHUB_RPC_H_ + +#include <cutils/native_handle.h> +#include <gui/BufferQueueDefs.h> +#include <sys/types.h> + +#include <pdx/channel_handle.h> +#include <pdx/file_handle.h> +#include <pdx/rpc/remote_method.h> +#include <pdx/rpc/serializable.h> +#include <private/dvr/ion_buffer.h> + +namespace android { +namespace dvr { + +template <typename FileHandleType> +class NativeBufferHandle { + public: + NativeBufferHandle() { Clear(); } + NativeBufferHandle(const IonBuffer& buffer, int id) + : id_(id), + stride_(buffer.stride()), + width_(buffer.width()), + height_(buffer.height()), + layer_count_(buffer.layer_count()), + format_(buffer.format()), + usage_(buffer.usage()) { + // Populate the fd and int vectors: native_handle->data[] is an array of fds + // followed by an array of opaque ints. + const int fd_count = buffer.handle()->numFds; + const int int_count = buffer.handle()->numInts; + for (int i = 0; i < fd_count; i++) { + fds_.emplace_back(FileHandleType::AsDuplicate(buffer.handle()->data[i])); + } + for (int i = 0; i < int_count; i++) { + opaque_ints_.push_back(buffer.handle()->data[fd_count + i]); + } + } + NativeBufferHandle(NativeBufferHandle&& other) = default; + NativeBufferHandle& operator=(NativeBufferHandle&& other) = default; + + // Imports the native handle into the given IonBuffer instance. + int Import(IonBuffer* buffer) { + // This is annoying, but we need to convert the vector of FileHandles into a + // vector of ints for the Import API. + std::vector<int> fd_ints; + for (const auto& fd : fds_) + fd_ints.push_back(fd.Get()); + + const int ret = + buffer->Import(fd_ints.data(), fd_ints.size(), opaque_ints_.data(), + opaque_ints_.size(), width_, height_, layer_count_, + stride_, format_, usage_); + if (ret < 0) + return ret; + + // Import succeeded, release the file handles which are now owned by the + // IonBuffer and clear members. + for (auto& fd : fds_) + fd.Release(); + opaque_ints_.clear(); + Clear(); + + return 0; + } + + int id() const { return id_; } + size_t IntCount() const { return opaque_ints_.size(); } + size_t FdCount() const { return fds_.size(); } + + private: + int id_; + uint32_t stride_; + uint32_t width_; + uint32_t height_; + uint32_t layer_count_; + uint32_t format_; + uint64_t usage_; + std::vector<int> opaque_ints_; + std::vector<FileHandleType> fds_; + + void Clear() { + id_ = -1; + stride_ = width_ = height_ = format_ = usage_ = 0; + } + + PDX_SERIALIZABLE_MEMBERS(NativeBufferHandle<FileHandleType>, id_, stride_, + width_, height_, layer_count_, format_, usage_, + opaque_ints_, fds_); + + NativeBufferHandle(const NativeBufferHandle&) = delete; + void operator=(const NativeBufferHandle&) = delete; +}; + +using BorrowedNativeBufferHandle = NativeBufferHandle<pdx::BorrowedHandle>; +using LocalNativeBufferHandle = NativeBufferHandle<pdx::LocalHandle>; + +template <typename FileHandleType> +class FenceHandle { + public: + FenceHandle() = default; + explicit FenceHandle(int fence) : fence_{fence} {} + explicit FenceHandle(FileHandleType&& fence) : fence_{std::move(fence)} {} + FenceHandle(FenceHandle&&) = default; + FenceHandle& operator=(FenceHandle&&) = default; + + explicit operator bool() const { return fence_.IsValid(); } + + const FileHandleType& get() const { fence_; } + FileHandleType&& take() { return std::move(fence_); } + + int get_fd() const { return fence_.Get(); } + void close() { fence_.Close(); } + + FenceHandle<pdx::BorrowedHandle> borrow() const { + return FenceHandle<pdx::BorrowedHandle>(fence_.Borrow()); + } + + private: + FileHandleType fence_; + + PDX_SERIALIZABLE_MEMBERS(FenceHandle<FileHandleType>, fence_); + + FenceHandle(const FenceHandle&) = delete; + void operator=(const FenceHandle&) = delete; +}; + +using LocalFence = FenceHandle<pdx::LocalHandle>; +using BorrowedFence = FenceHandle<pdx::BorrowedHandle>; + +struct QueueInfo { + size_t meta_size_bytes; + int id; + + private: + PDX_SERIALIZABLE_MEMBERS(QueueInfo, meta_size_bytes, id); +}; + +struct UsagePolicy { + uint64_t usage_set_mask; + uint64_t usage_clear_mask; + uint64_t usage_deny_set_mask; + uint64_t usage_deny_clear_mask; + + private: + PDX_SERIALIZABLE_MEMBERS(UsagePolicy, usage_set_mask, usage_clear_mask, + usage_deny_set_mask, usage_deny_clear_mask); +}; + +// BufferHub Service RPC interface. Defines the endpoints, op codes, and method +// type signatures supported by bufferhubd. +struct BufferHubRPC { + // Service path. + static constexpr char kClientPath[] = "system/buffer_hub/client"; + + // |BufferHubQueue| will keep track of at most this value of buffers. + // Attempts at runtime to increase the number of buffers past this + // will fail. Note that the value is in sync with |android::BufferQueue|, so + // that slot id can be shared between |android::dvr::BufferHubQueueProducer| + // and |android::BufferQueueProducer| which both implements the same + // interface: |android::IGraphicBufferProducer|. + static constexpr size_t kMaxQueueCapacity = + android::BufferQueueDefs::NUM_BUFFER_SLOTS; + + // Op codes. + enum { + kOpCreateBuffer = 0, + kOpCreatePersistentBuffer, + kOpGetPersistentBuffer, + kOpGetBuffer, + kOpNewConsumer, + kOpProducerMakePersistent, + kOpProducerRemovePersistence, + kOpProducerPost, + kOpProducerGain, + kOpConsumerAcquire, + kOpConsumerRelease, + kOpConsumerSetIgnore, + kOpCreateProducerQueue, + kOpCreateConsumerQueue, + kOpGetQueueInfo, + kOpProducerQueueAllocateBuffers, + kOpProducerQueueDetachBuffer, + kOpConsumerQueueImportBuffers, + }; + + // Aliases. + using MetaData = pdx::rpc::BufferWrapper<std::uint8_t*>; + using LocalChannelHandle = pdx::LocalChannelHandle; + using LocalHandle = pdx::LocalHandle; + using Void = pdx::rpc::Void; + + // Methods. + PDX_REMOTE_METHOD(CreateBuffer, kOpCreateBuffer, + void(uint32_t width, uint32_t height, uint32_t format, + uint64_t usage, size_t meta_size_bytes)); + PDX_REMOTE_METHOD(CreatePersistentBuffer, kOpCreatePersistentBuffer, + void(const std::string& name, int user_id, int group_id, + uint32_t width, uint32_t height, uint32_t format, + uint64_t usage, size_t meta_size_bytes)); + PDX_REMOTE_METHOD(GetPersistentBuffer, kOpGetPersistentBuffer, + void(const std::string& name)); + PDX_REMOTE_METHOD(GetBuffer, kOpGetBuffer, + NativeBufferHandle<LocalHandle>(Void)); + PDX_REMOTE_METHOD(NewConsumer, kOpNewConsumer, LocalChannelHandle(Void)); + PDX_REMOTE_METHOD(ProducerMakePersistent, kOpProducerMakePersistent, + void(const std::string& name, int user_id, int group_id)); + PDX_REMOTE_METHOD(ProducerRemovePersistence, kOpProducerRemovePersistence, + void(Void)); + PDX_REMOTE_METHOD(ProducerPost, kOpProducerPost, + void(LocalFence acquire_fence, MetaData)); + PDX_REMOTE_METHOD(ProducerGain, kOpProducerGain, LocalFence(Void)); + PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire, + std::pair<LocalFence, MetaData>(std::size_t metadata_size)); + PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease, + void(LocalFence release_fence)); + PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, void(bool ignore)); + + // Buffer Queue Methods. + PDX_REMOTE_METHOD(CreateProducerQueue, kOpCreateProducerQueue, + QueueInfo(size_t meta_size_bytes, + const UsagePolicy& usage_policy)); + PDX_REMOTE_METHOD(CreateConsumerQueue, kOpCreateConsumerQueue, + LocalChannelHandle(Void)); + PDX_REMOTE_METHOD(GetQueueInfo, kOpGetQueueInfo, QueueInfo(Void)); + PDX_REMOTE_METHOD(ProducerQueueAllocateBuffers, + kOpProducerQueueAllocateBuffers, + std::vector<std::pair<LocalChannelHandle, size_t>>( + uint32_t width, uint32_t height, uint32_t layer_count, + uint32_t format, uint64_t usage, size_t buffer_count)); + PDX_REMOTE_METHOD(ProducerQueueDetachBuffer, kOpProducerQueueDetachBuffer, + void(size_t slot)); + PDX_REMOTE_METHOD(ConsumerQueueImportBuffers, kOpConsumerQueueImportBuffers, + std::vector<std::pair<LocalChannelHandle, size_t>>(Void)); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_BUFFERHUB_RPC_H_ diff --git a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h new file mode 100644 index 0000000000..0d337f7b3b --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h @@ -0,0 +1,96 @@ +#ifndef ANDROID_DVR_ION_BUFFER_H_ +#define ANDROID_DVR_ION_BUFFER_H_ + +#include <hardware/gralloc.h> +#include <log/log.h> +#include <ui/GraphicBuffer.h> + +namespace android { +namespace dvr { + +// IonBuffer is an abstraction of Ion/Gralloc buffers. +class IonBuffer { + public: + IonBuffer(); + IonBuffer(uint32_t width, uint32_t height, uint32_t format, uint64_t usage); + IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height, + uint32_t stride, uint32_t format, uint64_t usage); + IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height, + uint32_t layer_count, uint32_t stride, uint32_t format, + uint64_t usage); + ~IonBuffer(); + + IonBuffer(IonBuffer&& other); + IonBuffer& operator=(IonBuffer&& other); + + // Frees the underlying native handle and leaves the instance initialized to + // empty. + void FreeHandle(); + + // Allocates a new native handle with the given parameters, freeing the + // previous native handle if necessary. Returns 0 on success or a negative + // errno code otherwise. If allocation fails the previous native handle is + // left intact. + int Alloc(uint32_t width, uint32_t height, uint32_t layer_count, + uint32_t format, uint64_t usage); + + // Resets the underlying native handle and parameters, freeing the previous + // native handle if necessary. + void Reset(buffer_handle_t handle, uint32_t width, uint32_t height, + uint32_t layer_count, uint32_t stride, uint32_t format, + uint64_t usage); + + // Like Reset but also registers the native handle, which is necessary for + // native handles received over IPC. Returns 0 on success or a negative errno + // code otherwise. If import fails the previous native handle is left intact. + int Import(buffer_handle_t handle, uint32_t width, uint32_t height, + uint32_t layer_count, uint32_t stride, uint32_t format, + uint64_t usage); + + // Like Reset but imports a native handle from raw fd and int arrays. Returns + // 0 on success or a negative errno code otherwise. If import fails the + // previous native handle is left intact. + int Import(const int* fd_array, int fd_count, const int* int_array, + int int_count, uint32_t width, uint32_t height, + uint32_t layer_count, uint32_t stride, uint32_t format, + uint64_t usage); + + // Duplicates the native handle underlying |other| and then imports it. This + // is useful for creating multiple, independent views of the same Ion/Gralloc + // buffer. Returns 0 on success or a negative errno code otherwise. If + // duplication or import fail the previous native handle is left intact. + int Duplicate(const IonBuffer* other); + + int Lock(uint32_t usage, int x, int y, int width, int height, void** address); + int LockYUV(uint32_t usage, int x, int y, int width, int height, + struct android_ycbcr* yuv); + int Unlock(); + + const sp<GraphicBuffer>& buffer() const { return buffer_; } + buffer_handle_t handle() const { + return buffer_.get() ? buffer_->handle : nullptr; + } + uint32_t width() const { return buffer_.get() ? buffer_->getWidth() : 0; } + uint32_t height() const { return buffer_.get() ? buffer_->getHeight() : 0; } + uint32_t layer_count() const { + return buffer_.get() ? buffer_->getLayerCount() : 0; + } + uint32_t stride() const { return buffer_.get() ? buffer_->getStride() : 0; } + uint32_t format() const { + return buffer_.get() ? buffer_->getPixelFormat() : 0; + } + uint64_t usage() const { + return buffer_.get() ? static_cast<uint64_t>(buffer_->getUsage()) : 0; + } + + private: + sp<GraphicBuffer> buffer_; + + IonBuffer(const IonBuffer&) = delete; + void operator=(const IonBuffer&) = delete; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_ION_BUFFER_H_ diff --git a/libs/vr/libbufferhub/include/private/dvr/native_buffer.h b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h new file mode 100644 index 0000000000..b4ef2f50d7 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h @@ -0,0 +1,176 @@ +#ifndef ANDROID_DVR_NATIVE_BUFFER_H_ +#define ANDROID_DVR_NATIVE_BUFFER_H_ + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <log/log.h> +#include <system/window.h> +#include <ui/ANativeObjectBase.h> +#include <utils/RefBase.h> + +#include <private/dvr/buffer_hub_client.h> + +namespace android { +namespace dvr { + +// ANativeWindowBuffer is the abstraction Android HALs and frameworks use to +// pass around hardware graphics buffers. The following classes implement this +// abstraction with different DVR backing buffers, all of which provide +// different semantics on top of ion/gralloc buffers. + +// An implementation of ANativeWindowBuffer backed by an IonBuffer. +class NativeBuffer + : public android::ANativeObjectBase<ANativeWindowBuffer, NativeBuffer, + android::LightRefBase<NativeBuffer>> { + public: + static constexpr int kEmptyFence = -1; + + explicit NativeBuffer(const std::shared_ptr<IonBuffer>& buffer) + : BASE(), buffer_(buffer), fence_(kEmptyFence) { + ANativeWindowBuffer::width = buffer->width(); + ANativeWindowBuffer::height = buffer->height(); + ANativeWindowBuffer::stride = buffer->stride(); + ANativeWindowBuffer::format = buffer->format(); + ANativeWindowBuffer::usage = buffer->usage(); + handle = buffer_->handle(); + } + + virtual ~NativeBuffer() {} + + std::shared_ptr<IonBuffer> buffer() { return buffer_; } + int fence() const { return fence_.Get(); } + + void SetFence(int fence) { fence_.Reset(fence); } + + private: + friend class android::LightRefBase<NativeBuffer>; + + std::shared_ptr<IonBuffer> buffer_; + pdx::LocalHandle fence_; + + NativeBuffer(const NativeBuffer&) = delete; + void operator=(NativeBuffer&) = delete; +}; + +// NativeBufferProducer is an implementation of ANativeWindowBuffer backed by a +// BufferProducer. +class NativeBufferProducer : public android::ANativeObjectBase< + ANativeWindowBuffer, NativeBufferProducer, + android::LightRefBase<NativeBufferProducer>> { + public: + static constexpr int kEmptyFence = -1; + + NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer, + EGLDisplay display, uint32_t surface_buffer_index) + : BASE(), + buffer_(buffer), + surface_buffer_index_(surface_buffer_index), + display_(display) { + ANativeWindowBuffer::width = buffer_->width(); + ANativeWindowBuffer::height = buffer_->height(); + ANativeWindowBuffer::stride = buffer_->stride(); + ANativeWindowBuffer::format = buffer_->format(); + ANativeWindowBuffer::usage = buffer_->usage(); + handle = buffer_->native_handle(); + } + + explicit NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer) + : NativeBufferProducer(buffer, nullptr, 0) {} + + virtual ~NativeBufferProducer() { + for (EGLImageKHR egl_image : egl_images_) { + if (egl_image != EGL_NO_IMAGE_KHR) + eglDestroyImageKHR(display_, egl_image); + } + } + + EGLImageKHR image_khr(int index) const { return egl_images_[index]; } + std::shared_ptr<BufferProducer> buffer() const { return buffer_; } + int release_fence() const { return release_fence_.Get(); } + uint32_t surface_buffer_index() const { return surface_buffer_index_; } + + // Return the release fence, passing ownership to the caller. + pdx::LocalHandle ClaimReleaseFence() { return std::move(release_fence_); } + + // Post the buffer consumer, closing the acquire and release fences. + int Post(int acquire_fence, uint64_t sequence) { + release_fence_.Close(); + return buffer_->Post(pdx::LocalHandle(acquire_fence), sequence); + } + + // Gain the buffer producer, closing the previous release fence if valid. + int Gain() { return buffer_->Gain(&release_fence_); } + + // Asynchronously gain the buffer, closing the previous release fence. + int GainAsync() { + release_fence_.Close(); + return buffer_->GainAsync(); + } + + private: + friend class android::LightRefBase<NativeBufferProducer>; + + std::shared_ptr<BufferProducer> buffer_; + pdx::LocalHandle release_fence_; + std::vector<EGLImageKHR> egl_images_; + uint32_t surface_buffer_index_; + EGLDisplay display_; + + NativeBufferProducer(const NativeBufferProducer&) = delete; + void operator=(NativeBufferProducer&) = delete; +}; + +// NativeBufferConsumer is an implementation of ANativeWindowBuffer backed by a +// BufferConsumer. +class NativeBufferConsumer : public android::ANativeObjectBase< + ANativeWindowBuffer, NativeBufferConsumer, + android::LightRefBase<NativeBufferConsumer>> { + public: + static constexpr int kEmptyFence = -1; + + explicit NativeBufferConsumer(const std::shared_ptr<BufferConsumer>& buffer) + : BASE(), buffer_(buffer), acquire_fence_(kEmptyFence), sequence_(0) { + ANativeWindowBuffer::width = buffer_->width(); + ANativeWindowBuffer::height = buffer_->height(); + ANativeWindowBuffer::stride = buffer_->stride(); + ANativeWindowBuffer::format = buffer_->format(); + ANativeWindowBuffer::usage = buffer_->usage(); + handle = buffer_->native_handle(); + } + + virtual ~NativeBufferConsumer() {} + + std::shared_ptr<BufferConsumer> buffer() const { return buffer_; } + int acquire_fence() const { return acquire_fence_.Get(); } + uint64_t sequence() const { return sequence_; } + + // Return the acquire fence, passing ownership to the caller. + pdx::LocalHandle ClaimAcquireFence() { return std::move(acquire_fence_); } + + // Acquire the underlying buffer consumer, closing the previous acquire fence + // if valid. + int Acquire() { return buffer_->Acquire(&acquire_fence_, &sequence_); } + + // Release the buffer consumer, closing the acquire and release fences if + // valid. + int Release(int release_fence) { + acquire_fence_.Close(); + sequence_ = 0; + return buffer_->Release(pdx::LocalHandle(release_fence)); + } + + private: + friend class android::LightRefBase<NativeBufferConsumer>; + + std::shared_ptr<BufferConsumer> buffer_; + pdx::LocalHandle acquire_fence_; + uint64_t sequence_; + + NativeBufferConsumer(const NativeBufferConsumer&) = delete; + void operator=(NativeBufferConsumer&) = delete; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_NATIVE_BUFFER_H_ diff --git a/libs/vr/libbufferhub/ion_buffer.cpp b/libs/vr/libbufferhub/ion_buffer.cpp new file mode 100644 index 0000000000..cbaa24a7bc --- /dev/null +++ b/libs/vr/libbufferhub/ion_buffer.cpp @@ -0,0 +1,240 @@ +#include <private/dvr/ion_buffer.h> + +#include <log/log.h> +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <utils/Trace.h> + +#include <mutex> + +namespace { + +constexpr uint32_t kDefaultGraphicBufferLayerCount = 1; + +} // anonymous namespace + +namespace android { +namespace dvr { + +IonBuffer::IonBuffer() : IonBuffer(nullptr, 0, 0, 0, 0, 0, 0) {} + +IonBuffer::IonBuffer(uint32_t width, uint32_t height, uint32_t format, + uint64_t usage) + : IonBuffer() { + Alloc(width, height, kDefaultGraphicBufferLayerCount, format, usage); +} + +IonBuffer::IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height, + uint32_t stride, uint32_t format, uint64_t usage) + : IonBuffer(handle, width, height, kDefaultGraphicBufferLayerCount, stride, + format, usage) {} + +IonBuffer::IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height, + uint32_t layer_count, uint32_t stride, uint32_t format, + uint64_t usage) + : buffer_(nullptr) { + ALOGD_IF(TRACE, + "IonBuffer::IonBuffer: handle=%p width=%u height=%u layer_count=%u " + "stride=%u format=%u usage=%" PRIx64, + handle, width, height, layer_count, stride, format, usage); + if (handle != 0) { + Import(handle, width, height, layer_count, stride, format, usage); + } +} + +IonBuffer::~IonBuffer() { + ALOGD_IF(TRACE, + "IonBuffer::~IonBuffer: handle=%p width=%u height=%u stride=%u " + "format=%u usage=%" PRIx64, + handle(), width(), height(), stride(), format(), usage()); + FreeHandle(); +} + +IonBuffer::IonBuffer(IonBuffer&& other) : IonBuffer() { + *this = std::move(other); +} + +IonBuffer& IonBuffer::operator=(IonBuffer&& other) { + ALOGD_IF(TRACE, "IonBuffer::operator=: handle_=%p other.handle_=%p", handle(), + other.handle()); + + if (this != &other) { + buffer_ = other.buffer_; + other.FreeHandle(); + } + return *this; +} + +void IonBuffer::FreeHandle() { + if (buffer_.get()) { + // GraphicBuffer unregisters and cleans up the handle if needed + buffer_ = nullptr; + } +} + +int IonBuffer::Alloc(uint32_t width, uint32_t height, uint32_t layer_count, + uint32_t format, uint64_t usage) { + ALOGD_IF(TRACE, + "IonBuffer::Alloc: width=%u height=%u layer_count=%u format=%u " + "usage=%" PRIx64, width, height, layer_count, format, usage); + + sp<GraphicBuffer> buffer = + new GraphicBuffer(width, height, format, layer_count, usage); + if (buffer->initCheck() != OK) { + ALOGE("IonBuffer::Aloc: Failed to allocate buffer"); + return -EINVAL; + } else { + buffer_ = buffer; + return 0; + } +} + +void IonBuffer::Reset(buffer_handle_t handle, uint32_t width, uint32_t height, + uint32_t layer_count, uint32_t stride, uint32_t format, + uint64_t usage) { + ALOGD_IF(TRACE, + "IonBuffer::Reset: handle=%p width=%u height=%u layer_count=%u " + "stride=%u format=%u usage=%" PRIx64, + handle, width, height, layer_count, stride, format, usage); + Import(handle, width, height, layer_count, stride, format, usage); +} + +int IonBuffer::Import(buffer_handle_t handle, uint32_t width, uint32_t height, + uint32_t layer_count, uint32_t stride, uint32_t format, + uint64_t usage) { + ATRACE_NAME("IonBuffer::Import1"); + ALOGD_IF(TRACE, + "IonBuffer::Import: handle=%p width=%u height=%u layer_count=%u " + "stride=%u format=%u usage=%" PRIx64, + handle, width, height, layer_count, stride, format, usage); + FreeHandle(); + sp<GraphicBuffer> buffer = + new GraphicBuffer(handle, GraphicBuffer::TAKE_UNREGISTERED_HANDLE, width, + height, format, layer_count, usage, stride); + if (buffer->initCheck() != OK) { + ALOGE("IonBuffer::Import: Failed to import buffer"); + return -EINVAL; + } else { + buffer_ = buffer; + return 0; + } +} + +int IonBuffer::Import(const int* fd_array, int fd_count, const int* int_array, + int int_count, uint32_t width, uint32_t height, + uint32_t layer_count, uint32_t stride, uint32_t format, + uint64_t usage) { + ATRACE_NAME("IonBuffer::Import2"); + ALOGD_IF(TRACE, + "IonBuffer::Import: fd_count=%d int_count=%d width=%u height=%u " + "layer_count=%u stride=%u format=%u usage=%" PRIx64, + fd_count, int_count, width, height, layer_count, stride, format, + usage); + + if (fd_count < 0 || int_count < 0) { + ALOGE("IonBuffer::Import: invalid arguments."); + return -EINVAL; + } + + native_handle_t* handle = native_handle_create(fd_count, int_count); + if (!handle) { + ALOGE("IonBuffer::Import: failed to create new native handle."); + return -ENOMEM; + } + + // Copy fd_array into the first part of handle->data and int_array right + // after it. + memcpy(handle->data, fd_array, sizeof(int) * fd_count); + memcpy(handle->data + fd_count, int_array, sizeof(int) * int_count); + + const int ret = + Import(handle, width, height, layer_count, stride, format, usage); + if (ret < 0) { + ALOGE("IonBuffer::Import: failed to import raw native handle: %s", + strerror(-ret)); + native_handle_close(handle); + native_handle_delete(handle); + } + + return ret; +} + +int IonBuffer::Duplicate(const IonBuffer* other) { + if (!other->handle()) + return -EINVAL; + + const int fd_count = other->handle()->numFds; + const int int_count = other->handle()->numInts; + + if (fd_count < 0 || int_count < 0) + return -EINVAL; + + native_handle_t* handle = native_handle_create(fd_count, int_count); + if (!handle) { + ALOGE("IonBuffer::Duplicate: Failed to create new native handle."); + return -ENOMEM; + } + + // Duplicate the file descriptors from the other native handle. + for (int i = 0; i < fd_count; i++) + handle->data[i] = dup(other->handle()->data[i]); + + // Copy the ints after the file descriptors. + memcpy(handle->data + fd_count, other->handle()->data + fd_count, + sizeof(int) * int_count); + + const int ret = + Import(handle, other->width(), other->height(), other->layer_count(), + other->stride(), other->format(), other->usage()); + if (ret < 0) { + ALOGE("IonBuffer::Duplicate: Failed to import duplicate native handle: %s", + strerror(-ret)); + native_handle_close(handle); + native_handle_delete(handle); + } + + return ret; +} + +int IonBuffer::Lock(uint32_t usage, int x, int y, int width, int height, + void** address) { + ATRACE_NAME("IonBuffer::Lock"); + ALOGD_IF(TRACE, + "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d " + "address=%p", + handle(), usage, x, y, width, height, address); + + status_t err = + buffer_->lock(usage, Rect(x, y, x + width, y + height), address); + if (err != NO_ERROR) + return -EINVAL; + else + return 0; +} + +int IonBuffer::LockYUV(uint32_t usage, int x, int y, int width, int height, + struct android_ycbcr* yuv) { + ATRACE_NAME("IonBuffer::LockYUV"); + ALOGD_IF(TRACE, + "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d", + handle(), usage, x, y, width, height); + + status_t err = + buffer_->lockYCbCr(usage, Rect(x, y, x + width, y + height), yuv); + if (err != NO_ERROR) + return -EINVAL; + else + return 0; +} + +int IonBuffer::Unlock() { + ATRACE_NAME("IonBuffer::Unlock"); + ALOGD_IF(TRACE, "IonBuffer::Unlock: handle=%p", handle()); + + status_t err = buffer_->unlock(); + if (err != NO_ERROR) + return -EINVAL; + else + return 0; +} +} // namespace dvr +} // namespace android diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp new file mode 100644 index 0000000000..a587f95879 --- /dev/null +++ b/libs/vr/libbufferhubqueue/Android.bp @@ -0,0 +1,59 @@ +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +sourceFiles = [ + "buffer_hub_queue_client.cpp", + "buffer_hub_queue_producer.cpp", +] + +includeFiles = [ + "include", +] + +headerLibraries = [ + "libdvr_headers", +] + +staticLibraries = [ + "libbufferhub", + "libdvrcommon", + "libpdx_default_transport", +] + +sharedLibraries = [ + "libbase", + "libbinder", + "libcutils", + "libhardware", + "liblog", + "libui", + "libutils", + "libgui", +] + +cc_library { + name: "libbufferhubqueue", + cflags: [ + "-DLOG_TAG=\"libbufferhubqueue\"", + "-DTRACE=0", + ], + srcs: sourceFiles, + export_include_dirs: includeFiles, + export_static_lib_headers: staticLibraries, + header_libs: headerLibraries, + static_libs: staticLibraries, + shared_libs: sharedLibraries, +} + +subdirs = ["tests"] diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp new file mode 100644 index 0000000000..012a4e70e4 --- /dev/null +++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp @@ -0,0 +1,635 @@ +#include "include/private/dvr/buffer_hub_queue_client.h" + +#include <inttypes.h> +#include <log/log.h> +#include <poll.h> +#include <sys/epoll.h> + +#include <array> + +#include <pdx/default_transport/client_channel.h> +#include <pdx/default_transport/client_channel_factory.h> +#include <pdx/file_handle.h> +#include <private/dvr/bufferhub_rpc.h> + +#define RETRY_EINTR(fnc_call) \ + ([&]() -> decltype(fnc_call) { \ + decltype(fnc_call) result; \ + do { \ + result = (fnc_call); \ + } while (result == -1 && errno == EINTR); \ + return result; \ + })() + +using android::pdx::ErrorStatus; +using android::pdx::LocalChannelHandle; +using android::pdx::Status; + +namespace android { +namespace dvr { + +BufferHubQueue::BufferHubQueue(LocalChannelHandle channel_handle) + : Client{pdx::default_transport::ClientChannel::Create( + std::move(channel_handle))}, + meta_size_(0), + buffers_(BufferHubQueue::kMaxQueueCapacity), + epollhup_pending_(BufferHubQueue::kMaxQueueCapacity, false), + available_buffers_(BufferHubQueue::kMaxQueueCapacity), + fences_(BufferHubQueue::kMaxQueueCapacity), + capacity_(0), + id_(-1) { + Initialize(); +} + +BufferHubQueue::BufferHubQueue(const std::string& endpoint_path) + : Client{pdx::default_transport::ClientChannelFactory::Create( + endpoint_path)}, + meta_size_(0), + buffers_(BufferHubQueue::kMaxQueueCapacity), + epollhup_pending_(BufferHubQueue::kMaxQueueCapacity, false), + available_buffers_(BufferHubQueue::kMaxQueueCapacity), + fences_(BufferHubQueue::kMaxQueueCapacity), + capacity_(0), + id_(-1) { + Initialize(); +} + +void BufferHubQueue::Initialize() { + int ret = epoll_fd_.Create(); + if (ret < 0) { + ALOGE("BufferHubQueue::BufferHubQueue: Failed to create epoll fd: %s", + strerror(-ret)); + return; + } + + epoll_event event = {.events = EPOLLIN | EPOLLET, + .data = {.u64 = static_cast<uint64_t>( + BufferHubQueue::kEpollQueueEventIndex)}}; + ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event); + if (ret < 0) { + ALOGE("BufferHubQueue::Initialize: Failed to add event fd to epoll set: %s", + strerror(-ret)); + } +} + +Status<void> BufferHubQueue::ImportQueue() { + auto status = InvokeRemoteMethod<BufferHubRPC::GetQueueInfo>(); + if (!status) { + ALOGE("BufferHubQueue::ImportQueue: Failed to import queue: %s", + status.GetErrorMessage().c_str()); + return ErrorStatus(status.error()); + } else { + SetupQueue(status.get().meta_size_bytes, status.get().id); + return {}; + } +} + +void BufferHubQueue::SetupQueue(size_t meta_size_bytes, int id) { + meta_size_ = meta_size_bytes; + id_ = id; + meta_buffer_tmp_.reset(meta_size_ > 0 ? new uint8_t[meta_size_] : nullptr); +} + +std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() { + if (auto status = CreateConsumerQueueHandle()) + return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take())); + else + return nullptr; +} + +std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateSilentConsumerQueue() { + if (auto status = CreateConsumerQueueHandle()) + return std::unique_ptr<ConsumerQueue>( + new ConsumerQueue(status.take(), true)); + else + return nullptr; +} + +Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle() { + auto status = InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>(); + if (!status) { + ALOGE( + "BufferHubQueue::CreateConsumerQueue: Failed to create consumer queue: " + "%s", + status.GetErrorMessage().c_str()); + return ErrorStatus(status.error()); + } + + return status; +} + +bool BufferHubQueue::WaitForBuffers(int timeout) { + std::array<epoll_event, kMaxEvents> events; + + // Loop at least once to check for hangups. + do { + ALOGD_IF( + TRACE, + "BufferHubQueue::WaitForBuffers: queue_id=%d count=%zu capacity=%zu", + id(), count(), capacity()); + + // If there is already a buffer then just check for hangup without waiting. + const int ret = epoll_fd_.Wait(events.data(), events.size(), + count() == 0 ? timeout : 0); + + if (ret == 0) { + ALOGI_IF(TRACE, + "BufferHubQueue::WaitForBuffers: No events before timeout: " + "queue_id=%d", + id()); + return count() != 0; + } + + if (ret < 0 && ret != -EINTR) { + ALOGE("BufferHubQueue::WaitForBuffers: Failed to wait for buffers: %s", + strerror(-ret)); + return false; + } + + const int num_events = ret; + + // A BufferQueue's epoll fd tracks N+1 events, where there are N events, + // one for each buffer, in the queue and one extra event for the queue + // client itself. + for (int i = 0; i < num_events; i++) { + int64_t index = static_cast<int64_t>(events[i].data.u64); + + ALOGD_IF(TRACE, + "BufferHubQueue::WaitForBuffers: event %d: index=%" PRId64, i, + index); + + if (is_buffer_event_index(index)) { + HandleBufferEvent(static_cast<size_t>(index), events[i].events); + } else if (is_queue_event_index(index)) { + HandleQueueEvent(events[i].events); + } else { + ALOGW("BufferHubQueue::WaitForBuffers: Unknown event index: %" PRId64, + index); + } + } + } while (count() == 0 && capacity() > 0 && !hung_up()); + + return count() != 0; +} + +void BufferHubQueue::HandleBufferEvent(size_t slot, int poll_events) { + auto buffer = buffers_[slot]; + if (!buffer) { + ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot); + return; + } + + auto status = buffer->GetEventMask(poll_events); + if (!status) { + ALOGW("BufferHubQueue::HandleBufferEvent: Failed to get event mask: %s", + status.GetErrorMessage().c_str()); + return; + } + + const int events = status.get(); + if (events & EPOLLIN) { + const int ret = OnBufferReady(buffer, &fences_[slot]); + if (ret == 0 || ret == -EALREADY || ret == -EBUSY) { + // Only enqueue the buffer if it moves to or is already in the state + // requested in OnBufferReady(). If the buffer is busy this means that the + // buffer moved from released to posted when a new consumer was created + // before the ProducerQueue had a chance to regain it. This is a valid + // transition that we have to handle because edge triggered poll events + // latch the ready state even if it is later de-asserted -- don't enqueue + // or print an error log in this case. + if (ret != -EBUSY) + Enqueue(buffer, slot); + } else { + ALOGE( + "BufferHubQueue::HandleBufferEvent: Failed to set buffer ready, " + "queue_id=%d buffer_id=%d: %s", + id(), buffer->id(), strerror(-ret)); + } + } else if (events & EPOLLHUP) { + // This might be caused by producer replacing an existing buffer slot, or + // when BufferHubQueue is shutting down. For the first case, currently the + // epoll FD is cleaned up when the replacement consumer client is imported, + // we shouldn't detach again if |epollhub_pending_[slot]| is set. + ALOGW( + "BufferHubQueue::HandleBufferEvent: Received EPOLLHUP at slot: %zu, " + "buffer event fd: %d, EPOLLHUP pending: %d", + slot, buffer->event_fd(), int{epollhup_pending_[slot]}); + if (epollhup_pending_[slot]) { + epollhup_pending_[slot] = false; + } else { + DetachBuffer(slot); + } + } else { + ALOGW( + "BufferHubQueue::HandleBufferEvent: Unknown event, slot=%zu, epoll " + "events=%d", + slot, events); + } +} + +void BufferHubQueue::HandleQueueEvent(int poll_event) { + auto status = GetEventMask(poll_event); + if (!status) { + ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s", + status.GetErrorMessage().c_str()); + return; + } + + const int events = status.get(); + if (events & EPOLLIN) { + // Note that after buffer imports, if |count()| still returns 0, epoll + // wait will be tried again to acquire the newly imported buffer. + auto buffer_status = OnBufferAllocated(); + if (!buffer_status) { + ALOGE("BufferHubQueue::HandleQueueEvent: Failed to import buffer: %s", + buffer_status.GetErrorMessage().c_str()); + } + } else if (events & EPOLLHUP) { + ALOGD_IF(TRACE, "BufferHubQueue::HandleQueueEvent: hang up event!"); + hung_up_ = true; + } else { + ALOGW("BufferHubQueue::HandleQueueEvent: Unknown epoll events=%x", events); + } +} + +int BufferHubQueue::AddBuffer(const std::shared_ptr<BufferHubBuffer>& buf, + size_t slot) { + if (is_full()) { + // TODO(jwcai) Move the check into Producer's AllocateBuffer and consumer's + // import buffer. + ALOGE("BufferHubQueue::AddBuffer queue is at maximum capacity: %zu", + capacity_); + return -E2BIG; + } + + if (buffers_[slot] != nullptr) { + // Replace the buffer if the slot is preoccupied. This could happen when the + // producer side replaced the slot with a newly allocated buffer. Detach the + // buffer before setting up with the new one. + DetachBuffer(slot); + epollhup_pending_[slot] = true; + } + + epoll_event event = {.events = EPOLLIN | EPOLLET, .data = {.u64 = slot}}; + const int ret = epoll_fd_.Control(EPOLL_CTL_ADD, buf->event_fd(), &event); + if (ret < 0) { + ALOGE("BufferHubQueue::AddBuffer: Failed to add buffer to epoll set: %s", + strerror(-ret)); + return ret; + } + + buffers_[slot] = buf; + capacity_++; + return 0; +} + +int BufferHubQueue::DetachBuffer(size_t slot) { + auto& buf = buffers_[slot]; + if (buf == nullptr) { + ALOGE("BufferHubQueue::DetachBuffer: Invalid slot: %zu", slot); + return -EINVAL; + } + + const int ret = epoll_fd_.Control(EPOLL_CTL_DEL, buf->event_fd(), nullptr); + if (ret < 0) { + ALOGE( + "BufferHubQueue::DetachBuffer: Failed to detach buffer from epoll set: " + "%s", + strerror(-ret)); + return ret; + } + + buffers_[slot] = nullptr; + capacity_--; + return 0; +} + +void BufferHubQueue::Enqueue(const std::shared_ptr<BufferHubBuffer>& buf, + size_t slot) { + if (count() == capacity_) { + ALOGE("BufferHubQueue::Enqueue: Buffer queue is full!"); + return; + } + + // Set slot buffer back to vector. + // TODO(jwcai) Here have to dynamically allocate BufferInfo::metadata due to + // the limitation of the RingBuffer we are using. Would be better to refactor + // that. + BufferInfo buffer_info(slot, meta_size_); + buffer_info.buffer = buf; + // Swap metadata loaded during onBufferReady into vector. + std::swap(buffer_info.metadata, meta_buffer_tmp_); + + available_buffers_.Append(std::move(buffer_info)); +} + +Status<std::shared_ptr<BufferHubBuffer>> BufferHubQueue::Dequeue( + int timeout, size_t* slot, void* meta, LocalHandle* fence) { + ALOGD_IF(TRACE, "Dequeue: count=%zu, timeout=%d", count(), timeout); + + if (!WaitForBuffers(timeout)) + return ErrorStatus(ETIMEDOUT); + + std::shared_ptr<BufferHubBuffer> buf; + BufferInfo& buffer_info = available_buffers_.Front(); + + *fence = std::move(fences_[buffer_info.slot]); + + // Report current pos as the output slot. + std::swap(buffer_info.slot, *slot); + // Swap buffer from vector to be returned later. + std::swap(buffer_info.buffer, buf); + // Swap metadata from vector into tmp so that we can write out to |meta|. + std::swap(buffer_info.metadata, meta_buffer_tmp_); + + available_buffers_.PopFront(); + + if (!buf) { + ALOGE("BufferHubQueue::Dequeue: Buffer to be dequeued is nullptr"); + return ErrorStatus(ENOBUFS); + } + + if (meta) { + std::copy(meta_buffer_tmp_.get(), meta_buffer_tmp_.get() + meta_size_, + reinterpret_cast<uint8_t*>(meta)); + } + + return {std::move(buf)}; +} + +ProducerQueue::ProducerQueue(size_t meta_size) + : ProducerQueue(meta_size, 0, 0, 0, 0) {} + +ProducerQueue::ProducerQueue(LocalChannelHandle handle) + : BASE(std::move(handle)) { + auto status = ImportQueue(); + if (!status) { + ALOGE("ProducerQueue::ProducerQueue: Failed to import queue: %s", + status.GetErrorMessage().c_str()); + Close(-status.error()); + } +} + +ProducerQueue::ProducerQueue(size_t meta_size, uint64_t usage_set_mask, + uint64_t usage_clear_mask, + uint64_t usage_deny_set_mask, + uint64_t usage_deny_clear_mask) + : BASE(BufferHubRPC::kClientPath) { + auto status = InvokeRemoteMethod<BufferHubRPC::CreateProducerQueue>( + meta_size, UsagePolicy{usage_set_mask, usage_clear_mask, + usage_deny_set_mask, usage_deny_clear_mask}); + if (!status) { + ALOGE("ProducerQueue::ProducerQueue: Failed to create producer queue: %s", + status.GetErrorMessage().c_str()); + Close(-status.error()); + return; + } + + SetupQueue(status.get().meta_size_bytes, status.get().id); +} + +int ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height, + uint32_t layer_count, uint32_t format, + uint64_t usage, size_t* out_slot) { + if (out_slot == nullptr) { + ALOGE("ProducerQueue::AllocateBuffer: Parameter out_slot cannot be null."); + return -EINVAL; + } + + if (is_full()) { + ALOGE("ProducerQueue::AllocateBuffer queue is at maximum capacity: %zu", + capacity()); + return -E2BIG; + } + + const size_t kBufferCount = 1U; + Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status = + InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>( + width, height, layer_count, format, usage, kBufferCount); + if (!status) { + ALOGE("ProducerQueue::AllocateBuffer failed to create producer buffer: %s", + status.GetErrorMessage().c_str()); + return -status.error(); + } + + auto buffer_handle_slots = status.take(); + LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != kBufferCount, + "BufferHubRPC::ProducerQueueAllocateBuffers should " + "return one and only one buffer handle."); + + // We only allocate one buffer at a time. + auto& buffer_handle = buffer_handle_slots[0].first; + size_t buffer_slot = buffer_handle_slots[0].second; + ALOGD_IF(TRACE, + "ProducerQueue::AllocateBuffer, new buffer, channel_handle: %d", + buffer_handle.value()); + + *out_slot = buffer_slot; + return AddBuffer(BufferProducer::Import(std::move(buffer_handle)), + buffer_slot); +} + +int ProducerQueue::AddBuffer(const std::shared_ptr<BufferProducer>& buf, + size_t slot) { + ALOGD_IF(TRACE, "ProducerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu", + id(), buf->id(), slot); + // For producer buffer, we need to enqueue the newly added buffer + // immediately. Producer queue starts with all buffers in available state. + const int ret = BufferHubQueue::AddBuffer(buf, slot); + if (ret < 0) + return ret; + + Enqueue(buf, slot); + return 0; +} + +int ProducerQueue::DetachBuffer(size_t slot) { + auto status = + InvokeRemoteMethod<BufferHubRPC::ProducerQueueDetachBuffer>(slot); + if (!status) { + ALOGE("ProducerQueue::DetachBuffer: Failed to detach producer buffer: %s", + status.GetErrorMessage().c_str()); + return -status.error(); + } + + return BufferHubQueue::DetachBuffer(slot); +} + +Status<std::shared_ptr<BufferProducer>> ProducerQueue::Dequeue( + int timeout, size_t* slot, LocalHandle* release_fence) { + if (slot == nullptr || release_fence == nullptr) { + ALOGE("ProducerQueue::Dequeue: Invalid parameter: slot=%p release_fence=%p", + slot, release_fence); + return ErrorStatus(EINVAL); + } + + auto buffer_status = + BufferHubQueue::Dequeue(timeout, slot, nullptr, release_fence); + if (!buffer_status) + return buffer_status.error_status(); + + return {std::static_pointer_cast<BufferProducer>(buffer_status.take())}; +} + +int ProducerQueue::OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf, + LocalHandle* release_fence) { + ALOGD_IF(TRACE, "ProducerQueue::OnBufferReady: queue_id=%d buffer_id=%d", + id(), buf->id()); + auto buffer = std::static_pointer_cast<BufferProducer>(buf); + return buffer->Gain(release_fence); +} + +ConsumerQueue::ConsumerQueue(LocalChannelHandle handle, bool ignore_on_import) + : BufferHubQueue(std::move(handle)), ignore_on_import_(ignore_on_import) { + auto status = ImportQueue(); + if (!status) { + ALOGE("ConsumerQueue::ConsumerQueue: Failed to import queue: %s", + status.GetErrorMessage().c_str()); + Close(-status.error()); + } + + auto import_status = ImportBuffers(); + if (import_status) { + ALOGI("ConsumerQueue::ConsumerQueue: Imported %zu buffers.", + import_status.get()); + } else { + ALOGE("ConsumerQueue::ConsumerQueue: Failed to import buffers: %s", + import_status.GetErrorMessage().c_str()); + } +} + +Status<size_t> ConsumerQueue::ImportBuffers() { + auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>(); + if (!status) { + ALOGE("ConsumerQueue::ImportBuffers: Failed to import consumer buffer: %s", + status.GetErrorMessage().c_str()); + return ErrorStatus(status.error()); + } + + int ret; + int last_error = 0; + int imported_buffers = 0; + + auto buffer_handle_slots = status.take(); + for (auto& buffer_handle_slot : buffer_handle_slots) { + ALOGD_IF(TRACE, "ConsumerQueue::ImportBuffers: buffer_handle=%d", + buffer_handle_slot.first.value()); + + std::unique_ptr<BufferConsumer> buffer_consumer = + BufferConsumer::Import(std::move(buffer_handle_slot.first)); + + // Setup ignore state before adding buffer to the queue. + if (ignore_on_import_) { + ALOGD_IF(TRACE, + "ConsumerQueue::ImportBuffers: Setting buffer to ignored state: " + "buffer_id=%d", + buffer_consumer->id()); + ret = buffer_consumer->SetIgnore(true); + if (ret < 0) { + ALOGE( + "ConsumerQueue::ImportBuffers: Failed to set ignored state on " + "imported buffer buffer_id=%d: %s", + buffer_consumer->id(), strerror(-ret)); + last_error = ret; + } + } + + ret = AddBuffer(std::move(buffer_consumer), buffer_handle_slot.second); + if (ret < 0) { + ALOGE("ConsumerQueue::ImportBuffers: Failed to add buffer: %s", + strerror(-ret)); + last_error = ret; + continue; + } else { + imported_buffers++; + } + } + + if (imported_buffers > 0) + return {imported_buffers}; + else + return ErrorStatus(-last_error); +} + +int ConsumerQueue::AddBuffer(const std::shared_ptr<BufferConsumer>& buf, + size_t slot) { + ALOGD_IF(TRACE, "ConsumerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu", + id(), buf->id(), slot); + const int ret = BufferHubQueue::AddBuffer(buf, slot); + if (ret < 0) + return ret; + + // Check to see if the buffer is already signaled. This is necessary to catch + // cases where buffers are already available; epoll edge triggered mode does + // not fire until and edge transition when adding new buffers to the epoll + // set. + const int kTimeoutMs = 0; + pollfd pfd{buf->event_fd(), POLLIN, 0}; + const int count = RETRY_EINTR(poll(&pfd, 1, kTimeoutMs)); + if (count < 0) { + const int error = errno; + ALOGE("ConsumerQueue::AddBuffer: Failed to poll consumer buffer: %s", + strerror(errno)); + return -error; + } + + if (count == 1) + HandleBufferEvent(slot, pfd.revents); + + return 0; +} + +Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue( + int timeout, size_t* slot, void* meta, size_t meta_size, + LocalHandle* acquire_fence) { + if (meta_size != meta_size_) { + ALOGE( + "ConsumerQueue::Dequeue: Metadata size (%zu) for the dequeuing buffer " + "does not match metadata size (%zu) for the queue.", + meta_size, meta_size_); + return ErrorStatus(EINVAL); + } + + if (slot == nullptr || acquire_fence == nullptr) { + ALOGE( + "ConsumerQueue::Dequeue: Invalid parameter: slot=%p meta=%p " + "acquire_fence=%p", + slot, meta, acquire_fence); + return ErrorStatus(EINVAL); + } + + auto buffer_status = + BufferHubQueue::Dequeue(timeout, slot, meta, acquire_fence); + if (!buffer_status) + return buffer_status.error_status(); + + return {std::static_pointer_cast<BufferConsumer>(buffer_status.take())}; +} + +int ConsumerQueue::OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf, + LocalHandle* acquire_fence) { + ALOGD_IF(TRACE, "ConsumerQueue::OnBufferReady: queue_id=%d buffer_id=%d", + id(), buf->id()); + auto buffer = std::static_pointer_cast<BufferConsumer>(buf); + return buffer->Acquire(acquire_fence, meta_buffer_tmp_.get(), meta_size_); +} + +Status<void> ConsumerQueue::OnBufferAllocated() { + auto status = ImportBuffers(); + if (!status) { + ALOGE("ConsumerQueue::OnBufferAllocated: Failed to import buffers: %s", + status.GetErrorMessage().c_str()); + return ErrorStatus(status.error()); + } else if (status.get() == 0) { + ALOGW("ConsumerQueue::OnBufferAllocated: No new buffers allocated!"); + return ErrorStatus(ENOBUFS); + } else { + ALOGD_IF(TRACE, + "ConsumerQueue::OnBufferAllocated: Imported %zu consumer buffers.", + status.get()); + return {}; + } +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp new file mode 100644 index 0000000000..8582bbf3a6 --- /dev/null +++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp @@ -0,0 +1,643 @@ +#include "include/private/dvr/buffer_hub_queue_producer.h" + +#include <dvr/dvr_api.h> +#include <inttypes.h> +#include <log/log.h> + +namespace android { +namespace dvr { + +/* static */ +sp<BufferHubQueueProducer> BufferHubQueueProducer::Create() { + sp<BufferHubQueueProducer> producer = new BufferHubQueueProducer; + producer->queue_ = ProducerQueue::Create<DvrNativeBufferMetadata>(); + return producer; +} + +/* static */ +sp<BufferHubQueueProducer> BufferHubQueueProducer::Create( + const std::shared_ptr<ProducerQueue>& queue) { + if (queue->metadata_size() != sizeof(DvrNativeBufferMetadata)) { + ALOGE( + "BufferHubQueueProducer::Create producer's metadata size is different " + "than the size of DvrNativeBufferMetadata"); + return nullptr; + } + + sp<BufferHubQueueProducer> producer = new BufferHubQueueProducer; + producer->queue_ = queue; + return producer; +} + +status_t BufferHubQueueProducer::requestBuffer(int slot, + sp<GraphicBuffer>* buf) { + ALOGD_IF(TRACE, "requestBuffer: slot=%d", slot); + + std::unique_lock<std::mutex> lock(mutex_); + + if (connected_api_ == kNoConnectedApi) { + ALOGE("requestBuffer: BufferHubQueueProducer has no connected producer"); + return NO_INIT; + } + + if (slot < 0 || slot >= max_buffer_count_) { + ALOGE("requestBuffer: slot index %d out of range [0, %d)", slot, + max_buffer_count_); + return BAD_VALUE; + } else if (!buffers_[slot].mBufferState.isDequeued()) { + ALOGE("requestBuffer: slot %d is not owned by the producer (state = %s)", + slot, buffers_[slot].mBufferState.string()); + return BAD_VALUE; + } else if (buffers_[slot].mGraphicBuffer != nullptr) { + ALOGE("requestBuffer: slot %d is not empty.", slot); + return BAD_VALUE; + } else if (buffers_[slot].mBufferProducer == nullptr) { + ALOGE("requestBuffer: slot %d is not dequeued.", slot); + return BAD_VALUE; + } + + const auto& buffer_producer = buffers_[slot].mBufferProducer; + sp<GraphicBuffer> graphic_buffer = buffer_producer->buffer()->buffer(); + + buffers_[slot].mGraphicBuffer = graphic_buffer; + buffers_[slot].mRequestBufferCalled = true; + + *buf = graphic_buffer; + return NO_ERROR; +} + +status_t BufferHubQueueProducer::setMaxDequeuedBufferCount( + int max_dequeued_buffers) { + ALOGD_IF(TRACE, "setMaxDequeuedBufferCount: max_dequeued_buffers=%d", + max_dequeued_buffers); + + std::unique_lock<std::mutex> lock(mutex_); + + if (max_dequeued_buffers <= 0 || + max_dequeued_buffers > + static_cast<int>(BufferHubQueue::kMaxQueueCapacity - + kDefaultUndequeuedBuffers)) { + ALOGE("setMaxDequeuedBufferCount: %d out of range (0, %zu]", + max_dequeued_buffers, BufferHubQueue::kMaxQueueCapacity); + return BAD_VALUE; + } + + // The new dequeued_buffers count should not be violated by the number + // of currently dequeued buffers. + int dequeued_count = 0; + for (const auto& buf : buffers_) { + if (buf.mBufferState.isDequeued()) { + dequeued_count++; + } + } + if (dequeued_count > max_dequeued_buffers) { + ALOGE( + "setMaxDequeuedBufferCount: the requested dequeued_buffers" + "count (%d) exceeds the current dequeued buffer count (%d)", + max_dequeued_buffers, dequeued_count); + return BAD_VALUE; + } + + max_dequeued_buffer_count_ = max_dequeued_buffers; + return NO_ERROR; +} + +status_t BufferHubQueueProducer::setAsyncMode(bool async) { + if (async) { + // TODO(b/36724099) BufferHubQueue's consumer end always acquires the buffer + // automatically and behaves differently from IGraphicBufferConsumer. Thus, + // android::BufferQueue's async mode (a.k.a. allocating an additional buffer + // to prevent dequeueBuffer from being blocking) technically does not apply + // here. + // + // In Daydream, non-blocking producer side dequeue is guaranteed by careful + // buffer consumer implementations. In another word, BufferHubQueue based + // dequeueBuffer should never block whether setAsyncMode(true) is set or + // not. + // + // See: IGraphicBufferProducer::setAsyncMode and + // BufferQueueProducer::setAsyncMode for more about original implementation. + ALOGW( + "BufferHubQueueProducer::setAsyncMode: BufferHubQueue should always be " + "asynchronous. This call makes no effact."); + return NO_ERROR; + } + return NO_ERROR; +} + +status_t BufferHubQueueProducer::dequeueBuffer( + int* out_slot, sp<Fence>* out_fence, uint32_t width, uint32_t height, + PixelFormat format, uint32_t usage, + FrameEventHistoryDelta* /* out_timestamps */) { + ALOGD_IF(TRACE, "dequeueBuffer: w=%u, h=%u, format=%d, usage=%u", width, + height, format, usage); + + status_t ret; + std::unique_lock<std::mutex> lock(mutex_); + + if (connected_api_ == kNoConnectedApi) { + ALOGE("dequeueBuffer: BufferQueue has no connected producer"); + return NO_INIT; + } + + const uint32_t kLayerCount = 1; + if (static_cast<int32_t>(queue_->capacity()) < + max_dequeued_buffer_count_ + kDefaultUndequeuedBuffers) { + // Lazy allocation. When the capacity of |queue_| has not reached + // |max_dequeued_buffer_count_|, allocate new buffer. + // TODO(jwcai) To save memory, the really reasonable thing to do is to go + // over existing slots and find first existing one to dequeue. + ret = AllocateBuffer(width, height, kLayerCount, format, usage); + if (ret < 0) + return ret; + } + + size_t slot; + std::shared_ptr<BufferProducer> buffer_producer; + + for (size_t retry = 0; retry < BufferHubQueue::kMaxQueueCapacity; retry++) { + LocalHandle fence; + auto buffer_status = queue_->Dequeue(dequeue_timeout_ms_, &slot, &fence); + + buffer_producer = buffer_status.take(); + if (!buffer_producer) + return NO_MEMORY; + + if (width == buffer_producer->width() && + height == buffer_producer->height() && + static_cast<uint32_t>(format) == buffer_producer->format()) { + // The producer queue returns a buffer producer matches the request. + break; + } + + // Needs reallocation. + // TODO(jwcai) Consider use VLOG instead if we find this log is not useful. + ALOGI( + "dequeueBuffer: requested buffer (w=%u, h=%u, format=%u) is different " + "from the buffer returned at slot: %zu (w=%u, h=%u, format=%u). Need " + "re-allocattion.", + width, height, format, slot, buffer_producer->width(), + buffer_producer->height(), buffer_producer->format()); + // Mark the slot as reallocating, so that later we can set + // BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued. + buffers_[slot].mIsReallocating = true; + + // Remove the old buffer once the allocation before allocating its + // replacement. + RemoveBuffer(slot); + + // Allocate a new producer buffer with new buffer configs. Note that if + // there are already multiple buffers in the queue, the next one returned + // from |queue_->Dequeue| may not be the new buffer we just reallocated. + // Retry up to BufferHubQueue::kMaxQueueCapacity times. + ret = AllocateBuffer(width, height, kLayerCount, format, usage); + if (ret < 0) + return ret; + } + + // With the BufferHub backed solution. Buffer slot returned from + // |queue_->Dequeue| is guaranteed to avaiable for producer's use. + // It's either in free state (if the buffer has never been used before) or + // in queued state (if the buffer has been dequeued and queued back to + // BufferHubQueue). + // TODO(jwcai) Clean this up, make mBufferState compatible with BufferHub's + // model. + LOG_ALWAYS_FATAL_IF((!buffers_[slot].mBufferState.isFree() && + !buffers_[slot].mBufferState.isQueued()), + "dequeueBuffer: slot %zu is not free or queued.", slot); + + buffers_[slot].mBufferState.freeQueued(); + buffers_[slot].mBufferState.dequeue(); + ALOGD_IF(TRACE, "dequeueBuffer: slot=%zu", slot); + + // TODO(jwcai) Handle fence properly. |BufferHub| has full fence support, we + // just need to exopose that through |BufferHubQueue| once we need fence. + *out_fence = Fence::NO_FENCE; + *out_slot = slot; + ret = NO_ERROR; + + if (buffers_[slot].mIsReallocating) { + ret |= BUFFER_NEEDS_REALLOCATION; + buffers_[slot].mIsReallocating = false; + } + + return ret; +} + +status_t BufferHubQueueProducer::detachBuffer(int /* slot */) { + ALOGE("BufferHubQueueProducer::detachBuffer not implemented."); + return INVALID_OPERATION; +} + +status_t BufferHubQueueProducer::detachNextBuffer( + sp<GraphicBuffer>* /* out_buffer */, sp<Fence>* /* out_fence */) { + ALOGE("BufferHubQueueProducer::detachNextBuffer not implemented."); + return INVALID_OPERATION; +} + +status_t BufferHubQueueProducer::attachBuffer( + int* /* out_slot */, const sp<GraphicBuffer>& /* buffer */) { + // With this BufferHub backed implementation, we assume (for now) all buffers + // are allocated and owned by the BufferHub. Thus the attempt of transfering + // ownership of a buffer to the buffer queue is intentionally unsupported. + LOG_ALWAYS_FATAL("BufferHubQueueProducer::attachBuffer not supported."); + return INVALID_OPERATION; +} + +status_t BufferHubQueueProducer::queueBuffer(int slot, + const QueueBufferInput& input, + QueueBufferOutput* output) { + ALOGD_IF(TRACE, "queueBuffer: slot %d", slot); + + if (output == nullptr) { + return BAD_VALUE; + } + + int64_t timestamp; + bool is_auto_timestamp; + android_dataspace dataspace; + Rect crop(Rect::EMPTY_RECT); + int scaling_mode; + uint32_t transform; + sp<Fence> fence; + + input.deflate(×tamp, &is_auto_timestamp, &dataspace, &crop, + &scaling_mode, &transform, &fence); + + // Check input scaling mode is valid. + switch (scaling_mode) { + 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: + break; + default: + ALOGE("queueBuffer: unknown scaling mode %d", scaling_mode); + return BAD_VALUE; + } + + // Check input fence is valid. + if (fence == nullptr) { + ALOGE("queueBuffer: fence is NULL"); + return BAD_VALUE; + } + + status_t ret; + std::unique_lock<std::mutex> lock(mutex_); + + if (connected_api_ == kNoConnectedApi) { + ALOGE("queueBuffer: BufferQueue has no connected producer"); + return NO_INIT; + } + + if (slot < 0 || slot >= max_buffer_count_) { + ALOGE("queueBuffer: slot index %d out of range [0, %d)", slot, + max_buffer_count_); + return BAD_VALUE; + } else if (!buffers_[slot].mBufferState.isDequeued()) { + ALOGE("queueBuffer: slot %d is not owned by the producer (state = %s)", + slot, buffers_[slot].mBufferState.string()); + return BAD_VALUE; + } else if ((!buffers_[slot].mRequestBufferCalled || + buffers_[slot].mGraphicBuffer == nullptr)) { + ALOGE( + "queueBuffer: slot %d is not requested (mRequestBufferCalled=%d, " + "mGraphicBuffer=%p)", + slot, buffers_[slot].mRequestBufferCalled, + buffers_[slot].mGraphicBuffer.get()); + return BAD_VALUE; + } + + // Post the buffer producer with timestamp in the metadata. + const auto& buffer_producer = buffers_[slot].mBufferProducer; + + // Check input crop is not out of boundary of current buffer. + Rect buffer_rect(buffer_producer->width(), buffer_producer->height()); + Rect cropped_rect(Rect::EMPTY_RECT); + crop.intersect(buffer_rect, &cropped_rect); + if (cropped_rect != crop) { + ALOGE("queueBuffer: slot %d has out-of-boundary crop.", slot); + return BAD_VALUE; + } + + LocalHandle fence_fd(fence->isValid() ? fence->dup() : -1); + + DvrNativeBufferMetadata meta_data = {}; + meta_data.timestamp = timestamp; + meta_data.is_auto_timestamp = static_cast<int32_t>(is_auto_timestamp); + meta_data.dataspace = static_cast<int32_t>(dataspace); + meta_data.crop_left = crop.left; + meta_data.crop_top = crop.top; + meta_data.crop_right = crop.right; + meta_data.crop_bottom = crop.bottom; + meta_data.scaling_mode = static_cast<int32_t>(scaling_mode); + meta_data.transform = static_cast<int32_t>(transform); + + buffer_producer->Post(fence_fd, &meta_data, sizeof(meta_data)); + buffers_[slot].mBufferState.queue(); + + output->width = buffer_producer->width(); + output->height = buffer_producer->height(); + output->transformHint = 0; // default value, we don't use it yet. + + // |numPendingBuffers| counts of the number of buffers that has been enqueued + // by the producer but not yet acquired by the consumer. Due to the nature + // of BufferHubQueue design, this is hard to trace from the producer's client + // side, but it's safe to assume it's zero. + output->numPendingBuffers = 0; + + // Note that we are not setting nextFrameNumber here as it seems to be only + // used by surface flinger. See more at b/22802885, ag/791760. + output->nextFrameNumber = 0; + + return NO_ERROR; +} + +status_t BufferHubQueueProducer::cancelBuffer(int slot, + const sp<Fence>& fence) { + ALOGD_IF(TRACE, __FUNCTION__); + + std::unique_lock<std::mutex> lock(mutex_); + + if (connected_api_ == kNoConnectedApi) { + ALOGE("cancelBuffer: BufferQueue has no connected producer"); + return NO_INIT; + } + + if (slot < 0 || slot >= max_buffer_count_) { + ALOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, + max_buffer_count_); + return BAD_VALUE; + } else if (!buffers_[slot].mBufferState.isDequeued()) { + ALOGE("cancelBuffer: slot %d is not owned by the producer (state = %s)", + slot, buffers_[slot].mBufferState.string()); + return BAD_VALUE; + } else if (fence == nullptr) { + ALOGE("cancelBuffer: fence is NULL"); + return BAD_VALUE; + } + + auto buffer_producer = buffers_[slot].mBufferProducer; + queue_->Enqueue(buffer_producer, slot); + buffers_[slot].mBufferState.cancel(); + buffers_[slot].mFence = fence; + ALOGD_IF(TRACE, "cancelBuffer: slot %d", slot); + + return NO_ERROR; +} + +status_t BufferHubQueueProducer::query(int what, int* out_value) { + ALOGD_IF(TRACE, __FUNCTION__); + + std::unique_lock<std::mutex> lock(mutex_); + + if (out_value == nullptr) { + ALOGE("query: out_value was NULL"); + return BAD_VALUE; + } + + int value = 0; + switch (what) { + case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: + // TODO(b/36187402) This should be the maximum number of buffers that this + // producer queue's consumer can acquire. Set to be at least one. Need to + // find a way to set from the consumer side. + value = kDefaultUndequeuedBuffers; + break; + case NATIVE_WINDOW_BUFFER_AGE: + value = 0; + break; + case NATIVE_WINDOW_WIDTH: + value = queue_->default_width(); + break; + case NATIVE_WINDOW_HEIGHT: + value = queue_->default_height(); + break; + case NATIVE_WINDOW_FORMAT: + value = queue_->default_format(); + break; + case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: + // BufferHubQueue is always operating in async mode, thus semantically + // consumer can never be running behind. See BufferQueueCore.cpp core + // for more information about the original meaning of this flag. + value = 0; + break; + case NATIVE_WINDOW_CONSUMER_USAGE_BITS: + // TODO(jwcai) This is currently not implement as we don't need + // IGraphicBufferConsumer parity. + value = 0; + break; + case NATIVE_WINDOW_DEFAULT_DATASPACE: + // TODO(jwcai) Return the default value android::BufferQueue is using as + // there is no way dvr::ConsumerQueue can set it. + value = 0; // HAL_DATASPACE_UNKNOWN + break; + case NATIVE_WINDOW_STICKY_TRANSFORM: + // TODO(jwcai) Return the default value android::BufferQueue is using as + // there is no way dvr::ConsumerQueue can set it. + value = 0; + break; + case NATIVE_WINDOW_CONSUMER_IS_PROTECTED: + // In Daydream's implementation, the consumer end (i.e. VR Compostior) + // knows how to handle protected buffers. + value = 1; + break; + default: + return BAD_VALUE; + } + + ALOGD_IF(TRACE, "query: key=%d, v=%d", what, value); + *out_value = value; + return NO_ERROR; +} + +status_t BufferHubQueueProducer::connect( + const sp<IProducerListener>& /* listener */, int api, + bool /* producer_controlled_by_app */, QueueBufferOutput* output) { + // Consumer interaction are actually handled by buffer hub, and we need + // to maintain consumer operations here. We only need to perform basic input + // parameter checks here. + ALOGD_IF(TRACE, __FUNCTION__); + + if (output == nullptr) { + return BAD_VALUE; + } + + std::unique_lock<std::mutex> lock(mutex_); + + if (connected_api_ != kNoConnectedApi) { + return BAD_VALUE; + } + + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + connected_api_ = api; + + output->width = queue_->default_width(); + output->height = queue_->default_height(); + + // default values, we don't use them yet. + output->transformHint = 0; + output->numPendingBuffers = 0; + output->nextFrameNumber = 0; + output->bufferReplaced = false; + + break; + default: + ALOGE("BufferHubQueueProducer::connect: unknow API %d", api); + return BAD_VALUE; + } + + return NO_ERROR; +} + +status_t BufferHubQueueProducer::disconnect(int api, DisconnectMode /*mode*/) { + // Consumer interaction are actually handled by buffer hub, and we need + // to maintain consumer operations here. We only need to perform basic input + // parameter checks here. + ALOGD_IF(TRACE, __FUNCTION__); + + std::unique_lock<std::mutex> lock(mutex_); + + if (kNoConnectedApi == connected_api_) { + return NO_INIT; + } else if (api != connected_api_) { + return BAD_VALUE; + } + + connected_api_ = kNoConnectedApi; + return NO_ERROR; +} + +status_t BufferHubQueueProducer::setSidebandStream( + const sp<NativeHandle>& stream) { + if (stream != nullptr) { + // TODO(jwcai) Investigate how is is used, maybe use BufferHubBuffer's + // metadata. + ALOGE("SidebandStream is not currently supported."); + return INVALID_OPERATION; + } + return NO_ERROR; +} + +void BufferHubQueueProducer::allocateBuffers(uint32_t /* width */, + uint32_t /* height */, + PixelFormat /* format */, + uint32_t /* usage */) { + // TODO(jwcai) |allocateBuffers| aims to preallocate up to the maximum number + // of buffers permitted by the current BufferQueue configuration (aka + // |max_buffer_count_|). + ALOGE("BufferHubQueueProducer::allocateBuffers not implemented."); +} + +status_t BufferHubQueueProducer::allowAllocation(bool /* allow */) { + ALOGE("BufferHubQueueProducer::allowAllocation not implemented."); + return INVALID_OPERATION; +} + +status_t BufferHubQueueProducer::setGenerationNumber( + uint32_t generation_number) { + ALOGD_IF(TRACE, __FUNCTION__); + + std::unique_lock<std::mutex> lock(mutex_); + generation_number_ = generation_number; + return NO_ERROR; +} + +String8 BufferHubQueueProducer::getConsumerName() const { + // BufferHub based implementation could have one to many producer/consumer + // relationship, thus |getConsumerName| from the producer side does not + // make any sense. + ALOGE("BufferHubQueueProducer::getConsumerName not supported."); + return String8("BufferHubQueue::DummyConsumer"); +} + +status_t BufferHubQueueProducer::setSharedBufferMode(bool shared_buffer_mode) { + if (shared_buffer_mode) { + ALOGE( + "BufferHubQueueProducer::setSharedBufferMode(true) is not supported."); + // TODO(b/36373181) Front buffer mode for buffer hub queue as ANativeWindow. + return INVALID_OPERATION; + } + // Setting to default should just work as a no-op. + return NO_ERROR; +} + +status_t BufferHubQueueProducer::setAutoRefresh(bool auto_refresh) { + if (auto_refresh) { + ALOGE("BufferHubQueueProducer::setAutoRefresh(true) is not supported."); + return INVALID_OPERATION; + } + // Setting to default should just work as a no-op. + return NO_ERROR; +} + +status_t BufferHubQueueProducer::setDequeueTimeout(nsecs_t timeout) { + ALOGD_IF(TRACE, __FUNCTION__); + + std::unique_lock<std::mutex> lock(mutex_); + dequeue_timeout_ms_ = static_cast<int>(timeout / (1000 * 1000)); + return NO_ERROR; +} + +status_t BufferHubQueueProducer::getLastQueuedBuffer( + sp<GraphicBuffer>* /* out_buffer */, sp<Fence>* /* out_fence */, + float /*out_transform_matrix*/[16]) { + ALOGE("BufferHubQueueProducer::getLastQueuedBuffer not implemented."); + return INVALID_OPERATION; +} + +void BufferHubQueueProducer::getFrameTimestamps( + FrameEventHistoryDelta* /*outDelta*/) { + ALOGE("BufferHubQueueProducer::getFrameTimestamps not implemented."); +} + +status_t BufferHubQueueProducer::getUniqueId(uint64_t* out_id) const { + ALOGD_IF(TRACE, __FUNCTION__); + + *out_id = unique_id_; + return NO_ERROR; +} + +status_t BufferHubQueueProducer::AllocateBuffer(uint32_t width, uint32_t height, + uint32_t layer_count, + PixelFormat format, + uint64_t usage) { + size_t slot; + + if (queue_->AllocateBuffer(width, height, layer_count, format, usage, &slot) < + 0) { + ALOGE("Failed to allocate new buffer in BufferHub."); + return NO_MEMORY; + } + + auto buffer_producer = queue_->GetBuffer(slot); + + LOG_ALWAYS_FATAL_IF(buffer_producer == nullptr, + "Failed to get buffer producer at slot: %zu", slot); + + buffers_[slot].mBufferProducer = buffer_producer; + + return NO_ERROR; +} + +status_t BufferHubQueueProducer::RemoveBuffer(size_t slot) { + int ret = queue_->DetachBuffer(slot); + if (ret < 0) { + ALOGE("BufferHubQueueProducer::RemoveBuffer failed through RPC, ret=%s", + strerror(-ret)); + return ret; + } + + // Reset in memory objects related the the buffer. + buffers_[slot].mBufferProducer = nullptr; + buffers_[slot].mGraphicBuffer = nullptr; + buffers_[slot].mBufferState.detachProducer(); + return NO_ERROR; +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h new file mode 100644 index 0000000000..ed67f79951 --- /dev/null +++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h @@ -0,0 +1,456 @@ +#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ +#define ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ + +#include <gui/BufferQueueDefs.h> + +#include <pdx/client.h> +#include <pdx/status.h> +#include <private/dvr/buffer_hub_client.h> +#include <private/dvr/epoll_file_descriptor.h> +#include <private/dvr/ring_buffer.h> + +#include <memory> +#include <vector> + +namespace android { +namespace dvr { + +class ConsumerQueue; + +// |BufferHubQueue| manages a queue of |BufferHubBuffer|s. Buffers are +// automatically re-requeued when released by the remote side. +class BufferHubQueue : public pdx::Client { + public: + using LocalHandle = pdx::LocalHandle; + using LocalChannelHandle = pdx::LocalChannelHandle; + template <typename T> + using Status = pdx::Status<T>; + + virtual ~BufferHubQueue() {} + void Initialize(); + + // Create a new consumer queue that is attached to the producer. Returns + // a new consumer queue client or nullptr on failure. + std::unique_ptr<ConsumerQueue> CreateConsumerQueue(); + + // Create a new consumer queue that is attached to the producer. This queue + // sets each of its imported consumer buffers to the ignored state to avoid + // participation in lifecycle events. + std::unique_ptr<ConsumerQueue> CreateSilentConsumerQueue(); + + // Return the default buffer width of this buffer queue. + size_t default_width() const { return default_width_; } + + // Return the default buffer height of this buffer queue. + size_t default_height() const { return default_height_; } + + // Return the default buffer format of this buffer queue. + int32_t default_format() const { return default_format_; } + + // Create a new consumer in handle form for immediate transport over RPC. + Status<LocalChannelHandle> CreateConsumerQueueHandle(); + + // Return the number of buffers avaiable for dequeue. + size_t count() const { return available_buffers_.GetSize(); } + + // Return the total number of buffers that the queue is tracking. + size_t capacity() const { return capacity_; } + + // Return the size of metadata structure associated with this BufferBubQueue. + size_t metadata_size() const { return meta_size_; } + + // Return whether the buffer queue is alrady full. + bool is_full() const { return available_buffers_.IsFull(); } + + explicit operator bool() const { return epoll_fd_.IsValid(); } + + std::shared_ptr<BufferHubBuffer> GetBuffer(size_t slot) const { + return buffers_[slot]; + } + + Status<int> GetEventMask(int events) { + if (auto* client_channel = GetChannel()) { + return client_channel->GetEventMask(events); + } else { + return pdx::ErrorStatus(EINVAL); + } + } + + // Returns an fd that signals pending queue events using + // EPOLLIN/POLLIN/readible. Either HandleQueueEvents or WaitForBuffers may be + // called to handle pending queue events. + int queue_fd() const { return epoll_fd_.Get(); } + + // Handles any pending events, returning available buffers to the queue and + // reaping disconnected buffers. Returns true if successful, false if an error + // occurred. + bool HandleQueueEvents() { return WaitForBuffers(0); } + + // Enqueue a buffer marks buffer to be available (|Gain|'ed for producer + // and |Acquire|'ed for consumer. This is only used for internal bookkeeping. + void Enqueue(const std::shared_ptr<BufferHubBuffer>& buf, size_t slot); + + // |BufferHubQueue| will keep track of at most this value of buffers. + static constexpr size_t kMaxQueueCapacity = + android::BufferQueueDefs::NUM_BUFFER_SLOTS; + + // Special epoll data field indicating that the epoll event refers to the + // queue. + static constexpr int64_t kEpollQueueEventIndex = -1; + + // When pass |kNoTimeout| to |Dequeue|, it will block indefinitely without a + // timeout. + static constexpr int kNoTimeOut = -1; + + int id() const { return id_; } + bool hung_up() const { return hung_up_; } + + protected: + BufferHubQueue(LocalChannelHandle channel); + BufferHubQueue(const std::string& endpoint_path); + + // Imports the queue parameters by querying BufferHub for the parameters for + // this channel. + Status<void> ImportQueue(); + + // Sets up the queue with the given parameters. + void SetupQueue(size_t meta_size_bytes_, int id); + + // Called by ProducerQueue::AddBuffer and ConsumerQueue::AddBuffer only. to + // register a buffer for epoll and internal bookkeeping. + int AddBuffer(const std::shared_ptr<BufferHubBuffer>& buf, size_t slot); + + // Called by ProducerQueue::DetachBuffer and ConsumerQueue::DetachBuffer only. + // to deregister a buffer for epoll and internal bookkeeping. + virtual int DetachBuffer(size_t slot); + + // Dequeue a buffer from the free queue, blocking until one is available. The + // timeout argument specifies the number of milliseconds that |Dequeue()| will + // block. Specifying a timeout of -1 causes |Dequeue()| to block indefinitely, + // while specifying a timeout equal to zero cause |Dequeue()| to return + // immediately, even if no buffers are available. + pdx::Status<std::shared_ptr<BufferHubBuffer>> Dequeue(int timeout, + size_t* slot, + void* meta, + LocalHandle* fence); + + // Wait for buffers to be released and re-add them to the queue. + bool WaitForBuffers(int timeout); + void HandleBufferEvent(size_t slot, int poll_events); + void HandleQueueEvent(int poll_events); + + virtual int OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf, + LocalHandle* fence) = 0; + + // Called when a buffer is allocated remotely. + virtual Status<void> OnBufferAllocated() { return {}; } + + // Data members to handle arbitrary metadata passed through BufferHub. It is + // fair to enforce that all buffers in the same queue share the same metadata + // type. |meta_size_| is used to store the size of metadata on queue creation; + // and |meta_buffer_tmp_| is allocated and resized to |meta_size_| on queue + // creation to be later used as temporary space so that we can avoid + // additional dynamic memory allocation in each |Enqueue| and |Dequeue| call. + size_t meta_size_; + + // Here we intentionally choose |unique_ptr<uint8_t[]>| over vector<uint8_t> + // to disallow dynamic resizing for stability reasons. + std::unique_ptr<uint8_t[]> meta_buffer_tmp_; + + private: + static constexpr size_t kMaxEvents = 128; + + // The |u64| data field of an epoll event is interpreted as int64_t: + // When |index| >= 0 and |index| < kMaxQueueCapacity it refers to a specific + // element of |buffers_| as a direct index; + static bool is_buffer_event_index(int64_t index) { + return index >= 0 && + index < static_cast<int64_t>(BufferHubQueue::kMaxQueueCapacity); + } + + // When |index| == kEpollQueueEventIndex, it refers to the queue itself. + static bool is_queue_event_index(int64_t index) { + return index == BufferHubQueue::kEpollQueueEventIndex; + } + + struct BufferInfo { + // A logical slot number that is assigned to a buffer at allocation time. + // The slot number remains unchanged during the entire life cycle of the + // buffer and should not be confused with the enqueue and dequeue order. + size_t slot; + + // A BufferHubBuffer client. + std::shared_ptr<BufferHubBuffer> buffer; + + // Metadata associated with the buffer. + std::unique_ptr<uint8_t[]> metadata; + + BufferInfo() : BufferInfo(-1, 0) {} + + BufferInfo(size_t slot, size_t metadata_size) + : slot(slot), + buffer(nullptr), + metadata(metadata_size ? new uint8_t[metadata_size] : nullptr) {} + + BufferInfo(BufferInfo&& other) + : slot(other.slot), + buffer(std::move(other.buffer)), + metadata(std::move(other.metadata)) {} + + BufferInfo& operator=(BufferInfo&& other) { + slot = other.slot; + buffer = std::move(other.buffer); + metadata = std::move(other.metadata); + return *this; + } + + private: + BufferInfo(const BufferInfo&) = delete; + void operator=(BufferInfo&) = delete; + }; + + // Default buffer width that can be set to override the buffer width when a + // width and height of 0 are specified in AllocateBuffer. + size_t default_width_{1}; + + // Default buffer height that can be set to override the buffer height when a + // width and height of 0 are specified in AllocateBuffer. + size_t default_height_{1}; + + // Default buffer format that can be set to override the buffer format when it + // isn't specified in AllocateBuffer. + int32_t default_format_{PIXEL_FORMAT_RGBA_8888}; + + // Buffer queue: + // |buffers_| tracks all |BufferHubBuffer|s created by this |BufferHubQueue|. + std::vector<std::shared_ptr<BufferHubBuffer>> buffers_; + + // |epollhup_pending_| tracks whether a slot of |buffers_| get detached before + // its corresponding EPOLLHUP event got handled. This could happen as the + // following sequence: + // 1. Producer queue's client side allocates a new buffer (at slot 1). + // 2. Producer queue's client side replaces an existing buffer (at slot 0). + // This is implemented by first detaching the buffer and then allocating a + // new buffer. + // 3. During the same epoll_wait, Consumer queue's client side gets EPOLLIN + // event on the queue which indicates a new buffer is available and the + // EPOLLHUP event for slot 0. Consumer handles these two events in order. + // 4. Consumer client calls BufferHubRPC::ConsumerQueueImportBuffers and both + // slot 0 and (the new) slot 1 buffer will be imported. During the import + // of the buffer at slot 1, consumer client detaches the old buffer so that + // the new buffer can be registered. At the same time + // |epollhup_pending_[slot]| is marked to indicate that buffer at this slot + // was detached prior to EPOLLHUP event. + // 5. Consumer client continues to handle the EPOLLHUP. Since + // |epollhup_pending_[slot]| is marked as true, it can safely ignore the + // event without detaching the newly allocated buffer at slot 1. + // + // In normal situations where the previously described sequence doesn't + // happen, an EPOLLHUP event should trigger a regular buffer detach. + std::vector<bool> epollhup_pending_; + + // |available_buffers_| uses |dvr::RingBuffer| to implementation queue + // sematics. When |Dequeue|, we pop the front element from + // |available_buffers_|, and that buffer's reference count will decrease by + // one, while another reference in |buffers_| keeps the last reference to + // prevent the buffer from being deleted. + RingBuffer<BufferInfo> available_buffers_; + + // Fences (acquire fence for consumer and release fence for consumer) , one + // for each buffer slot. + std::vector<LocalHandle> fences_; + + // Keep track with how many buffers have been added into the queue. + size_t capacity_; + + // Epoll fd used to wait for BufferHub events. + EpollFileDescriptor epoll_fd_; + + // Flag indicating that the other side hung up. For ProducerQueues this + // triggers when BufferHub dies or explicitly closes the queue channel. For + // ConsumerQueues this can either mean the same or that the ProducerQueue on + // the other end hung up. + bool hung_up_{false}; + + // Global id for the queue that is consistent across processes. + int id_; + + BufferHubQueue(const BufferHubQueue&) = delete; + void operator=(BufferHubQueue&) = delete; +}; + +class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> { + public: + template <typename Meta> + static std::unique_ptr<ProducerQueue> Create() { + return BASE::Create(sizeof(Meta)); + } + static std::unique_ptr<ProducerQueue> Create(size_t meta_size_bytes) { + return BASE::Create(meta_size_bytes); + } + + // Usage bits in |usage_set_mask| will be automatically masked on. Usage bits + // in |usage_clear_mask| will be automatically masked off. Note that + // |usage_set_mask| and |usage_clear_mask| may conflict with each other, but + // |usage_set_mask| takes precedence over |usage_clear_mask|. All buffer + // allocation through this producer queue shall not have any of the usage bits + // in |usage_deny_set_mask| set. Allocation calls violating this will be + // rejected. All buffer allocation through this producer queue must have all + // the usage bits in |usage_deny_clear_mask| set. Allocation calls violating + // this will be rejected. Note that |usage_deny_set_mask| and + // |usage_deny_clear_mask| shall not conflict with each other. Such + // configuration will be treated as invalid input on creation. + template <typename Meta> + static std::unique_ptr<ProducerQueue> Create(uint32_t usage_set_mask, + uint32_t usage_clear_mask, + uint32_t usage_deny_set_mask, + uint32_t usage_deny_clear_mask) { + return BASE::Create(sizeof(Meta), usage_set_mask, usage_clear_mask, + usage_deny_set_mask, usage_deny_clear_mask); + } + static std::unique_ptr<ProducerQueue> Create(size_t meta_size_bytes, + uint32_t usage_set_mask, + uint32_t usage_clear_mask, + uint32_t usage_deny_set_mask, + uint32_t usage_deny_clear_mask) { + return BASE::Create(meta_size_bytes, usage_set_mask, usage_clear_mask, + usage_deny_set_mask, usage_deny_clear_mask); + } + + // Import a |ProducerQueue| from a channel handle. + static std::unique_ptr<ProducerQueue> Import(LocalChannelHandle handle) { + return BASE::Create(std::move(handle)); + } + + // Get a buffer producer. Note that the method doesn't check whether the + // buffer slot has a valid buffer that has been allocated already. When no + // buffer has been imported before it returns |nullptr|; otherwise it returns + // a shared pointer to a |BufferProducer|. + std::shared_ptr<BufferProducer> GetBuffer(size_t slot) const { + return std::static_pointer_cast<BufferProducer>( + BufferHubQueue::GetBuffer(slot)); + } + + // Allocate producer buffer to populate the queue. Once allocated, a producer + // buffer is automatically enqueue'd into the ProducerQueue and available to + // use (i.e. in |Gain|'ed mode). + // Returns Zero on success and negative error code when buffer allocation + // fails. + int AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count, + uint32_t format, uint64_t usage, size_t* out_slot); + + // Add a producer buffer to populate the queue. Once added, a producer buffer + // is available to use (i.e. in |Gain|'ed mode). + int AddBuffer(const std::shared_ptr<BufferProducer>& buf, size_t slot); + + // Detach producer buffer from the queue. + // Returns Zero on success and negative error code when buffer detach + // fails. + int DetachBuffer(size_t slot) override; + + // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode, + // and caller should call Post() once it's done writing to release the buffer + // to the consumer side. + pdx::Status<std::shared_ptr<BufferProducer>> Dequeue( + int timeout, size_t* slot, LocalHandle* release_fence); + + private: + friend BASE; + + // Constructors are automatically exposed through ProducerQueue::Create(...) + // static template methods inherited from ClientBase, which take the same + // arguments as the constructors. + explicit ProducerQueue(size_t meta_size); + ProducerQueue(LocalChannelHandle handle); + ProducerQueue(size_t meta_size, uint64_t usage_set_mask, + uint64_t usage_clear_mask, uint64_t usage_deny_set_mask, + uint64_t usage_deny_clear_mask); + + int OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf, + LocalHandle* release_fence) override; +}; + +// Explicit specializations of ProducerQueue::Create for void metadata type. +template <> +inline std::unique_ptr<ProducerQueue> ProducerQueue::Create<void>() { + return ProducerQueue::Create(0); +} +template <> +inline std::unique_ptr<ProducerQueue> ProducerQueue::Create<void>( + uint32_t usage_set_mask, uint32_t usage_clear_mask, + uint32_t usage_deny_set_mask, uint32_t usage_deny_clear_mask) { + return ProducerQueue::Create(0, usage_set_mask, usage_clear_mask, + usage_deny_set_mask, usage_deny_clear_mask); +} + +class ConsumerQueue : public BufferHubQueue { + public: + // Get a buffer consumer. Note that the method doesn't check whether the + // buffer slot has a valid buffer that has been imported already. When no + // buffer has been imported before it returns nullptr; otherwise returns a + // shared pointer to a BufferConsumer. + std::shared_ptr<BufferConsumer> GetBuffer(size_t slot) const { + return std::static_pointer_cast<BufferConsumer>( + BufferHubQueue::GetBuffer(slot)); + } + + // Import a ConsumerQueue from a channel handle. |ignore_on_import| controls + // whether or not buffers are set to be ignored when imported. This may be + // used to avoid participation in the buffer lifecycle by a consumer queue + // that is only used to spawn other consumer queues, such as in an + // intermediate service. + static std::unique_ptr<ConsumerQueue> Import(LocalChannelHandle handle, + bool ignore_on_import = false) { + return std::unique_ptr<ConsumerQueue>( + new ConsumerQueue(std::move(handle), ignore_on_import)); + } + + // Import newly created buffers from the service side. + // Returns number of buffers successfully imported or an error. + Status<size_t> ImportBuffers(); + + // Dequeue a consumer buffer to read. The returned buffer in |Acquired|'ed + // mode, and caller should call Releasse() once it's done writing to release + // the buffer to the producer side. |meta| is passed along from BufferHub, + // The user of BufferProducer is responsible with making sure that the + // Dequeue() is done with the corect metadata type and size with those used + // when the buffer is orignally created. + template <typename Meta> + pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue( + int timeout, size_t* slot, Meta* meta, LocalHandle* acquire_fence) { + return Dequeue(timeout, slot, meta, sizeof(*meta), acquire_fence); + } + pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue( + int timeout, size_t* slot, LocalHandle* acquire_fence) { + return Dequeue(timeout, slot, nullptr, 0, acquire_fence); + } + + pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue( + int timeout, size_t* slot, void* meta, size_t meta_size, + LocalHandle* acquire_fence); + + private: + friend BufferHubQueue; + + ConsumerQueue(LocalChannelHandle handle, bool ignore_on_import = false); + + // Add a consumer buffer to populate the queue. Once added, a consumer buffer + // is NOT available to use until the producer side |Post| it. |WaitForBuffers| + // will catch the |Post| and |Acquire| the buffer to make it available for + // consumer. + int AddBuffer(const std::shared_ptr<BufferConsumer>& buf, size_t slot); + + int OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf, + LocalHandle* acquire_fence) override; + + Status<void> OnBufferAllocated() override; + + // Flag indicating that imported (consumer) buffers should be ignored when + // imported to avoid participating in the buffer ownership flow. + bool ignore_on_import_; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h new file mode 100644 index 0000000000..7890176f04 --- /dev/null +++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h @@ -0,0 +1,183 @@ +#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_ +#define ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_ + +#include <gui/IGraphicBufferProducer.h> +#include <private/dvr/buffer_hub_queue_client.h> + +namespace android { +namespace dvr { + +class BufferHubQueueProducer : public BnGraphicBufferProducer { + public: + static constexpr int kNoConnectedApi = -1; + + // TODO(b/36187402) The actual implementation of BufferHubQueue's consumer + // side logic doesn't limit the number of buffer it can acquire + // simultaneously. We need a way for consumer logic to configure and enforce + // that. + static constexpr int kDefaultUndequeuedBuffers = 1; + + // Create a BufferHubQueueProducer instance by creating a new producer queue. + static sp<BufferHubQueueProducer> Create(); + + // Create a BufferHubQueueProducer instance by importing an existing prodcuer + // queue. + static sp<BufferHubQueueProducer> Create( + const std::shared_ptr<ProducerQueue>& producer); + + // See |IGraphicBufferProducer::requestBuffer| + status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override; + + // For the BufferHub based implementation. All buffers in the queue are + // allowed to be dequeued from the consumer side. It call always returns + // 0 for |NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS| query. Thus setting + // |max_dequeued_buffers| here can be considered the same as setting queue + // capacity. + // + // See |IGraphicBufferProducer::setMaxDequeuedBufferCount| for more info + status_t setMaxDequeuedBufferCount(int max_dequeued_buffers) override; + + // See |IGraphicBufferProducer::setAsyncMode| + status_t setAsyncMode(bool async) override; + + // See |IGraphicBufferProducer::dequeueBuffer| + status_t dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width, + uint32_t height, PixelFormat format, uint32_t usage, + FrameEventHistoryDelta* outTimestamps) override; + + // See |IGraphicBufferProducer::detachBuffer| + status_t detachBuffer(int slot) override; + + // See |IGraphicBufferProducer::detachNextBuffer| + status_t detachNextBuffer(sp<GraphicBuffer>* out_buffer, + sp<Fence>* out_fence) override; + + // See |IGraphicBufferProducer::attachBuffer| + status_t attachBuffer(int* out_slot, + const sp<GraphicBuffer>& buffer) override; + + // See |IGraphicBufferProducer::queueBuffer| + status_t queueBuffer(int slot, const QueueBufferInput& input, + QueueBufferOutput* output) override; + + // See |IGraphicBufferProducer::cancelBuffer| + status_t cancelBuffer(int slot, const sp<Fence>& fence) override; + + // See |IGraphicBufferProducer::query| + status_t query(int what, int* out_value) override; + + // See |IGraphicBufferProducer::connect| + status_t connect(const sp<IProducerListener>& listener, int api, + bool producer_controlled_by_app, + QueueBufferOutput* output) override; + + // See |IGraphicBufferProducer::disconnect| + status_t disconnect(int api, + DisconnectMode mode = DisconnectMode::Api) override; + + // See |IGraphicBufferProducer::setSidebandStream| + status_t setSidebandStream(const sp<NativeHandle>& stream) override; + + // See |IGraphicBufferProducer::allocateBuffers| + void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format, + uint32_t usage) override; + + // See |IGraphicBufferProducer::allowAllocation| + status_t allowAllocation(bool allow) override; + + // See |IGraphicBufferProducer::setGenerationNumber| + status_t setGenerationNumber(uint32_t generation_number) override; + + // See |IGraphicBufferProducer::getConsumerName| + String8 getConsumerName() const override; + + // See |IGraphicBufferProducer::setSharedBufferMode| + status_t setSharedBufferMode(bool shared_buffer_mode) override; + + // See |IGraphicBufferProducer::setAutoRefresh| + status_t setAutoRefresh(bool auto_refresh) override; + + // See |IGraphicBufferProducer::setDequeueTimeout| + status_t setDequeueTimeout(nsecs_t timeout) override; + + // See |IGraphicBufferProducer::getLastQueuedBuffer| + status_t getLastQueuedBuffer(sp<GraphicBuffer>* out_buffer, + sp<Fence>* out_fence, + float out_transform_matrix[16]) override; + + // See |IGraphicBufferProducer::getFrameTimestamps| + void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) override; + + // See |IGraphicBufferProducer::getUniqueId| + status_t getUniqueId(uint64_t* out_id) const override; + + private: + using LocalHandle = pdx::LocalHandle; + + // Private constructor to force use of |Create|. + BufferHubQueueProducer() {} + + static uint64_t genUniqueId() { + static std::atomic<uint32_t> counter{0}; + static uint64_t id = static_cast<uint64_t>(getpid()) << 32; + return id | counter++; + } + + // Allocate new buffer through BufferHub and add it into |queue_| for + // bookkeeping. + status_t AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count, + PixelFormat format, uint64_t usage); + + // Remove a buffer via BufferHubRPC. + status_t RemoveBuffer(size_t slot); + + // Concreate implementation backed by BufferHubBuffer. + std::shared_ptr<ProducerQueue> queue_; + + // Mutex for thread safety. + std::mutex mutex_; + + // Connect client API, should be one of the NATIVE_WINDOW_API_* flags. + int connected_api_{kNoConnectedApi}; + + // |max_buffer_count_| sets the capacity of the underlying buffer queue. + int32_t max_buffer_count_{BufferHubQueue::kMaxQueueCapacity}; + + // |max_dequeued_buffer_count_| set the maximum number of buffers that can + // be dequeued at the same momment. + int32_t max_dequeued_buffer_count_{1}; + + // Sets how long dequeueBuffer or attachBuffer will block if a buffer or + // slot is not yet available. The timeout is stored in milliseconds. + int dequeue_timeout_ms_{BufferHubQueue::kNoTimeOut}; + + // |generation_number_| stores the current generation number of the attached + // producer. Any attempt to attach a buffer with a different generation + // number will fail. + // TOOD(b/38137191) Currently not used as we don't support + // IGraphicBufferProducer::detachBuffer. + uint32_t generation_number_{0}; + + // |buffers_| stores the buffers that have been dequeued from + // |dvr::BufferHubQueue|, It is initialized to invalid buffers, and gets + // filled in with the result of |Dequeue|. + // TODO(jwcai) The buffer allocated to a slot will also be replaced if the + // requested buffer usage or geometry differs from that of the buffer + // allocated to a slot. + struct BufferHubSlot : public BufferSlot { + BufferHubSlot() : mBufferProducer(nullptr), mIsReallocating(false) {} + // BufferSlot comes from android framework, using m prefix to comply with + // the name convention with the reset of data fields from BufferSlot. + std::shared_ptr<BufferProducer> mBufferProducer; + bool mIsReallocating; + }; + BufferHubSlot buffers_[BufferHubQueue::kMaxQueueCapacity]; + + // A uniqueId used by IGraphicBufferProducer interface. + const uint64_t unique_id_{genUniqueId()}; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_ diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp new file mode 100644 index 0000000000..865573cafd --- /dev/null +++ b/libs/vr/libbufferhubqueue/tests/Android.bp @@ -0,0 +1,48 @@ + + +shared_libraries = [ + "libbase", + "libbinder", + "libcutils", + "libgui", + "liblog", + "libhardware", + "libui", + "libutils", +] + +static_libraries = [ + "libbufferhubqueue", + "libbufferhub", + "libchrome", + "libdvrcommon", + "libpdx_default_transport", +] + +cc_test { + srcs: ["buffer_hub_queue-test.cpp"], + static_libs: static_libraries, + shared_libs: shared_libraries, + cflags: [ + "-DLOG_TAG=\"buffer_hub_queue-test\"", + "-DTRACE=0", + "-O0", + "-g", + ], + name: "buffer_hub_queue-test", + tags: ["optional"], +} + +cc_test { + srcs: ["buffer_hub_queue_producer-test.cpp"], + static_libs: static_libraries, + shared_libs: shared_libraries, + cflags: [ + "-DLOG_TAG=\"buffer_hub_queue_producer-test\"", + "-DTRACE=0", + "-O0", + "-g", + ], + name: "buffer_hub_queue_producer-test", + tags: ["optional"], +} diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp new file mode 100644 index 0000000000..fe0b12aa44 --- /dev/null +++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp @@ -0,0 +1,421 @@ +#include <base/logging.h> +#include <private/dvr/buffer_hub_client.h> +#include <private/dvr/buffer_hub_queue_client.h> + +#include <gtest/gtest.h> + +#include <vector> + +namespace android { +namespace dvr { + +using pdx::LocalHandle; + +namespace { + +constexpr int kBufferWidth = 100; +constexpr int kBufferHeight = 1; +constexpr int kBufferLayerCount = 1; +constexpr int kBufferFormat = HAL_PIXEL_FORMAT_BLOB; +constexpr int kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY; + +class BufferHubQueueTest : public ::testing::Test { + public: + template <typename Meta> + bool CreateProducerQueue(uint64_t usage_set_mask = 0, + uint64_t usage_clear_mask = 0, + uint64_t usage_deny_set_mask = 0, + uint64_t usage_deny_clear_mask = 0) { + producer_queue_ = + ProducerQueue::Create<Meta>(usage_set_mask, usage_clear_mask, + usage_deny_set_mask, usage_deny_clear_mask); + return producer_queue_ != nullptr; + } + + bool CreateConsumerQueue() { + if (producer_queue_) { + consumer_queue_ = producer_queue_->CreateConsumerQueue(); + return consumer_queue_ != nullptr; + } else { + return false; + } + } + + template <typename Meta> + bool CreateQueues(int usage_set_mask = 0, int usage_clear_mask = 0, + int usage_deny_set_mask = 0, + int usage_deny_clear_mask = 0) { + return CreateProducerQueue<Meta>(usage_set_mask, usage_clear_mask, + usage_deny_set_mask, + usage_deny_clear_mask) && + CreateConsumerQueue(); + } + + void AllocateBuffer() { + // Create producer buffer. + size_t slot; + int ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, + kBufferLayerCount, kBufferFormat, + kBufferUsage, &slot); + ASSERT_EQ(ret, 0); + } + + protected: + std::unique_ptr<ProducerQueue> producer_queue_; + std::unique_ptr<ConsumerQueue> consumer_queue_; +}; + +TEST_F(BufferHubQueueTest, TestDequeue) { + const size_t nb_dequeue_times = 16; + + ASSERT_TRUE(CreateQueues<size_t>()); + + // Allocate only one buffer. + AllocateBuffer(); + + // But dequeue multiple times. + for (size_t i = 0; i < nb_dequeue_times; i++) { + size_t slot; + LocalHandle fence; + auto p1_status = producer_queue_->Dequeue(0, &slot, &fence); + ASSERT_TRUE(p1_status.ok()); + auto p1 = p1_status.take(); + ASSERT_NE(nullptr, p1); + size_t mi = i; + ASSERT_EQ(p1->Post(LocalHandle(), &mi, sizeof(mi)), 0); + size_t mo; + auto c1_status = consumer_queue_->Dequeue(100, &slot, &mo, &fence); + ASSERT_TRUE(c1_status.ok()); + auto c1 = c1_status.take(); + ASSERT_NE(nullptr, c1); + ASSERT_EQ(mi, mo); + c1->Release(LocalHandle()); + } +} + +TEST_F(BufferHubQueueTest, TestProducerConsumer) { + const size_t nb_buffer = 16; + size_t slot; + uint64_t seq; + + ASSERT_TRUE(CreateQueues<uint64_t>()); + + for (size_t i = 0; i < nb_buffer; i++) { + AllocateBuffer(); + + // Producer queue has all the available buffers on initialize. + ASSERT_EQ(producer_queue_->count(), i + 1); + ASSERT_EQ(producer_queue_->capacity(), i + 1); + + // Consumer queue has no avaiable buffer on initialize. + ASSERT_EQ(consumer_queue_->count(), 0U); + // Consumer queue does not import buffers until a dequeue is issued. + ASSERT_EQ(consumer_queue_->capacity(), i); + // Dequeue returns timeout since no buffer is ready to consumer, but + // this implicitly triggers buffer import and bump up |capacity|. + LocalHandle fence; + auto status = consumer_queue_->Dequeue(0, &slot, &seq, &fence); + ASSERT_FALSE(status.ok()); + ASSERT_EQ(ETIMEDOUT, status.error()); + ASSERT_EQ(consumer_queue_->capacity(), i + 1); + } + + for (size_t i = 0; i < nb_buffer; i++) { + LocalHandle fence; + // First time, there is no buffer available to dequeue. + auto consumer_status = consumer_queue_->Dequeue(0, &slot, &seq, &fence); + ASSERT_FALSE(consumer_status.ok()); + ASSERT_EQ(ETIMEDOUT, consumer_status.error()); + + // Make sure Producer buffer is Post()'ed so that it's ready to Accquire + // in the consumer's Dequeue() function. + auto producer_status = producer_queue_->Dequeue(0, &slot, &fence); + ASSERT_TRUE(producer_status.ok()); + auto producer = producer_status.take(); + ASSERT_NE(nullptr, producer); + + uint64_t seq_in = static_cast<uint64_t>(i); + ASSERT_EQ(producer->Post({}, &seq_in, sizeof(seq_in)), 0); + + // Second time, the just |Post()|'ed buffer should be dequeued. + uint64_t seq_out = 0; + consumer_status = consumer_queue_->Dequeue(0, &slot, &seq_out, &fence); + ASSERT_TRUE(consumer_status.ok()); + auto consumer = consumer_status.take(); + ASSERT_NE(nullptr, consumer); + ASSERT_EQ(seq_in, seq_out); + } +} + +TEST_F(BufferHubQueueTest, TestMultipleConsumers) { + ASSERT_TRUE(CreateProducerQueue<void>()); + + // Allocate buffers. + const size_t kBufferCount = 4u; + for (size_t i = 0; i < kBufferCount; i++) { + AllocateBuffer(); + } + ASSERT_EQ(kBufferCount, producer_queue_->count()); + + // Build a silent consumer queue to test multi-consumer queue features. + auto silent_queue = producer_queue_->CreateSilentConsumerQueue(); + ASSERT_NE(nullptr, silent_queue); + + // Check that buffers are correctly imported on construction. + EXPECT_EQ(kBufferCount, silent_queue->capacity()); + + // Dequeue and post a buffer. + size_t slot; + LocalHandle fence; + auto producer_status = producer_queue_->Dequeue(0, &slot, &fence); + ASSERT_TRUE(producer_status.ok()); + auto producer_buffer = producer_status.take(); + ASSERT_NE(nullptr, producer_buffer); + ASSERT_EQ(0, producer_buffer->Post<void>({})); + + // Currently we expect no buffer to be available prior to calling + // WaitForBuffers/HandleQueueEvents. + // TODO(eieio): Note this behavior may change in the future. + EXPECT_EQ(0u, silent_queue->count()); + EXPECT_FALSE(silent_queue->HandleQueueEvents()); + EXPECT_EQ(0u, silent_queue->count()); + + // Build a new consumer queue to test multi-consumer queue features. + consumer_queue_ = silent_queue->CreateConsumerQueue(); + ASSERT_NE(nullptr, consumer_queue_); + + // Check that buffers are correctly imported on construction. + EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); + EXPECT_EQ(1u, consumer_queue_->count()); + + // Reclaim released/ignored buffers. + producer_queue_->HandleQueueEvents(); + ASSERT_EQ(kBufferCount - 1, producer_queue_->count()); + + // Post another buffer. + producer_status = producer_queue_->Dequeue(0, &slot, &fence); + ASSERT_TRUE(producer_status.ok()); + producer_buffer = producer_status.take(); + ASSERT_NE(nullptr, producer_buffer); + ASSERT_EQ(0, producer_buffer->Post<void>({})); + + // Verify that the consumer queue receives it. + EXPECT_EQ(1u, consumer_queue_->count()); + EXPECT_TRUE(consumer_queue_->HandleQueueEvents()); + EXPECT_EQ(2u, consumer_queue_->count()); + + // Dequeue and acquire/release (discard) buffers on the consumer end. + auto consumer_status = consumer_queue_->Dequeue(0, &slot, &fence); + ASSERT_TRUE(consumer_status.ok()); + auto consumer_buffer = consumer_status.take(); + ASSERT_NE(nullptr, consumer_buffer); + consumer_buffer->Discard(); + + // Buffer should be returned to the producer queue without being handled by + // the silent consumer queue. + EXPECT_EQ(1u, consumer_queue_->count()); + EXPECT_EQ(kBufferCount - 2, producer_queue_->count()); + EXPECT_TRUE(producer_queue_->HandleQueueEvents()); + EXPECT_EQ(kBufferCount - 1, producer_queue_->count()); +} + +struct TestMetadata { + char a; + int32_t b; + int64_t c; +}; + +TEST_F(BufferHubQueueTest, TestMetadata) { + ASSERT_TRUE(CreateQueues<TestMetadata>()); + AllocateBuffer(); + + std::vector<TestMetadata> ms = { + {'0', 0, 0}, {'1', 10, 3333}, {'@', 123, 1000000000}}; + + for (auto mi : ms) { + size_t slot; + LocalHandle fence; + auto p1_status = producer_queue_->Dequeue(0, &slot, &fence); + ASSERT_TRUE(p1_status.ok()); + auto p1 = p1_status.take(); + ASSERT_NE(nullptr, p1); + ASSERT_EQ(p1->Post(LocalHandle(-1), &mi, sizeof(mi)), 0); + TestMetadata mo; + auto c1_status = consumer_queue_->Dequeue(0, &slot, &mo, &fence); + ASSERT_TRUE(c1_status.ok()); + auto c1 = c1_status.take(); + ASSERT_EQ(mi.a, mo.a); + ASSERT_EQ(mi.b, mo.b); + ASSERT_EQ(mi.c, mo.c); + c1->Release(LocalHandle(-1)); + } +} + +TEST_F(BufferHubQueueTest, TestMetadataMismatch) { + ASSERT_TRUE(CreateQueues<int64_t>()); + AllocateBuffer(); + + int64_t mi = 3; + size_t slot; + LocalHandle fence; + auto p1_status = producer_queue_->Dequeue(0, &slot, &fence); + ASSERT_TRUE(p1_status.ok()); + auto p1 = p1_status.take(); + ASSERT_NE(nullptr, p1); + ASSERT_EQ(p1->Post(LocalHandle(-1), &mi, sizeof(mi)), 0); + + int32_t mo; + // Acquire a buffer with mismatched metadata is not OK. + auto c1_status = consumer_queue_->Dequeue(0, &slot, &mo, &fence); + ASSERT_FALSE(c1_status.ok()); +} + +TEST_F(BufferHubQueueTest, TestEnqueue) { + ASSERT_TRUE(CreateQueues<int64_t>()); + AllocateBuffer(); + + size_t slot; + LocalHandle fence; + auto p1_status = producer_queue_->Dequeue(0, &slot, &fence); + ASSERT_TRUE(p1_status.ok()); + auto p1 = p1_status.take(); + ASSERT_NE(nullptr, p1); + + int64_t mo; + producer_queue_->Enqueue(p1, slot); + auto c1_status = consumer_queue_->Dequeue(0, &slot, &mo, &fence); + ASSERT_FALSE(c1_status.ok()); +} + +TEST_F(BufferHubQueueTest, TestAllocateBuffer) { + ASSERT_TRUE(CreateQueues<int64_t>()); + + size_t s1; + AllocateBuffer(); + LocalHandle fence; + auto p1_status = producer_queue_->Dequeue(0, &s1, &fence); + ASSERT_TRUE(p1_status.ok()); + auto p1 = p1_status.take(); + ASSERT_NE(nullptr, p1); + + // producer queue is exhausted + size_t s2; + auto p2_status = producer_queue_->Dequeue(0, &s2, &fence); + ASSERT_FALSE(p2_status.ok()); + ASSERT_EQ(ETIMEDOUT, p2_status.error()); + + // dynamically add buffer. + AllocateBuffer(); + ASSERT_EQ(producer_queue_->count(), 1U); + ASSERT_EQ(producer_queue_->capacity(), 2U); + + // now we can dequeue again + p2_status = producer_queue_->Dequeue(0, &s2, &fence); + ASSERT_TRUE(p2_status.ok()); + auto p2 = p2_status.take(); + ASSERT_NE(nullptr, p2); + ASSERT_EQ(producer_queue_->count(), 0U); + // p1 and p2 should have different slot number + ASSERT_NE(s1, s2); + + // Consumer queue does not import buffers until |Dequeue| or |ImportBuffers| + // are called. So far consumer_queue_ should be empty. + ASSERT_EQ(consumer_queue_->count(), 0U); + + int64_t seq = 1; + ASSERT_EQ(p1->Post(LocalHandle(), seq), 0); + size_t cs1, cs2; + auto c1_status = consumer_queue_->Dequeue(0, &cs1, &seq, &fence); + ASSERT_TRUE(c1_status.ok()); + auto c1 = c1_status.take(); + ASSERT_NE(nullptr, c1); + ASSERT_EQ(consumer_queue_->count(), 0U); + ASSERT_EQ(consumer_queue_->capacity(), 2U); + ASSERT_EQ(cs1, s1); + + ASSERT_EQ(p2->Post(LocalHandle(), seq), 0); + auto c2_status = consumer_queue_->Dequeue(0, &cs2, &seq, &fence); + ASSERT_TRUE(c2_status.ok()); + auto c2 = c2_status.take(); + ASSERT_NE(nullptr, c2); + ASSERT_EQ(cs2, s2); +} + +TEST_F(BufferHubQueueTest, TestUsageSetMask) { + const uint32_t set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; + ASSERT_TRUE(CreateQueues<int64_t>(set_mask, 0, 0, 0)); + + // When allocation, leave out |set_mask| from usage bits on purpose. + size_t slot; + int ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, + kBufferFormat, kBufferLayerCount, + kBufferUsage & ~set_mask, &slot); + ASSERT_EQ(0, ret); + + LocalHandle fence; + auto p1_status = producer_queue_->Dequeue(0, &slot, &fence); + ASSERT_TRUE(p1_status.ok()); + auto p1 = p1_status.take(); + ASSERT_EQ(p1->usage() & set_mask, set_mask); +} + +TEST_F(BufferHubQueueTest, TestUsageClearMask) { + const uint32_t clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; + ASSERT_TRUE(CreateQueues<int64_t>(0, clear_mask, 0, 0)); + + // When allocation, add |clear_mask| into usage bits on purpose. + size_t slot; + int ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, + kBufferLayerCount, kBufferFormat, + kBufferUsage | clear_mask, &slot); + ASSERT_EQ(0, ret); + + LocalHandle fence; + auto p1_status = producer_queue_->Dequeue(0, &slot, &fence); + ASSERT_TRUE(p1_status.ok()); + auto p1 = p1_status.take(); + ASSERT_EQ(0u, p1->usage() & clear_mask); +} + +TEST_F(BufferHubQueueTest, TestUsageDenySetMask) { + const uint32_t deny_set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; + ASSERT_TRUE(CreateQueues<int64_t>(0, 0, deny_set_mask, 0)); + + // Now that |deny_set_mask| is illegal, allocation without those bits should + // be able to succeed. + size_t slot; + int ret = producer_queue_->AllocateBuffer( + kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, + kBufferUsage & ~deny_set_mask, &slot); + ASSERT_EQ(ret, 0); + + // While allocation with those bits should fail. + ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, + kBufferLayerCount, kBufferFormat, + kBufferUsage | deny_set_mask, &slot); + ASSERT_EQ(ret, -EINVAL); +} + +TEST_F(BufferHubQueueTest, TestUsageDenyClearMask) { + const uint32_t deny_clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; + ASSERT_TRUE(CreateQueues<int64_t>(0, 0, 0, deny_clear_mask)); + + // Now that clearing |deny_clear_mask| is illegal (i.e. setting these bits are + // mandatory), allocation with those bits should be able to succeed. + size_t slot; + int ret = producer_queue_->AllocateBuffer( + kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, + kBufferUsage | deny_clear_mask, &slot); + ASSERT_EQ(ret, 0); + + // While allocation without those bits should fail. + ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, + kBufferLayerCount, kBufferFormat, + kBufferUsage & ~deny_clear_mask, &slot); + ASSERT_EQ(ret, -EINVAL); +} + +} // namespace + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp new file mode 100644 index 0000000000..2b6239f499 --- /dev/null +++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp @@ -0,0 +1,512 @@ +#include <private/dvr/buffer_hub_queue_producer.h> + +#include <base/logging.h> +#include <gui/IProducerListener.h> +#include <gui/Surface.h> + +#include <gtest/gtest.h> + +namespace android { +namespace dvr { + +namespace { + +// Default dimensions before setDefaultBufferSize is called by the consumer. +constexpr uint32_t kDefaultWidth = 1; +constexpr uint32_t kDefaultHeight = 1; + +// Default format before setDefaultBufferFormat is called by the consumer. +constexpr PixelFormat kDefaultFormat = HAL_PIXEL_FORMAT_RGBA_8888; +constexpr int kDefaultConsumerUsageBits = 0; + +// Default transform hint before setTransformHint is called by the consumer. +constexpr uint32_t kDefaultTransformHint = 0; + +constexpr int kTestApi = NATIVE_WINDOW_API_CPU; +constexpr int kTestApiOther = NATIVE_WINDOW_API_EGL; +constexpr int kTestApiInvalid = 0xDEADBEEF; +constexpr int kTestProducerUsageBits = 0; +constexpr bool kTestControlledByApp = true; + +// Builder pattern to slightly vary *almost* correct input +// -- avoids copying and pasting +struct QueueBufferInputBuilder { + IGraphicBufferProducer::QueueBufferInput build() { + return IGraphicBufferProducer::QueueBufferInput( + mTimestamp, mIsAutoTimestamp, mDataSpace, mCrop, mScalingMode, + mTransform, mFence); + } + + QueueBufferInputBuilder& setTimestamp(int64_t timestamp) { + this->mTimestamp = timestamp; + return *this; + } + + QueueBufferInputBuilder& setIsAutoTimestamp(bool isAutoTimestamp) { + this->mIsAutoTimestamp = isAutoTimestamp; + return *this; + } + + QueueBufferInputBuilder& setDataSpace(android_dataspace dataSpace) { + this->mDataSpace = dataSpace; + return *this; + } + + QueueBufferInputBuilder& setCrop(Rect crop) { + this->mCrop = crop; + return *this; + } + + QueueBufferInputBuilder& setScalingMode(int scalingMode) { + this->mScalingMode = scalingMode; + return *this; + } + + QueueBufferInputBuilder& setTransform(uint32_t transform) { + this->mTransform = transform; + return *this; + } + + QueueBufferInputBuilder& setFence(sp<Fence> fence) { + this->mFence = fence; + return *this; + } + + private: + int64_t mTimestamp{1384888611}; + bool mIsAutoTimestamp{false}; + android_dataspace mDataSpace{HAL_DATASPACE_UNKNOWN}; + Rect mCrop{Rect(kDefaultWidth, kDefaultHeight)}; + int mScalingMode{0}; + uint32_t mTransform{0}; + sp<Fence> mFence{Fence::NO_FENCE}; +}; + +// This is a test that covers our implementation of bufferhubqueue-based +// IGraphicBufferProducer. +class BufferHubQueueProducerTest : public ::testing::Test { + protected: + virtual void SetUp() { + const ::testing::TestInfo* const testInfo = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD_IF(TRACE, "Begin test: %s.%s", testInfo->test_case_name(), + testInfo->name()); + + mProducer = BufferHubQueueProducer::Create(); + ASSERT_TRUE(mProducer != nullptr); + mSurface = new Surface(mProducer, true); + ASSERT_TRUE(mSurface != nullptr); + } + + // Connect to a producer in a 'correct' fashion. + void ConnectProducer() { + IGraphicBufferProducer::QueueBufferOutput output; + // Can connect the first time. + ASSERT_EQ(NO_ERROR, mProducer->connect(kDummyListener, kTestApi, + kTestControlledByApp, &output)); + } + + // Dequeue a buffer in a 'correct' fashion. + // Precondition: Producer is connected. + void DequeueBuffer(int* outSlot) { + sp<Fence> fence; + ASSERT_NO_FATAL_FAILURE(DequeueBuffer(outSlot, &fence)); + } + + void DequeueBuffer(int* outSlot, sp<Fence>* outFence) { + ASSERT_NE(nullptr, outSlot); + ASSERT_NE(nullptr, outFence); + + int ret = mProducer->dequeueBuffer(outSlot, outFence, kDefaultWidth, + kDefaultHeight, kDefaultFormat, + kTestProducerUsageBits, nullptr); + // BUFFER_NEEDS_REALLOCATION can be either on or off. + ASSERT_EQ(0, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & ret); + + // Slot number should be in boundary. + ASSERT_LE(0, *outSlot); + ASSERT_GT(BufferQueueDefs::NUM_BUFFER_SLOTS, *outSlot); + } + + // Create a generic "valid" input for queueBuffer + // -- uses the default buffer format, width, etc. + static IGraphicBufferProducer::QueueBufferInput CreateBufferInput() { + return QueueBufferInputBuilder().build(); + } + + const sp<IProducerListener> kDummyListener{new DummyProducerListener}; + + sp<BufferHubQueueProducer> mProducer; + sp<Surface> mSurface; +}; + +TEST_F(BufferHubQueueProducerTest, ConnectFirst_ReturnsError) { + IGraphicBufferProducer::QueueBufferOutput output; + + // NULL output returns BAD_VALUE + EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi, + kTestControlledByApp, nullptr)); + + // Invalid API returns bad value + EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApiInvalid, + kTestControlledByApp, &output)); +} + +TEST_F(BufferHubQueueProducerTest, ConnectAgain_ReturnsError) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + // Can't connect when there is already a producer connected. + IGraphicBufferProducer::QueueBufferOutput output; + EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi, + kTestControlledByApp, &output)); +} + +TEST_F(BufferHubQueueProducerTest, Disconnect_Succeeds) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); +} + +TEST_F(BufferHubQueueProducerTest, Disconnect_ReturnsError) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + // Must disconnect with same API number + EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiOther)); + // API must not be out of range + EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiInvalid)); +} + +TEST_F(BufferHubQueueProducerTest, Query_Succeeds) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + int32_t value = -1; + EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value)); + EXPECT_EQ(kDefaultWidth, static_cast<uint32_t>(value)); + + EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_HEIGHT, &value)); + EXPECT_EQ(kDefaultHeight, static_cast<uint32_t>(value)); + + EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_FORMAT, &value)); + EXPECT_EQ(kDefaultFormat, value); + + EXPECT_EQ(NO_ERROR, + mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value)); + EXPECT_LE(0, value); + EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, static_cast<size_t>(value)); + + EXPECT_EQ(NO_ERROR, + mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value)); + EXPECT_FALSE(value); // Can't run behind when we haven't touched the queue + + EXPECT_EQ(NO_ERROR, + mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value)); + EXPECT_EQ(kDefaultConsumerUsageBits, value); +} + +TEST_F(BufferHubQueueProducerTest, Query_ReturnsError) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + // One past the end of the last 'query' enum value. Update this if we add more + // enums. + const int NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE = NATIVE_WINDOW_BUFFER_AGE + 1; + + int value; + // What was out of range + EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ -1, &value)); + EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ 0xDEADBEEF, &value)); + EXPECT_EQ(BAD_VALUE, + mProducer->query(NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE, &value)); + + // Some enums from window.h are 'invalid' + EXPECT_EQ(BAD_VALUE, + mProducer->query(NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &value)); + EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_CONCRETE_TYPE, &value)); + EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_WIDTH, &value)); + EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_HEIGHT, &value)); + EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value)); + + // Value was NULL + EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/ NULL)); +} + +TEST_F(BufferHubQueueProducerTest, Queue_Succeeds) { + int slot = -1; + + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); + + // Request the buffer (pre-requisite for queueing) + sp<GraphicBuffer> buffer; + ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); + + // A generic "valid" input + IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); + IGraphicBufferProducer::QueueBufferOutput output; + + // Queue the buffer back into the BQ + ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output)); + + EXPECT_EQ(kDefaultWidth, output.width); + EXPECT_EQ(kDefaultHeight, output.height); + EXPECT_EQ(kDefaultTransformHint, output.transformHint); + + // BufferHubQueue delivers buffers to consumer immediately. + EXPECT_EQ(0u, output.numPendingBuffers); + + // Note that BufferHubQueue doesn't support nextFrameNumber as it seems to + // be a SurfaceFlinger specific optimization. + EXPECT_EQ(0u, output.nextFrameNumber); + + // Buffer was not in the dequeued state + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); +} + +// Test invalid slot number +TEST_F(BufferHubQueueProducerTest, QueueInvalidSlot_ReturnsError) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + // A generic "valid" input + IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); + IGraphicBufferProducer::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(BufferQueueDefs::NUM_BUFFER_SLOTS, + input, &output)); +} + +// Slot was not in the dequeued state (all slots start out in Free state) +TEST_F(BufferHubQueueProducerTest, QueueNotDequeued_ReturnsError) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); + IGraphicBufferProducer::QueueBufferOutput output; + + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ 0, input, &output)); +} + +// Slot was enqueued without requesting a buffer +TEST_F(BufferHubQueueProducerTest, QueueNotRequested_ReturnsError) { + int slot = -1; + + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); + + IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); + IGraphicBufferProducer::QueueBufferOutput output; + + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); +} + +// Test when fence was NULL +TEST_F(BufferHubQueueProducerTest, QueueNoFence_ReturnsError) { + int slot = -1; + + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); + + sp<GraphicBuffer> buffer; + ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); + + sp<Fence> nullFence = NULL; + + IGraphicBufferProducer::QueueBufferInput input = + QueueBufferInputBuilder().setFence(nullFence).build(); + IGraphicBufferProducer::QueueBufferOutput output; + + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); +} + +// Test scaling mode was invalid +TEST_F(BufferHubQueueProducerTest, QueueTestInvalidScalingMode_ReturnsError) { + int slot = -1; + + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); + + sp<GraphicBuffer> buffer; + ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); + + IGraphicBufferProducer::QueueBufferInput input = + QueueBufferInputBuilder().setScalingMode(-1).build(); + IGraphicBufferProducer::QueueBufferOutput output; + + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); + + input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build(); + + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); +} + +// Test crop rect is out of bounds of the buffer dimensions +TEST_F(BufferHubQueueProducerTest, QueueCropOutOfBounds_ReturnsError) { + int slot = -1; + + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); + + sp<GraphicBuffer> buffer; + ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); + + IGraphicBufferProducer::QueueBufferInput input = + QueueBufferInputBuilder() + .setCrop(Rect(kDefaultWidth + 1, kDefaultHeight + 1)) + .build(); + IGraphicBufferProducer::QueueBufferOutput output; + + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); +} + +TEST_F(BufferHubQueueProducerTest, CancelBuffer_Succeeds) { + int slot = -1; + sp<Fence> fence; + + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence)); + + // Should be able to cancel buffer after a dequeue. + EXPECT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence)); +} + +TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) { + return; + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + int minUndequeuedBuffers; + ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &minUndequeuedBuffers)); + + const int minBuffers = 1; + const int maxBuffers = + BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers; + + ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false)) + << "async mode: " << false; + ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(minBuffers)) + << "bufferCount: " << minBuffers; + + // Should now be able to dequeue up to minBuffers times + // Should now be able to dequeue up to maxBuffers times + int slot = -1; + for (int i = 0; i < minBuffers; ++i) { + ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); + } + + ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers)); + + // queue the first buffer to enable max dequeued buffer count checking + IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); + IGraphicBufferProducer::QueueBufferOutput output; + sp<GraphicBuffer> buffer; + ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); + ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output)); + + sp<Fence> fence; + for (int i = 0; i < maxBuffers; ++i) { + ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence)); + } + + // Cancel a buffer, so we can decrease the buffer count + ASSERT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence)); + + // Should now be able to decrease the max dequeued count by 1 + ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1)); +} + +TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + int minUndequeuedBuffers; + ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &minUndequeuedBuffers)); + + const int minBuffers = 1; + const int maxBuffers = + BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers; + + ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false)) + << "async mode: " << false; + // Buffer count was out of range + EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(0)) + << "bufferCount: " << 0; + EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(maxBuffers + 1)) + << "bufferCount: " << maxBuffers + 1; + + // Set max dequeue count to 2 + ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(2)); + // Dequeue 2 buffers + int slot = -1; + sp<Fence> fence; + for (int i = 0; i < 2; i++) { + ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + (mProducer->dequeueBuffer( + &slot, &fence, kDefaultWidth, kDefaultHeight, + kDefaultFormat, kTestProducerUsageBits, nullptr))) + << "slot: " << slot; + } + + // Client has too many buffers dequeued + EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(1)) + << "bufferCount: " << minBuffers; +} + +TEST_F(BufferHubQueueProducerTest, + DisconnectedProducerReturnsError_dequeueBuffer) { + int slot = -1; + sp<Fence> fence; + + ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth, + kDefaultHeight, kDefaultFormat, + kTestProducerUsageBits, nullptr)); +} + +TEST_F(BufferHubQueueProducerTest, + DisconnectedProducerReturnsError_requestBuffer) { + int slot = -1; + sp<GraphicBuffer> buffer; + + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); + + // Shouldn't be able to request buffer after disconnect. + ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); + ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer)); +} + +TEST_F(BufferHubQueueProducerTest, + DisconnectedProducerReturnsError_queueBuffer) { + int slot = -1; + sp<GraphicBuffer> buffer; + + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); + ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); + + // A generic "valid" input + IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); + IGraphicBufferProducer::QueueBufferOutput output; + + // Shouldn't be able to queue buffer after disconnect. + ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); + ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output)); +} + +TEST_F(BufferHubQueueProducerTest, + DisconnectedProducerReturnsError_cancelBuffer) { + int slot = -1; + sp<GraphicBuffer> buffer; + + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); + ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); + + // Shouldn't be able to cancel buffer after disconnect. + ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); + ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE)); +} + +} // namespace + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp new file mode 100644 index 0000000000..d90521a810 --- /dev/null +++ b/libs/vr/libdisplay/Android.bp @@ -0,0 +1,66 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +sourceFiles = [ + "display_client.cpp", + "display_manager_client.cpp", + "display_protocol.cpp", + "vsync_client.cpp", +] + +localIncludeFiles = [ + "include", +] + +sharedLibraries = [ + "libbase", + "libcutils", + "liblog", + "libutils", + "libui", + "libgui", + "libhardware", + "libsync", + "libnativewindow", +] + +staticLibraries = [ + "libdvrcommon", + "libbufferhubqueue", + "libbufferhub", + "libvrsensor", + "libpdx_default_transport", +] + +headerLibraries = [ + "vulkan_headers", +] + +cc_library { + tags: ["tests"], + srcs: sourceFiles, + cflags: ["-DLOG_TAG=\"libdisplay\"", + "-DTRACE=0", + "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", + "-DGL_GLEXT_PROTOTYPES", + "-DEGL_EGLEXT_PROTOTYPES", + ], // + [ "-UNDEBUG", "-DDEBUG", "-O0", "-g" ], + export_include_dirs: localIncludeFiles, + shared_libs: sharedLibraries, + static_libs: staticLibraries, + header_libs: headerLibraries, + export_header_lib_headers: headerLibraries, + + name: "libdisplay", +} diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp new file mode 100644 index 0000000000..935ca2ef81 --- /dev/null +++ b/libs/vr/libdisplay/display_client.cpp @@ -0,0 +1,210 @@ +#include "include/private/dvr/display_client.h" + +#include <cutils/native_handle.h> +#include <log/log.h> +#include <pdx/default_transport/client_channel.h> +#include <pdx/default_transport/client_channel_factory.h> +#include <pdx/status.h> + +#include <mutex> + +#include <private/dvr/display_protocol.h> +#include <private/dvr/native_buffer.h> + +using android::pdx::ErrorStatus; +using android::pdx::LocalHandle; +using android::pdx::LocalChannelHandle; +using android::pdx::Status; +using android::pdx::Transaction; +using android::pdx::rpc::IfAnyOf; + +namespace android { +namespace dvr { +namespace display { + +Surface::Surface(LocalChannelHandle channel_handle, int* error) + : BASE{pdx::default_transport::ClientChannel::Create( + std::move(channel_handle))} { + auto status = InvokeRemoteMethod<DisplayProtocol::GetSurfaceInfo>(); + if (!status) { + ALOGE("Surface::Surface: Failed to get surface info: %s", + status.GetErrorMessage().c_str()); + Close(status.error()); + if (error) + *error = status.error(); + } + + surface_id_ = status.get().surface_id; + z_order_ = status.get().z_order; + visible_ = status.get().visible; +} + +Surface::Surface(const SurfaceAttributes& attributes, int* error) + : BASE{pdx::default_transport::ClientChannelFactory::Create( + DisplayProtocol::kClientPath), + kInfiniteTimeout} { + auto status = InvokeRemoteMethod<DisplayProtocol::CreateSurface>(attributes); + if (!status) { + ALOGE("Surface::Surface: Failed to create display surface: %s", + status.GetErrorMessage().c_str()); + Close(status.error()); + if (error) + *error = status.error(); + } + + surface_id_ = status.get().surface_id; + z_order_ = status.get().z_order; + visible_ = status.get().visible; +} + +Status<void> Surface::SetVisible(bool visible) { + return SetAttributes( + {{SurfaceAttribute::Visible, SurfaceAttributeValue{visible}}}); +} + +Status<void> Surface::SetZOrder(int z_order) { + return SetAttributes( + {{SurfaceAttribute::ZOrder, SurfaceAttributeValue{z_order}}}); +} + +Status<void> Surface::SetAttributes(const SurfaceAttributes& attributes) { + auto status = InvokeRemoteMethod<DisplayProtocol::SetAttributes>(attributes); + if (!status) { + ALOGE( + "Surface::SetAttributes: Failed to set display surface " + "attributes: %s", + status.GetErrorMessage().c_str()); + return status.error_status(); + } + + // Set the local cached copies of the attributes we care about from the full + // set of attributes sent to the display service. + for (const auto& attribute : attributes) { + const auto& key = attribute.first; + const auto* variant = &attribute.second; + bool invalid_value = false; + switch (key) { + case SurfaceAttribute::Visible: + invalid_value = + !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &visible_); + break; + case SurfaceAttribute::ZOrder: + invalid_value = !IfAnyOf<int32_t>::Get(variant, &z_order_); + break; + } + + if (invalid_value) { + ALOGW( + "Surface::SetAttributes: Failed to set display surface " + "attribute %d because of incompatible type: %d", + key, variant->index()); + } + } + + return {}; +} + +Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue() { + ALOGD_IF(TRACE, "Surface::CreateQueue: Creating empty queue."); + auto status = InvokeRemoteMethod<DisplayProtocol::CreateQueue>(0); + if (!status) { + ALOGE("Surface::CreateQueue: Failed to create queue: %s", + status.GetErrorMessage().c_str()); + return status.error_status(); + } + + auto producer_queue = ProducerQueue::Import(status.take()); + if (!producer_queue) { + ALOGE("Surface::CreateQueue: Failed to import producer queue!"); + return ErrorStatus(ENOMEM); + } + + return {std::move(producer_queue)}; +} + +Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue( + uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, + uint64_t usage, size_t capacity) { + ALOGD_IF(TRACE, + "Surface::CreateQueue: width=%u height=%u layer_count=%u format=%u " + "usage=%" PRIx64 " capacity=%zu", + width, height, layer_count, format, usage, capacity); + auto status = CreateQueue(); + if (!status) + return status.error_status(); + + auto producer_queue = status.take(); + + ALOGD_IF(TRACE, "Surface::CreateQueue: Allocating %zu buffers...", capacity); + for (size_t i = 0; i < capacity; i++) { + size_t slot; + const int ret = producer_queue->AllocateBuffer(width, height, layer_count, + format, usage, &slot); + if (ret < 0) { + ALOGE( + "Surface::CreateQueue: Failed to allocate buffer on queue_id=%d: %s", + producer_queue->id(), strerror(-ret)); + return ErrorStatus(ENOMEM); + } + ALOGD_IF( + TRACE, + "Surface::CreateQueue: Allocated buffer at slot=%zu of capacity=%zu", + slot, capacity); + } + + return {std::move(producer_queue)}; +} + +DisplayClient::DisplayClient(int* error) + : BASE(pdx::default_transport::ClientChannelFactory::Create( + DisplayProtocol::kClientPath), + kInfiniteTimeout) { + if (error) + *error = Client::error(); +} + +Status<Metrics> DisplayClient::GetDisplayMetrics() { + return InvokeRemoteMethod<DisplayProtocol::GetMetrics>(); +} + +Status<std::unique_ptr<Surface>> DisplayClient::CreateSurface( + const SurfaceAttributes& attributes) { + int error; + if (auto client = Surface::Create(attributes, &error)) + return {std::move(client)}; + else + return ErrorStatus(error); +} + +Status<std::unique_ptr<IonBuffer>> DisplayClient::GetNamedBuffer( + const std::string& name) { + auto status = InvokeRemoteMethod<DisplayProtocol::GetNamedBuffer>(name); + if (!status) { + ALOGE( + "DisplayClient::GetNamedBuffer: Failed to get named buffer: name=%s; " + "error=%s", + name.c_str(), status.GetErrorMessage().c_str()); + return status.error_status(); + } + + auto ion_buffer = std::make_unique<IonBuffer>(); + auto native_buffer_handle = status.take(); + const int ret = native_buffer_handle.Import(ion_buffer.get()); + if (ret < 0) { + ALOGE( + "DisplayClient::GetNamedBuffer: Failed to import named buffer: " + "name=%s; error=%s", + name.c_str(), strerror(-ret)); + return ErrorStatus(-ret); + } + + return {std::move(ion_buffer)}; +} + +Status<bool> DisplayClient::IsVrAppRunning() { + return InvokeRemoteMethod<DisplayProtocol::IsVrAppRunning>(); +} + +} // namespace display +} // namespace dvr +} // namespace android diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp new file mode 100644 index 0000000000..82dacf707d --- /dev/null +++ b/libs/vr/libdisplay/display_manager_client.cpp @@ -0,0 +1,78 @@ +#include "include/private/dvr/display_manager_client.h" + +#include <pdx/default_transport/client_channel_factory.h> +#include <private/dvr/buffer_hub_client.h> +#include <private/dvr/buffer_hub_queue_client.h> +#include <private/dvr/display_protocol.h> +#include <utils/Log.h> + +using android::pdx::ErrorStatus; +using android::pdx::LocalChannelHandle; +using android::pdx::Transaction; + +namespace android { +namespace dvr { +namespace display { + +DisplayManagerClient::DisplayManagerClient() + : BASE(pdx::default_transport::ClientChannelFactory::Create( + DisplayManagerProtocol::kClientPath)) {} + +DisplayManagerClient::~DisplayManagerClient() {} + +pdx::Status<std::vector<display::SurfaceState>> +DisplayManagerClient::GetSurfaceState() { + auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceState>(); + if (!status) { + ALOGE( + "DisplayManagerClient::GetSurfaceState: Failed to get surface info: %s", + status.GetErrorMessage().c_str()); + } + + return status; +} + +pdx::Status<std::unique_ptr<IonBuffer>> DisplayManagerClient::SetupNamedBuffer( + const std::string& name, size_t size, uint64_t usage) { + auto status = InvokeRemoteMethod<DisplayManagerProtocol::SetupNamedBuffer>( + name, size, usage); + if (!status) { + ALOGE( + "DisplayManagerClient::SetupPoseBuffer: Failed to create the named " + "buffer %s", + status.GetErrorMessage().c_str()); + return status.error_status(); + } + + auto ion_buffer = std::make_unique<IonBuffer>(); + auto native_buffer_handle = status.take(); + const int ret = native_buffer_handle.Import(ion_buffer.get()); + if (ret < 0) { + ALOGE( + "DisplayClient::GetNamedBuffer: Failed to import named buffer: " + "name=%s; error=%s", + name.c_str(), strerror(-ret)); + return ErrorStatus(-ret); + } + + return {std::move(ion_buffer)}; +} + +pdx::Status<std::unique_ptr<ConsumerQueue>> +DisplayManagerClient::GetSurfaceQueue(int surface_id, int queue_id) { + auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceQueue>( + surface_id, queue_id); + if (!status) { + ALOGE( + "DisplayManagerClient::GetSurfaceQueue: Failed to get queue for " + "surface_id=%d queue_id=%d: %s", + surface_id, queue_id, status.GetErrorMessage().c_str()); + return status.error_status(); + } + + return {ConsumerQueue::Import(status.take())}; +} + +} // namespace display +} // namespace dvr +} // namespace android diff --git a/libs/vr/libdisplay/display_protocol.cpp b/libs/vr/libdisplay/display_protocol.cpp new file mode 100644 index 0000000000..773f9a5aa3 --- /dev/null +++ b/libs/vr/libdisplay/display_protocol.cpp @@ -0,0 +1,13 @@ +#include "include/private/dvr/display_protocol.h" + +namespace android { +namespace dvr { +namespace display { + +constexpr char DisplayProtocol::kClientPath[]; +constexpr char DisplayManagerProtocol::kClientPath[]; +constexpr char VSyncProtocol::kClientPath[]; + +} // namespace display +} // namespace dvr +} // namespace android diff --git a/libs/vr/libdisplay/include/CPPLINT.cfg b/libs/vr/libdisplay/include/CPPLINT.cfg new file mode 100644 index 0000000000..2f8a3c018c --- /dev/null +++ b/libs/vr/libdisplay/include/CPPLINT.cfg @@ -0,0 +1 @@ +filter=-build/header_guard diff --git a/libs/vr/libdisplay/include/dvr/dvr_display_types.h b/libs/vr/libdisplay/include/dvr/dvr_display_types.h new file mode 100644 index 0000000000..25364d8590 --- /dev/null +++ b/libs/vr/libdisplay/include/dvr/dvr_display_types.h @@ -0,0 +1,65 @@ +#ifndef ANDROID_DVR_DISPLAY_TYPES_H_ +#define ANDROID_DVR_DISPLAY_TYPES_H_ + +#include <sys/cdefs.h> + +__BEGIN_DECLS + +// Define types used in pose buffer fields. These types have atomicity +// guarantees that are useful in lock-free shared memory ring buffers. +#ifdef __ARM_NEON +#include <arm_neon.h> +#else +#ifndef __FLOAT32X4T_86 +#define __FLOAT32X4T_86 +typedef float float32x4_t __attribute__((__vector_size__(16))); +typedef struct float32x4x4_t { float32x4_t val[4]; }; +#endif +#endif + +// VrFlinger display manager surface state snapshots per surface flags +// indicating what changed since the last snapshot. +enum { + // No changes. + DVR_SURFACE_UPDATE_FLAGS_NONE = 0, + // This surface is new. + DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE = (1 << 0), + // Buffer queues added/removed. + DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED = (1 << 1), + // Visibility/z-order changed. + DVR_SURFACE_UPDATE_FLAGS_VISIBILITY_CHANGED = (1 << 2), + // Generic attributes changed. + DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED = (1 << 3), +}; + +// Surface attribute keys. VrFlinger defines keys in the negative integer space. +// The compositor is free to use keys in the positive integer space for +// implementation-defined purposes. +enum { + // DIRECT: bool + // Determines whether a direct surface is created (compositor output) or an + // application surface. Defaults to false (application surface). May only be + // set to true by a process with either UID=root or UID validated with + // IsTrustedUid() (VrCore). + DVR_SURFACE_ATTRIBUTE_DIRECT = -3, + // Z_ORDER: int32_t + // Interpreted by VrFlinger only on direct surfaces to order the corresponding + // hardware layers. More positive values render on top of more negative + // values. + DVR_SURFACE_ATTRIBUTE_Z_ORDER = -2, + // VISIBLE: bool + // Interpreted by VrFlinger only on direct surfaces to determine whether a + // surface is assigned to a hardware layer or ignored. + DVR_SURFACE_ATTRIBUTE_VISIBLE = -1, + // INVALID + // Invalid key. No attributes should have this key. + DVR_SURFACE_ATTRIBUTE_INVALID = 0, + // FIRST_USER_KEY + // VrFlinger ingores any keys with this value or greater, passing them to the + // compositor through surface state query results. + DVR_SURFACE_ATTRIBUTE_FIRST_USER_KEY = 1, +}; + +__END_DECLS + +#endif // ANDROID_DVR_DISPLAY_TYPES_H_ diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h new file mode 100644 index 0000000000..7a7f670ffa --- /dev/null +++ b/libs/vr/libdisplay/include/private/dvr/display_client.h @@ -0,0 +1,91 @@ +#ifndef ANDROID_DVR_DISPLAY_CLIENT_H_ +#define ANDROID_DVR_DISPLAY_CLIENT_H_ + +#include <hardware/hwcomposer.h> +#include <pdx/client.h> +#include <pdx/file_handle.h> +#include <private/dvr/buffer_hub_client.h> +#include <private/dvr/buffer_hub_queue_client.h> +#include <private/dvr/display_protocol.h> + +namespace android { +namespace dvr { +namespace display { + +class Surface : public pdx::ClientBase<Surface> { + public: + // Utility named constructor. This can be removed once ClientBase::Create is + // refactored to return Status<T> types. + static pdx::Status<std::unique_ptr<Surface>> CreateSurface( + const SurfaceAttributes& attributes) { + int error; + pdx::Status<std::unique_ptr<Surface>> status; + if (auto surface = Create(attributes, &error)) + status.SetValue(std::move(surface)); + else + status.SetError(error); + return status; + } + + int surface_id() const { return surface_id_; } + int z_order() const { return z_order_; } + bool visible() const { return visible_; } + + pdx::Status<void> SetVisible(bool visible); + pdx::Status<void> SetZOrder(int z_order); + pdx::Status<void> SetAttributes(const SurfaceAttributes& attributes); + + // Creates an empty queue. + pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(); + + // Creates a queue and populates it with |capacity| buffers of the specified + // parameters. + pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width, + uint32_t height, + uint32_t layer_count, + uint32_t format, + uint64_t usage, + size_t capacity); + + private: + friend BASE; + + int surface_id_ = -1; + int z_order_ = 0; + bool visible_ = false; + + // TODO(eieio,avakulenko): Remove error param once pdx::ClientBase::Create() + // returns Status<T>. + explicit Surface(const SurfaceAttributes& attributes, int* error = nullptr); + explicit Surface(pdx::LocalChannelHandle channel_handle, + int* error = nullptr); + + Surface(const Surface&) = delete; + void operator=(const Surface&) = delete; +}; + +class DisplayClient : public pdx::ClientBase<DisplayClient> { + public: + pdx::Status<Metrics> GetDisplayMetrics(); + pdx::Status<std::unique_ptr<IonBuffer>> GetNamedBuffer( + const std::string& name); + pdx::Status<std::unique_ptr<Surface>> CreateSurface( + const SurfaceAttributes& attributes); + + // Temporary query for current VR status. Will be removed later. + pdx::Status<bool> IsVrAppRunning(); + + private: + friend BASE; + + explicit DisplayClient(int* error = nullptr); + + DisplayClient(const DisplayClient&) = delete; + void operator=(const DisplayClient&) = delete; +}; + +} // namespace display +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_DISPLAY_CLIENT_H_ diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h new file mode 100644 index 0000000000..fea841595a --- /dev/null +++ b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h @@ -0,0 +1,51 @@ +#ifndef ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_ +#define ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_ + +#include <string> +#include <vector> + +#include <pdx/client.h> +#include <pdx/status.h> +#include <private/dvr/display_protocol.h> + +namespace android { +namespace dvr { + +class IonBuffer; +class ConsumerQueue; + +namespace display { + +class DisplayManagerClient : public pdx::ClientBase<DisplayManagerClient> { + public: + ~DisplayManagerClient() override; + + pdx::Status<std::vector<SurfaceState>> GetSurfaceState(); + pdx::Status<std::unique_ptr<IonBuffer>> SetupNamedBuffer( + const std::string& name, size_t size, uint64_t usage); + pdx::Status<std::unique_ptr<ConsumerQueue>> GetSurfaceQueue(int surface_id, + int queue_id); + + using Client::event_fd; + + pdx::Status<int> GetEventMask(int events) { + if (auto* client_channel = GetChannel()) + return client_channel->GetEventMask(events); + else + return pdx::ErrorStatus(EINVAL); + } + + private: + friend BASE; + + DisplayManagerClient(); + + DisplayManagerClient(const DisplayManagerClient&) = delete; + void operator=(const DisplayManagerClient&) = delete; +}; + +} // namespace display +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_ diff --git a/libs/vr/libdisplay/include/private/dvr/display_protocol.h b/libs/vr/libdisplay/include/private/dvr/display_protocol.h new file mode 100644 index 0000000000..f34d61fe49 --- /dev/null +++ b/libs/vr/libdisplay/include/private/dvr/display_protocol.h @@ -0,0 +1,283 @@ +#ifndef ANDROID_DVR_DISPLAY_PROTOCOL_H_ +#define ANDROID_DVR_DISPLAY_PROTOCOL_H_ + +#include <sys/types.h> + +#include <array> +#include <map> + +#include <dvr/dvr_display_types.h> + +#include <pdx/rpc/remote_method.h> +#include <pdx/rpc/serializable.h> +#include <pdx/rpc/variant.h> +#include <private/dvr/bufferhub_rpc.h> + +// RPC protocol definitions for DVR display services (VrFlinger). + +namespace android { +namespace dvr { +namespace display { + +// Native display metrics. +struct Metrics { + // Basic display properties. + uint32_t display_width; + uint32_t display_height; + uint32_t display_x_dpi; + uint32_t display_y_dpi; + uint32_t vsync_period_ns; + + // HMD metrics. + // TODO(eieio): Determine how these fields should be populated. On phones + // these values are determined at runtime by VrCore based on which headset the + // phone is in. On dedicated hardware this needs to come from somewhere else. + // Perhaps these should be moved to a separate structure that is returned by a + // separate runtime call. + uint32_t distorted_width; + uint32_t distorted_height; + uint32_t hmd_ipd_mm; + float inter_lens_distance_m; + std::array<float, 4> left_fov_lrbt; + std::array<float, 4> right_fov_lrbt; + + private: + PDX_SERIALIZABLE_MEMBERS(Metrics, display_width, display_height, + display_x_dpi, display_y_dpi, vsync_period_ns, + distorted_width, distorted_height, hmd_ipd_mm, + inter_lens_distance_m, left_fov_lrbt, + right_fov_lrbt); +}; + +// Serializable base type for enum structs. Enum structs are easier to use than +// enum classes, especially for bitmasks. This base type provides common +// utilities for flags types. +template <typename Integer> +class Flags { + public: + using Base = Flags<Integer>; + using Type = Integer; + + Flags(const Integer& value) : value_{value} {} + Flags(const Flags&) = default; + Flags& operator=(const Flags&) = default; + + Integer value() const { return value_; } + operator Integer() const { return value_; } + + bool IsSet(Integer bits) const { return (value_ & bits) == bits; } + bool IsClear(Integer bits) const { return (value_ & bits) == 0; } + + void Set(Integer bits) { value_ |= bits; } + void Clear(Integer bits) { value_ &= ~bits; } + + Integer operator|(Integer bits) const { return value_ | bits; } + Integer operator&(Integer bits) const { return value_ & bits; } + + Flags& operator|=(Integer bits) { + value_ |= bits; + return *this; + } + Flags& operator&=(Integer bits) { + value_ &= bits; + return *this; + } + + private: + Integer value_; + + PDX_SERIALIZABLE_MEMBERS(Flags<Integer>, value_); +}; + +// Flags indicating what changed since last update. +struct SurfaceUpdateFlags : public Flags<uint32_t> { + enum : Type { + None = DVR_SURFACE_UPDATE_FLAGS_NONE, + NewSurface = DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE, + BuffersChanged = DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED, + VisibilityChanged = DVR_SURFACE_UPDATE_FLAGS_VISIBILITY_CHANGED, + AttributesChanged = DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED, + }; + + SurfaceUpdateFlags() : Base{None} {} + using Base::Base; +}; + +// Surface attribute key/value types. +using SurfaceAttributeKey = int32_t; +using SurfaceAttributeValue = + pdx::rpc::Variant<int32_t, int64_t, bool, float, std::array<float, 2>, + std::array<float, 3>, std::array<float, 4>, + std::array<float, 8>, std::array<float, 16>>; + +// Defined surface attribute keys. +struct SurfaceAttribute : public Flags<SurfaceAttributeKey> { + enum : Type { + // Keys in the negative integer space are interpreted by VrFlinger for + // direct surfaces. + Direct = DVR_SURFACE_ATTRIBUTE_DIRECT, + ZOrder = DVR_SURFACE_ATTRIBUTE_Z_ORDER, + Visible = DVR_SURFACE_ATTRIBUTE_VISIBLE, + + // Invalid key. May be used to terminate C style lists in public API code. + Invalid = DVR_SURFACE_ATTRIBUTE_INVALID, + + // Positive keys are interpreted by the compositor only. + FirstUserKey = DVR_SURFACE_ATTRIBUTE_FIRST_USER_KEY, + }; + + SurfaceAttribute() : Base{Invalid} {} + using Base::Base; +}; + +// Collection of surface attribute key/value pairs. +using SurfaceAttributes = std::map<SurfaceAttributeKey, SurfaceAttributeValue>; + +struct SurfaceState { + int32_t surface_id; + int32_t process_id; + int32_t user_id; + + SurfaceAttributes surface_attributes; + SurfaceUpdateFlags update_flags; + std::vector<int32_t> queue_ids; + + // Convenience accessors. + bool GetVisible() const { + bool bool_value = false; + GetAttribute(SurfaceAttribute::Visible, &bool_value, + ValidTypes<int32_t, int64_t, bool, float>{}); + return bool_value; + } + + int GetZOrder() const { + int int_value = 0; + GetAttribute(SurfaceAttribute::ZOrder, &int_value, + ValidTypes<int32_t, int64_t, float>{}); + return int_value; + } + + private: + template <typename... Types> + struct ValidTypes {}; + + template <typename ReturnType, typename... Types> + bool GetAttribute(SurfaceAttributeKey key, ReturnType* out_value, + ValidTypes<Types...>) const { + auto search = surface_attributes.find(key); + if (search != surface_attributes.end()) + return pdx::rpc::IfAnyOf<Types...>::Get(&search->second, out_value); + else + return false; + } + + PDX_SERIALIZABLE_MEMBERS(SurfaceState, surface_id, process_id, + surface_attributes, update_flags, queue_ids); +}; + +struct SurfaceInfo { + int surface_id; + bool visible; + int z_order; + + private: + PDX_SERIALIZABLE_MEMBERS(SurfaceInfo, surface_id, visible, z_order); +}; + +struct DisplayProtocol { + // Service path. + static constexpr char kClientPath[] = "system/vr/display/client"; + + // Op codes. + enum { + kOpGetMetrics = 0, + kOpGetNamedBuffer, + kOpIsVrAppRunning, + kOpCreateSurface, + kOpGetSurfaceInfo, + kOpCreateQueue, + kOpSetAttributes, + }; + + // Aliases. + using LocalChannelHandle = pdx::LocalChannelHandle; + using Void = pdx::rpc::Void; + + // Methods. + PDX_REMOTE_METHOD(GetMetrics, kOpGetMetrics, Metrics(Void)); + PDX_REMOTE_METHOD(GetNamedBuffer, kOpGetNamedBuffer, + LocalNativeBufferHandle(std::string name)); + PDX_REMOTE_METHOD(IsVrAppRunning, kOpIsVrAppRunning, bool(Void)); + PDX_REMOTE_METHOD(CreateSurface, kOpCreateSurface, + SurfaceInfo(const SurfaceAttributes& attributes)); + PDX_REMOTE_METHOD(GetSurfaceInfo, kOpGetSurfaceInfo, SurfaceInfo(Void)); + PDX_REMOTE_METHOD(CreateQueue, kOpCreateQueue, + LocalChannelHandle(size_t meta_size_bytes)); + PDX_REMOTE_METHOD(SetAttributes, kOpSetAttributes, + void(const SurfaceAttributes& attributes)); +}; + +struct DisplayManagerProtocol { + // Service path. + static constexpr char kClientPath[] = "system/vr/display/manager"; + + // Op codes. + enum { + kOpGetSurfaceState = 0, + kOpGetSurfaceQueue, + kOpSetupNamedBuffer, + }; + + // Aliases. + using LocalChannelHandle = pdx::LocalChannelHandle; + using Void = pdx::rpc::Void; + + // Methods. + PDX_REMOTE_METHOD(GetSurfaceState, kOpGetSurfaceState, + std::vector<SurfaceState>(Void)); + PDX_REMOTE_METHOD(GetSurfaceQueue, kOpGetSurfaceQueue, + LocalChannelHandle(int surface_id, int queue_id)); + PDX_REMOTE_METHOD(SetupNamedBuffer, kOpSetupNamedBuffer, + LocalNativeBufferHandle(const std::string& name, + size_t size, uint64_t usage)); +}; + +struct VSyncSchedInfo { + int64_t vsync_period_ns; + int64_t timestamp_ns; + uint32_t next_vsync_count; + + private: + PDX_SERIALIZABLE_MEMBERS(VSyncSchedInfo, vsync_period_ns, timestamp_ns, + next_vsync_count); +}; + +struct VSyncProtocol { + // Service path. + static constexpr char kClientPath[] = "system/vr/display/vsync"; + + // Op codes. + enum { + kOpWait = 0, + kOpAck, + kOpGetLastTimestamp, + kOpGetSchedInfo, + kOpAcknowledge, + }; + + // Aliases. + using Void = pdx::rpc::Void; + using Timestamp = int64_t; + + // Methods. + PDX_REMOTE_METHOD(Wait, kOpWait, Timestamp(Void)); + PDX_REMOTE_METHOD(GetLastTimestamp, kOpGetLastTimestamp, Timestamp(Void)); + PDX_REMOTE_METHOD(GetSchedInfo, kOpGetSchedInfo, VSyncSchedInfo(Void)); + PDX_REMOTE_METHOD(Acknowledge, kOpAcknowledge, void(Void)); +}; + +} // namespace display +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_DISPLAY_PROTOCOL_H_ diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_client.h b/libs/vr/libdisplay/include/private/dvr/vsync_client.h new file mode 100644 index 0000000000..1eeb80e09d --- /dev/null +++ b/libs/vr/libdisplay/include/private/dvr/vsync_client.h @@ -0,0 +1,69 @@ +#ifndef ANDROID_DVR_VSYNC_CLIENT_H_ +#define ANDROID_DVR_VSYNC_CLIENT_H_ + +#include <stdint.h> + +#include <pdx/client.h> + +struct dvr_vsync_client {}; + +namespace android { +namespace dvr { + +/* + * VSyncClient is a remote interface to the vsync service in displayd. + * This class is used to wait for and retrieve information about the + * display vsync. + */ +class VSyncClient : public pdx::ClientBase<VSyncClient>, + public dvr_vsync_client { + public: + /* + * Wait for the next vsync signal. + * The timestamp (in ns) is written into *ts when ts is non-NULL. + */ + int Wait(int64_t* timestamp_ns); + + /* + * Returns the file descriptor used to communicate with the vsync system + * service or -1 on error. + */ + int GetFd(); + + /* + * Clears the select/poll/epoll event so that subsequent calls to + * these will not signal until the next vsync. + */ + int Acknowledge(); + + /* + * Get the timestamp of the last vsync event in ns. This call has + * the same side effect on events as Acknowledge(), which saves + * an IPC message. + */ + int GetLastTimestamp(int64_t* timestamp_ns); + + /* + * Get vsync scheduling info. + * Get the estimated timestamp of the next GPU lens warp preemption event in + * ns. Also returns the corresponding vsync count that the next lens warp + * operation will target. This call has the same side effect on events as + * Acknowledge(), which saves an IPC message. + */ + int GetSchedInfo(int64_t* vsync_period_ns, int64_t* next_timestamp_ns, + uint32_t* next_vsync_count); + + private: + friend BASE; + + VSyncClient(); + explicit VSyncClient(long timeout_ms); + + VSyncClient(const VSyncClient&) = delete; + void operator=(const VSyncClient&) = delete; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_VSYNC_CLIENT_H_ diff --git a/libs/vr/libdisplay/system/CPPLINT.cfg b/libs/vr/libdisplay/system/CPPLINT.cfg new file mode 100644 index 0000000000..2f8a3c018c --- /dev/null +++ b/libs/vr/libdisplay/system/CPPLINT.cfg @@ -0,0 +1 @@ +filter=-build/header_guard diff --git a/libs/vr/libdisplay/vsync_client.cpp b/libs/vr/libdisplay/vsync_client.cpp new file mode 100644 index 0000000000..bc6cf6cabe --- /dev/null +++ b/libs/vr/libdisplay/vsync_client.cpp @@ -0,0 +1,76 @@ +#include "include/private/dvr/vsync_client.h" + +#include <log/log.h> + +#include <pdx/default_transport/client_channel_factory.h> +#include <private/dvr/display_protocol.h> + +using android::dvr::display::VSyncProtocol; +using android::pdx::Transaction; + +namespace android { +namespace dvr { + +VSyncClient::VSyncClient(long timeout_ms) + : BASE(pdx::default_transport::ClientChannelFactory::Create( + VSyncProtocol::kClientPath), + timeout_ms) {} + +VSyncClient::VSyncClient() + : BASE(pdx::default_transport::ClientChannelFactory::Create( + VSyncProtocol::kClientPath)) {} + +int VSyncClient::Wait(int64_t* timestamp_ns) { + auto status = InvokeRemoteMethod<VSyncProtocol::Wait>(); + if (!status) { + ALOGE("VSyncClient::Wait: Failed to wait for vsync: %s", + status.GetErrorMessage().c_str()); + return -status.error(); + } + + if (timestamp_ns != nullptr) { + *timestamp_ns = status.get(); + } + return 0; +} + +int VSyncClient::GetFd() { return event_fd(); } + +int VSyncClient::GetLastTimestamp(int64_t* timestamp_ns) { + auto status = InvokeRemoteMethod<VSyncProtocol::GetLastTimestamp>(); + if (!status) { + ALOGE("VSyncClient::GetLastTimestamp: Failed to get vsync timestamp: %s", + status.GetErrorMessage().c_str()); + return -status.error(); + } + *timestamp_ns = status.get(); + return 0; +} + +int VSyncClient::GetSchedInfo(int64_t* vsync_period_ns, int64_t* timestamp_ns, + uint32_t* next_vsync_count) { + if (!vsync_period_ns || !timestamp_ns || !next_vsync_count) + return -EINVAL; + + auto status = InvokeRemoteMethod<VSyncProtocol::GetSchedInfo>(); + if (!status) { + ALOGE("VSyncClient::GetSchedInfo:: Failed to get warp timestamp: %s", + status.GetErrorMessage().c_str()); + return -status.error(); + } + + *vsync_period_ns = status.get().vsync_period_ns; + *timestamp_ns = status.get().timestamp_ns; + *next_vsync_count = status.get().next_vsync_count; + return 0; +} + +int VSyncClient::Acknowledge() { + auto status = InvokeRemoteMethod<VSyncProtocol::Acknowledge>(); + ALOGE_IF(!status, "VSuncClient::Acknowledge: Failed to ack vsync because: %s", + status.GetErrorMessage().c_str()); + return ReturnStatusOrError(status); +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp new file mode 100644 index 0000000000..fa78b1c136 --- /dev/null +++ b/libs/vr/libdvr/Android.bp @@ -0,0 +1,89 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +cc_library_headers { + name: "libdvr_headers", + owner: "google", + export_include_dirs: ["include"], +} + +cflags = [ + "-DLOG_TAG=\"libdvr\"", +] + +srcs = [ + "dvr_api.cpp", + "dvr_buffer.cpp", + "dvr_buffer_queue.cpp", + "dvr_display_manager.cpp", + "dvr_hardware_composer_client.cpp", + "dvr_surface.cpp", + "dvr_vsync.cpp", +] + +static_libs = [ + "libbufferhub", + "libbufferhubqueue", + "libdisplay", + "libvrsensor", + "libvirtualtouchpadclient", + "libvr_hwc-impl", + "libvr_hwc-binder", + "libgrallocusage", + "libpdx_default_transport", +] + +shared_libs = [ + "android.hardware.graphics.bufferqueue@1.0", + "android.hidl.token@1.0-utils", + "libbase", + "libbinder", + "liblog", + "libcutils", + "libutils", + "libnativewindow", + "libgui", + "libui", +] + +cc_library_shared { + name: "libdvr", + owner: "google", + cflags: cflags, + header_libs: ["libdvr_headers"], + export_header_lib_headers: ["libdvr_headers"], + srcs: srcs, + static_libs: static_libs, + shared_libs: shared_libs, + version_script: "exported_apis.lds", +} + +// Also build a static libdvr for linking into tests. The linker script +// restricting function access in the shared lib makes it inconvenient to use in +// test code. +cc_library_static { + name: "libdvr_static", + owner: "google", + cflags: cflags, + header_libs: ["libdvr_headers"], + export_header_lib_headers: ["libdvr_headers"], + srcs: srcs, + static_libs: static_libs, + shared_libs: shared_libs, +} + +subdirs = [ + "tests", +] diff --git a/libs/vr/libdvr/dvr_api.cpp b/libs/vr/libdvr/dvr_api.cpp new file mode 100644 index 0000000000..2c95583d20 --- /dev/null +++ b/libs/vr/libdvr/dvr_api.cpp @@ -0,0 +1,47 @@ +#include "include/dvr/dvr_api.h" + +#include <errno.h> +#include <utils/Log.h> + +// Headers from libdvr +#include <dvr/dvr_buffer.h> +#include <dvr/dvr_buffer_queue.h> +#include <dvr/dvr_display_manager.h> +#include <dvr/dvr_surface.h> +#include <dvr/dvr_vsync.h> + +// Headers not yet moved into libdvr. +// TODO(jwcai) Move these once their callers are moved into Google3. +#include <dvr/dvr_hardware_composer_client.h> +#include <dvr/pose_client.h> +#include <dvr/virtual_touchpad_client.h> + +extern "C" { + +int dvrGetApi(void* api, size_t struct_size, int version) { + ALOGI("dvrGetApi: api=%p struct_size=%zu version=%d", api, struct_size, + version); + if (version == 1) { + if (struct_size != sizeof(DvrApi_v1)) { + ALOGE("dvrGetApi: Size mismatch: expected %zu; actual %zu", + sizeof(DvrApi_v1), struct_size); + return -EINVAL; + } + DvrApi_v1* dvr_api = static_cast<DvrApi_v1*>(api); + +// Defines an API entry for V1 (no version suffix). +#define DVR_V1_API_ENTRY(name) dvr_api->name = dvr##name + +#include "include/dvr/dvr_api_entries.h" + +// Undefine macro definitions to play nice with Google3 style rules. +#undef DVR_V1_API_ENTRY + + return 0; + } + + ALOGE("dvrGetApi: Unknown API version=%d", version); + return -EINVAL; +} + +} // extern "C" diff --git a/libs/vr/libdvr/dvr_buffer.cpp b/libs/vr/libdvr/dvr_buffer.cpp new file mode 100644 index 0000000000..82469b81bf --- /dev/null +++ b/libs/vr/libdvr/dvr_buffer.cpp @@ -0,0 +1,202 @@ +#include "include/dvr/dvr_buffer.h" + +#include <android/hardware_buffer.h> +#include <private/dvr/buffer_hub_client.h> +#include <ui/GraphicBuffer.h> + +#include "dvr_internal.h" + +using namespace android; + +namespace android { +namespace dvr { + +DvrBuffer* CreateDvrBufferFromIonBuffer( + const std::shared_ptr<IonBuffer>& ion_buffer) { + if (!ion_buffer) + return nullptr; + return new DvrBuffer{std::move(ion_buffer)}; +} + +} // namespace dvr +} // namespace android + +namespace { + +int ConvertToAHardwareBuffer(GraphicBuffer* graphic_buffer, + AHardwareBuffer** hardware_buffer) { + if (!hardware_buffer || !graphic_buffer) { + return -EINVAL; + } + *hardware_buffer = reinterpret_cast<AHardwareBuffer*>(graphic_buffer); + AHardwareBuffer_acquire(*hardware_buffer); + return 0; +} + +} // anonymous namespace + +extern "C" { + +void dvrWriteBufferCreateEmpty(DvrWriteBuffer** write_buffer) { + if (write_buffer) + *write_buffer = new DvrWriteBuffer; +} + +void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer) { + delete write_buffer; +} + +int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer) { + return write_buffer && write_buffer->write_buffer; +} + +int dvrWriteBufferClear(DvrWriteBuffer* write_buffer) { + if (!write_buffer) + return -EINVAL; + + write_buffer->write_buffer = nullptr; + return 0; +} + +int dvrWriteBufferGetId(DvrWriteBuffer* write_buffer) { + if (!write_buffer || !write_buffer->write_buffer) + return -EINVAL; + + return write_buffer->write_buffer->id(); +} + +int dvrWriteBufferGetAHardwareBuffer(DvrWriteBuffer* write_buffer, + AHardwareBuffer** hardware_buffer) { + if (!write_buffer || !write_buffer->write_buffer) + return -EINVAL; + + return ConvertToAHardwareBuffer( + write_buffer->write_buffer->buffer()->buffer().get(), hardware_buffer); +} + +int dvrWriteBufferPost(DvrWriteBuffer* write_buffer, int ready_fence_fd, + const void* meta, size_t meta_size_bytes) { + if (!write_buffer || !write_buffer->write_buffer) + return -EINVAL; + + pdx::LocalHandle fence(ready_fence_fd); + int result = write_buffer->write_buffer->Post(fence, meta, meta_size_bytes); + return result; +} + +int dvrWriteBufferGain(DvrWriteBuffer* write_buffer, int* release_fence_fd) { + if (!write_buffer || !write_buffer->write_buffer || !release_fence_fd) + return -EINVAL; + + pdx::LocalHandle release_fence; + int result = write_buffer->write_buffer->Gain(&release_fence); + *release_fence_fd = release_fence.Release(); + return result; +} + +int dvrWriteBufferGainAsync(DvrWriteBuffer* write_buffer) { + if (!write_buffer || !write_buffer->write_buffer) + return -EINVAL; + + return write_buffer->write_buffer->GainAsync(); +} + +void dvrReadBufferCreateEmpty(DvrReadBuffer** read_buffer) { + if (read_buffer) + *read_buffer = new DvrReadBuffer; +} + +void dvrReadBufferDestroy(DvrReadBuffer* read_buffer) { delete read_buffer; } + +int dvrReadBufferIsValid(DvrReadBuffer* read_buffer) { + return read_buffer && read_buffer->read_buffer; +} + +int dvrReadBufferClear(DvrReadBuffer* read_buffer) { + if (!read_buffer) + return -EINVAL; + + read_buffer->read_buffer = nullptr; + return 0; +} + +int dvrReadBufferGetId(DvrReadBuffer* read_buffer) { + if (!read_buffer || !read_buffer->read_buffer) + return -EINVAL; + + return read_buffer->read_buffer->id(); +} + +int dvrReadBufferGetAHardwareBuffer(DvrReadBuffer* read_buffer, + AHardwareBuffer** hardware_buffer) { + if (!read_buffer || !read_buffer->read_buffer) + return -EINVAL; + + return ConvertToAHardwareBuffer( + read_buffer->read_buffer->buffer()->buffer().get(), hardware_buffer); +} + +int dvrReadBufferAcquire(DvrReadBuffer* read_buffer, int* ready_fence_fd, + void* meta, size_t meta_size_bytes) { + if (!read_buffer || !read_buffer->read_buffer) + return -EINVAL; + + pdx::LocalHandle ready_fence; + int result = + read_buffer->read_buffer->Acquire(&ready_fence, meta, meta_size_bytes); + *ready_fence_fd = ready_fence.Release(); + return result; +} + +int dvrReadBufferRelease(DvrReadBuffer* read_buffer, int release_fence_fd) { + if (!read_buffer || !read_buffer->read_buffer) + return -EINVAL; + + pdx::LocalHandle fence(release_fence_fd); + int result = read_buffer->read_buffer->Release(fence); + return result; +} + +int dvrReadBufferReleaseAsync(DvrReadBuffer* read_buffer) { + if (!read_buffer || !read_buffer->read_buffer) + return -EINVAL; + + return read_buffer->read_buffer->ReleaseAsync(); +} + +void dvrBufferDestroy(DvrBuffer* buffer) { delete buffer; } + +int dvrBufferGetAHardwareBuffer(DvrBuffer* buffer, + AHardwareBuffer** hardware_buffer) { + if (!buffer || !buffer->buffer || !hardware_buffer) { + return -EINVAL; + } + + return ConvertToAHardwareBuffer(buffer->buffer->buffer().get(), + hardware_buffer); +} + +const struct native_handle* dvrWriteBufferGetNativeHandle( + DvrWriteBuffer* write_buffer) { + if (!write_buffer || !write_buffer->write_buffer) + return nullptr; + + return write_buffer->write_buffer->native_handle(); +} + +const struct native_handle* dvrReadBufferGetNativeHandle( + DvrReadBuffer* read_buffer) { + if (!read_buffer || !read_buffer->read_buffer) + return nullptr; + + return read_buffer->read_buffer->native_handle(); +} + +const struct native_handle* dvrBufferGetNativeHandle(DvrBuffer* buffer) { + if (!buffer || !buffer->buffer) + return nullptr; + + return buffer->buffer->handle(); +} + +} // extern "C" diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp new file mode 100644 index 0000000000..f668510304 --- /dev/null +++ b/libs/vr/libdvr/dvr_buffer_queue.cpp @@ -0,0 +1,204 @@ +#include "include/dvr/dvr_api.h" +#include "include/dvr/dvr_buffer_queue.h" + +#include <android/native_window.h> +#include <gui/Surface.h> +#include <private/dvr/buffer_hub_queue_client.h> +#include <private/dvr/buffer_hub_queue_producer.h> + +#include "dvr_internal.h" + +#define CHECK_PARAM(param) \ + LOG_ALWAYS_FATAL_IF(param == nullptr, "%s: " #param "cannot be NULL.", \ + __FUNCTION__) + +using namespace android; + +namespace android { +namespace dvr { + +DvrWriteBufferQueue* CreateDvrWriteBufferQueueFromProducerQueue( + const std::shared_ptr<dvr::ProducerQueue>& producer_queue) { + return new DvrWriteBufferQueue{std::move(producer_queue)}; +} + +DvrReadBufferQueue* CreateDvrReadBufferQueueFromConsumerQueue( + const std::shared_ptr<dvr::ConsumerQueue>& consumer_queue) { + return new DvrReadBufferQueue{std::move(consumer_queue)}; +} + +dvr::ProducerQueue* GetProducerQueueFromDvrWriteBufferQueue( + DvrWriteBufferQueue* write_queue) { + return write_queue->producer_queue.get(); +} + +} // namespace dvr +} // namespace android + +extern "C" { + +void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue) { + if (write_queue != nullptr && write_queue->native_window != nullptr) + ANativeWindow_release(write_queue->native_window); + + delete write_queue; +} + +ssize_t dvrWriteBufferQueueGetCapacity(DvrWriteBufferQueue* write_queue) { + if (!write_queue || !write_queue->producer_queue) + return -EINVAL; + + return write_queue->producer_queue->capacity(); +} + +int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue) { + if (!write_queue) + return -EINVAL; + + return write_queue->producer_queue->id(); +} + +int dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue, + ANativeWindow** out_window) { + if (!write_queue || !out_window) + return -EINVAL; + + if (write_queue->producer_queue->metadata_size() != + sizeof(DvrNativeBufferMetadata)) { + ALOGE( + "The size of buffer metadata (%zu) of the write queue does not match " + "of size of DvrNativeBufferMetadata (%zu).", + write_queue->producer_queue->metadata_size(), + sizeof(DvrNativeBufferMetadata)); + return -EINVAL; + } + + // Lazy creation of |native_window|. + if (write_queue->native_window == nullptr) { + sp<IGraphicBufferProducer> gbp = + dvr::BufferHubQueueProducer::Create(write_queue->producer_queue); + sp<Surface> surface = new Surface(gbp, true); + write_queue->native_window = static_cast<ANativeWindow*>(surface.get()); + ANativeWindow_acquire(write_queue->native_window); + } + + *out_window = write_queue->native_window; + return 0; +} + +int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue, + DvrReadBufferQueue** out_read_queue) { + if (!write_queue || !write_queue->producer_queue || !out_read_queue) + return -EINVAL; + + auto read_queue = std::make_unique<DvrReadBufferQueue>(); + read_queue->consumer_queue = + write_queue->producer_queue->CreateConsumerQueue(); + if (read_queue->consumer_queue == nullptr) { + ALOGE( + "dvrWriteBufferQueueCreateReadQueue: Failed to create consumer queue " + "from DvrWriteBufferQueue[%p].", + write_queue); + return -ENOMEM; + } + + *out_read_queue = read_queue.release(); + return 0; +} + +int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout, + DvrWriteBuffer* write_buffer, + int* out_fence_fd) { + if (!write_queue || !write_queue->producer_queue || !write_buffer || + !out_fence_fd) { + return -EINVAL; + } + + size_t slot; + pdx::LocalHandle release_fence; + auto buffer_status = + write_queue->producer_queue->Dequeue(timeout, &slot, &release_fence); + if (!buffer_status) { + ALOGE_IF(buffer_status.error() != ETIMEDOUT, + "dvrWriteBufferQueueDequeue: Failed to dequeue buffer: %s", + buffer_status.GetErrorMessage().c_str()); + return -buffer_status.error(); + } + + write_buffer->write_buffer = buffer_status.take(); + *out_fence_fd = release_fence.Release(); + return 0; +} + +// ReadBufferQueue +void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue) { + delete read_queue; +} + +ssize_t dvrReadBufferQueueGetCapacity(DvrReadBufferQueue* read_queue) { + if (!read_queue) + return -EINVAL; + + return read_queue->consumer_queue->capacity(); +} + +int dvrReadBufferQueueGetId(DvrReadBufferQueue* read_queue) { + if (!read_queue) + return -EINVAL; + + return read_queue->consumer_queue->id(); +} + +int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue, + DvrReadBufferQueue** out_read_queue) { + if (!read_queue || !read_queue->consumer_queue || !out_read_queue) + return -EINVAL; + + auto new_read_queue = std::make_unique<DvrReadBufferQueue>(); + new_read_queue->consumer_queue = + read_queue->consumer_queue->CreateConsumerQueue(); + if (new_read_queue->consumer_queue == nullptr) { + ALOGE( + "dvrReadBufferQueueCreateReadQueue: Failed to create consumer queue " + "from DvrReadBufferQueue[%p].", + read_queue); + return -ENOMEM; + } + + *out_read_queue = new_read_queue.release(); + return 0; +} + +int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout, + DvrReadBuffer* read_buffer, int* out_fence_fd, + void* out_meta, size_t meta_size_bytes) { + if (!read_queue || !read_queue->consumer_queue || !read_buffer || + !out_fence_fd || !out_meta) { + return -EINVAL; + } + + if (meta_size_bytes != read_queue->consumer_queue->metadata_size()) { + ALOGE( + "dvrReadBufferQueueDequeue: Invalid metadata size, expected (%zu), " + "but actual (%zu).", + read_queue->consumer_queue->metadata_size(), meta_size_bytes); + return -EINVAL; + } + + size_t slot; + pdx::LocalHandle acquire_fence; + auto buffer_status = read_queue->consumer_queue->Dequeue( + timeout, &slot, out_meta, meta_size_bytes, &acquire_fence); + if (!buffer_status) { + ALOGE_IF(buffer_status.error() != ETIMEDOUT, + "dvrReadBufferQueueDequeue: Failed to dequeue buffer: %s", + buffer_status.GetErrorMessage().c_str()); + return -buffer_status.error(); + } + + read_buffer->read_buffer = buffer_status.take(); + *out_fence_fd = acquire_fence.Release(); + return 0; +} + +} // extern "C" diff --git a/libs/vr/libdvr/dvr_display_manager.cpp b/libs/vr/libdvr/dvr_display_manager.cpp new file mode 100644 index 0000000000..87636ecd91 --- /dev/null +++ b/libs/vr/libdvr/dvr_display_manager.cpp @@ -0,0 +1,304 @@ +#include "include/dvr/dvr_display_manager.h" + +#include <dvr/dvr_buffer.h> +#include <pdx/rpc/variant.h> +#include <private/android/AHardwareBufferHelpers.h> +#include <private/dvr/buffer_hub_client.h> +#include <private/dvr/buffer_hub_queue_client.h> +#include <private/dvr/display_manager_client.h> + +#include "dvr_internal.h" + +using android::AHardwareBuffer_convertToGrallocUsageBits; +using android::dvr::BufferConsumer; +using android::dvr::display::DisplayManagerClient; +using android::dvr::display::SurfaceAttributes; +using android::dvr::display::SurfaceAttribute; +using android::dvr::display::SurfaceState; +using android::dvr::CreateDvrReadBufferQueueFromConsumerQueue; +using android::pdx::rpc::EmptyVariant; + +namespace { + +// Extracts type and value from the attribute Variant and writes them into the +// respective fields of DvrSurfaceAttribute. +struct AttributeVisitor { + DvrSurfaceAttribute* attribute; + + void operator()(int32_t value) { + attribute->value.int32_value = value; + attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32; + } + void operator()(int64_t value) { + attribute->value.int64_value = value; + attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT64; + } + void operator()(bool value) { + attribute->value.bool_value = value; + attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL; + } + void operator()(float value) { + attribute->value.float_value = value; + attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT; + } + void operator()(const std::array<float, 2>& value) { + std::copy(value.cbegin(), value.cend(), attribute->value.float2_value); + attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2; + } + void operator()(const std::array<float, 3>& value) { + std::copy(value.cbegin(), value.cend(), attribute->value.float3_value); + attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3; + } + void operator()(const std::array<float, 4>& value) { + std::copy(value.cbegin(), value.cend(), attribute->value.float4_value); + attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4; + } + void operator()(const std::array<float, 8>& value) { + std::copy(value.cbegin(), value.cend(), attribute->value.float8_value); + attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8; + } + void operator()(const std::array<float, 16>& value) { + std::copy(value.cbegin(), value.cend(), attribute->value.float16_value); + attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16; + } + void operator()(EmptyVariant) { + attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_NONE; + } +}; + +size_t ConvertSurfaceAttributes(const SurfaceAttributes& surface_attributes, + DvrSurfaceAttribute* attributes, + size_t max_count) { + size_t count = 0; + for (const auto& attribute : surface_attributes) { + if (count >= max_count) + break; + + // Copy the key and extract the Variant value using a visitor. + attributes[count].key = attribute.first; + attribute.second.Visit(AttributeVisitor{&attributes[count]}); + count++; + } + + return count; +} + +} // anonymous namespace + +extern "C" { + +struct DvrDisplayManager { + std::unique_ptr<DisplayManagerClient> client; +}; + +struct DvrSurfaceState { + std::vector<SurfaceState> state; +}; + +int dvrDisplayManagerCreate(DvrDisplayManager** client_out) { + if (!client_out) + return -EINVAL; + + auto client = DisplayManagerClient::Create(); + if (!client) { + ALOGE("dvrDisplayManagerCreate: Failed to create display manager client!"); + return -EIO; + } + + *client_out = new DvrDisplayManager{std::move(client)}; + return 0; +} + +void dvrDisplayManagerDestroy(DvrDisplayManager* client) { delete client; } + +int dvrDisplayManagerSetupNamedBuffer(DvrDisplayManager* client, + const char* name, size_t size, + uint64_t usage, DvrBuffer** buffer_out) { + if (!client || !name || !buffer_out) + return -EINVAL; + + uint64_t gralloc_usage = AHardwareBuffer_convertToGrallocUsageBits(usage); + + auto buffer_status = + client->client->SetupNamedBuffer(name, size, gralloc_usage); + if (!buffer_status) { + ALOGE("dvrDisplayManagerSetupPoseBuffer: Failed to setup named buffer: %s", + buffer_status.GetErrorMessage().c_str()); + return -buffer_status.error(); + } + + *buffer_out = CreateDvrBufferFromIonBuffer(buffer_status.take()); + return 0; +} + +int dvrDisplayManagerGetEventFd(DvrDisplayManager* client) { + if (!client) + return -EINVAL; + + return client->client->event_fd(); +} + +int dvrDisplayManagerTranslateEpollEventMask(DvrDisplayManager* client, + int in_events, int* out_events) { + if (!client || !out_events) + return -EINVAL; + + auto status = client->client->GetEventMask(in_events); + if (!status) + return -status.error(); + + *out_events = status.get(); + return 0; +} + +int dvrDisplayManagerGetSurfaceState(DvrDisplayManager* client, + DvrSurfaceState* state) { + if (!client || !state) + return -EINVAL; + + auto status = client->client->GetSurfaceState(); + if (!status) + return -status.error(); + + state->state = status.take(); + return 0; +} + +int dvrDisplayManagerGetReadBufferQueue(DvrDisplayManager* client, + int surface_id, int queue_id, + DvrReadBufferQueue** queue_out) { + if (!client || !queue_out) + return -EINVAL; + + auto status = client->client->GetSurfaceQueue(surface_id, queue_id); + if (!status) { + ALOGE("dvrDisplayManagerGetReadBufferQueue: Failed to get queue: %s", + status.GetErrorMessage().c_str()); + return -status.error(); + } + + *queue_out = CreateDvrReadBufferQueueFromConsumerQueue(status.take()); + return 0; +} + +int dvrSurfaceStateCreate(DvrSurfaceState** surface_state_out) { + if (!surface_state_out) + return -EINVAL; + + *surface_state_out = new DvrSurfaceState{}; + return 0; +} + +void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state) { + delete surface_state; +} + +int dvrSurfaceStateGetSurfaceCount(DvrSurfaceState* surface_state, + size_t* count_out) { + if (!surface_state) + return -EINVAL; + + *count_out = surface_state->state.size(); + return 0; +} + +int dvrSurfaceStateGetUpdateFlags(DvrSurfaceState* surface_state, + size_t surface_index, + DvrSurfaceUpdateFlags* flags_out) { + if (!surface_state || surface_index >= surface_state->state.size()) + return -EINVAL; + + *flags_out = surface_state->state[surface_index].update_flags; + return 0; +} + +int dvrSurfaceStateGetSurfaceId(DvrSurfaceState* surface_state, + size_t surface_index, int* surface_id_out) { + if (!surface_state || surface_index >= surface_state->state.size()) + return -EINVAL; + + *surface_id_out = surface_state->state[surface_index].surface_id; + return 0; +} + +int dvrSurfaceStateGetProcessId(DvrSurfaceState* surface_state, + size_t surface_index, int* process_id_out) { + if (!surface_state || surface_index >= surface_state->state.size()) + return -EINVAL; + + *process_id_out = surface_state->state[surface_index].process_id; + return 0; +} + +int dvrSurfaceStateGetQueueCount(DvrSurfaceState* surface_state, + size_t surface_index, size_t* count_out) { + if (!surface_state || surface_index >= surface_state->state.size()) + return -EINVAL; + + *count_out = surface_state->state[surface_index].queue_ids.size(); + return 0; +} + +ssize_t dvrSurfaceStateGetQueueIds(DvrSurfaceState* surface_state, + size_t surface_index, int* queue_ids, + size_t max_count) { + if (!surface_state || surface_index >= surface_state->state.size()) + return -EINVAL; + + size_t i; + const auto& state = surface_state->state[surface_index]; + for (i = 0; i < std::min(max_count, state.queue_ids.size()); i++) { + queue_ids[i] = state.queue_ids[i]; + } + + return i; +} + +int dvrSurfaceStateGetZOrder(DvrSurfaceState* surface_state, + size_t surface_index, int* z_order_out) { + if (!surface_state || surface_index >= surface_state->state.size() || + !z_order_out) { + return -EINVAL; + } + + *z_order_out = surface_state->state[surface_index].GetZOrder(); + return 0; +} + +int dvrSurfaceStateGetVisible(DvrSurfaceState* surface_state, + size_t surface_index, bool* visible_out) { + if (!surface_state || surface_index >= surface_state->state.size() || + !visible_out) { + return -EINVAL; + } + + *visible_out = surface_state->state[surface_index].GetVisible(); + return 0; +} + +int dvrSurfaceStateGetAttributeCount(DvrSurfaceState* surface_state, + size_t surface_index, size_t* count_out) { + if (!surface_state || surface_index >= surface_state->state.size() || + !count_out) { + return -EINVAL; + } + + *count_out = surface_state->state[surface_index].surface_attributes.size(); + return 0; +} + +ssize_t dvrSurfaceStateGetAttributes(DvrSurfaceState* surface_state, + size_t surface_index, + DvrSurfaceAttribute* attributes, + size_t max_count) { + if (!surface_state || surface_index >= surface_state->state.size() || + !attributes) { + return -EINVAL; + } + + return ConvertSurfaceAttributes( + surface_state->state[surface_index].surface_attributes, attributes, + max_count); +} + +} // extern "C" diff --git a/libs/vr/libdvr/dvr_hardware_composer_client.cpp b/libs/vr/libdvr/dvr_hardware_composer_client.cpp new file mode 100644 index 0000000000..d3ae299cb9 --- /dev/null +++ b/libs/vr/libdvr/dvr_hardware_composer_client.cpp @@ -0,0 +1,237 @@ +#include "include/dvr/dvr_hardware_composer_client.h" + +#include <android/dvr/IVrComposer.h> +#include <android/dvr/BnVrComposerCallback.h> +#include <android/hardware_buffer.h> +#include <binder/IServiceManager.h> +#include <private/android/AHardwareBufferHelpers.h> + +#include <memory> + +struct DvrHwcFrame { + android::dvr::ComposerView::Frame frame; +}; + +namespace { + +class HwcCallback : public android::dvr::BnVrComposerCallback { + public: + explicit HwcCallback(DvrHwcOnFrameCallback callback, + void* client_state); + ~HwcCallback() override; + + std::unique_ptr<DvrHwcFrame> DequeueFrame(); + + private: + // android::dvr::BnVrComposerCallback: + android::binder::Status onNewFrame( + const android::dvr::ParcelableComposerFrame& frame, + android::dvr::ParcelableUniqueFd* fence) override; + + DvrHwcOnFrameCallback callback_; + void* client_state_; + + HwcCallback(const HwcCallback&) = delete; + void operator=(const HwcCallback&) = delete; +}; + +HwcCallback::HwcCallback(DvrHwcOnFrameCallback callback, void* client_state) + : callback_(callback), client_state_(client_state) {} + +HwcCallback::~HwcCallback() {} + +android::binder::Status HwcCallback::onNewFrame( + const android::dvr::ParcelableComposerFrame& frame, + android::dvr::ParcelableUniqueFd* fence) { + std::unique_ptr<DvrHwcFrame> dvr_frame(new DvrHwcFrame()); + dvr_frame->frame = frame.frame(); + + fence->set_fence(android::base::unique_fd(callback_(client_state_, + dvr_frame.release()))); + return android::binder::Status::ok(); +} + +} // namespace + +struct DvrHwcClient { + android::sp<android::dvr::IVrComposer> composer; + android::sp<HwcCallback> callback; +}; + +DvrHwcClient* dvrHwcClientCreate(DvrHwcOnFrameCallback callback, void* data) { + std::unique_ptr<DvrHwcClient> client(new DvrHwcClient()); + + android::sp<android::IServiceManager> sm(android::defaultServiceManager()); + client->composer = android::interface_cast<android::dvr::IVrComposer>( + sm->getService(android::dvr::IVrComposer::SERVICE_NAME())); + if (!client->composer.get()) + return nullptr; + + client->callback = new HwcCallback(callback, data); + android::binder::Status status = client->composer->registerObserver( + client->callback); + if (!status.isOk()) + return nullptr; + + return client.release(); +} + +void dvrHwcClientDestroy(DvrHwcClient* client) { + delete client; +} + +void dvrHwcFrameDestroy(DvrHwcFrame* frame) { + delete frame; +} + +DvrHwcDisplay dvrHwcFrameGetDisplayId(DvrHwcFrame* frame) { + return frame->frame.display_id; +} + +int32_t dvrHwcFrameGetDisplayWidth(DvrHwcFrame* frame) { + return frame->frame.display_width; +} + +int32_t dvrHwcFrameGetDisplayHeight(DvrHwcFrame* frame) { + return frame->frame.display_height; +} + +bool dvrHwcFrameGetDisplayRemoved(DvrHwcFrame* frame) { + return frame->frame.removed; +} + +size_t dvrHwcFrameGetLayerCount(DvrHwcFrame* frame) { + return frame->frame.layers.size(); +} + +uint32_t dvrHwcFrameGetActiveConfig(DvrHwcFrame* frame) { + return static_cast<uint32_t>(frame->frame.active_config); +} + +uint32_t dvrHwcFrameGetColorMode(DvrHwcFrame* frame) { + return static_cast<uint32_t>(frame->frame.color_mode); +} + +void dvrHwcFrameGetColorTransform(DvrHwcFrame* frame, float* out_matrix, + int32_t* out_hint) { + *out_hint = frame->frame.color_transform_hint; + memcpy(out_matrix, frame->frame.color_transform, + sizeof(frame->frame.color_transform)); +} + +uint32_t dvrHwcFrameGetPowerMode(DvrHwcFrame* frame) { + return static_cast<uint32_t>(frame->frame.power_mode); +} + +uint32_t dvrHwcFrameGetVsyncEnabled(DvrHwcFrame* frame) { + return static_cast<uint32_t>(frame->frame.vsync_enabled); +} + +DvrHwcLayer dvrHwcFrameGetLayerId(DvrHwcFrame* frame, size_t layer_index) { + return frame->frame.layers[layer_index].id; +} + +AHardwareBuffer* dvrHwcFrameGetLayerBuffer(DvrHwcFrame* frame, + size_t layer_index) { + AHardwareBuffer* buffer = android::AHardwareBuffer_from_GraphicBuffer( + frame->frame.layers[layer_index].buffer.get()); + AHardwareBuffer_acquire(buffer); + return buffer; +} + +int dvrHwcFrameGetLayerFence(DvrHwcFrame* frame, size_t layer_index) { + return frame->frame.layers[layer_index].fence->dup(); +} + +DvrHwcRecti dvrHwcFrameGetLayerDisplayFrame(DvrHwcFrame* frame, + size_t layer_index) { + return DvrHwcRecti{ + frame->frame.layers[layer_index].display_frame.left, + frame->frame.layers[layer_index].display_frame.top, + frame->frame.layers[layer_index].display_frame.right, + frame->frame.layers[layer_index].display_frame.bottom, + }; +} + +DvrHwcRectf dvrHwcFrameGetLayerCrop(DvrHwcFrame* frame, size_t layer_index) { + return DvrHwcRectf{ + frame->frame.layers[layer_index].crop.left, + frame->frame.layers[layer_index].crop.top, + frame->frame.layers[layer_index].crop.right, + frame->frame.layers[layer_index].crop.bottom, + }; +} + +DvrHwcBlendMode dvrHwcFrameGetLayerBlendMode(DvrHwcFrame* frame, + size_t layer_index) { + return static_cast<DvrHwcBlendMode>( + frame->frame.layers[layer_index].blend_mode); +} + +float dvrHwcFrameGetLayerAlpha(DvrHwcFrame* frame, size_t layer_index) { + return frame->frame.layers[layer_index].alpha; +} + +uint32_t dvrHwcFrameGetLayerType(DvrHwcFrame* frame, size_t layer_index) { + return frame->frame.layers[layer_index].type; +} + +uint32_t dvrHwcFrameGetLayerApplicationId(DvrHwcFrame* frame, + size_t layer_index) { + return frame->frame.layers[layer_index].app_id; +} + +uint32_t dvrHwcFrameGetLayerZOrder(DvrHwcFrame* frame, size_t layer_index) { + return frame->frame.layers[layer_index].z_order; +} + +void dvrHwcFrameGetLayerCursor(DvrHwcFrame* frame, size_t layer_index, + int32_t* out_x, int32_t* out_y) { + *out_x = frame->frame.layers[layer_index].cursor_x; + *out_y = frame->frame.layers[layer_index].cursor_y; +} + +uint32_t dvrHwcFrameGetLayerTransform(DvrHwcFrame* frame, size_t layer_index) { + return frame->frame.layers[layer_index].transform; +} + +uint32_t dvrHwcFrameGetLayerDataspace(DvrHwcFrame* frame, size_t layer_index) { + return frame->frame.layers[layer_index].dataspace; +} + +uint32_t dvrHwcFrameGetLayerColor(DvrHwcFrame* frame, size_t layer_index) { + const auto& color = frame->frame.layers[layer_index].color; + return color.r | (static_cast<uint32_t>(color.g) << 8) | + (static_cast<uint32_t>(color.b) << 16) | + (static_cast<uint32_t>(color.a) << 24); +} + +uint32_t dvrHwcFrameGetLayerNumVisibleRegions(DvrHwcFrame* frame, + size_t layer_index) { + return frame->frame.layers[layer_index].visible_regions.size(); +} + +DvrHwcRecti dvrHwcFrameGetLayerVisibleRegion(DvrHwcFrame* frame, + size_t layer_index, size_t index) { + return DvrHwcRecti{ + frame->frame.layers[layer_index].visible_regions[index].left, + frame->frame.layers[layer_index].visible_regions[index].top, + frame->frame.layers[layer_index].visible_regions[index].right, + frame->frame.layers[layer_index].visible_regions[index].bottom, + }; +} + +uint32_t dvrHwcFrameGetLayerNumDamagedRegions(DvrHwcFrame* frame, + size_t layer_index) { + return frame->frame.layers[layer_index].damaged_regions.size(); +} + +DvrHwcRecti dvrHwcFrameGetLayerDamagedRegion(DvrHwcFrame* frame, + size_t layer_index, size_t index) { + return DvrHwcRecti{ + frame->frame.layers[layer_index].damaged_regions[index].left, + frame->frame.layers[layer_index].damaged_regions[index].top, + frame->frame.layers[layer_index].damaged_regions[index].right, + frame->frame.layers[layer_index].damaged_regions[index].bottom, + }; +} diff --git a/libs/vr/libdvr/dvr_internal.h b/libs/vr/libdvr/dvr_internal.h new file mode 100644 index 0000000000..89bef09871 --- /dev/null +++ b/libs/vr/libdvr/dvr_internal.h @@ -0,0 +1,70 @@ +#ifndef ANDROID_DVR_INTERNAL_H_ +#define ANDROID_DVR_INTERNAL_H_ + +#include <sys/cdefs.h> + +#include <memory> + +extern "C" { + +typedef struct DvrBuffer DvrBuffer; +typedef struct DvrReadBuffer DvrReadBuffer; +typedef struct DvrWriteBuffer DvrWriteBuffer; +typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; +typedef struct DvrReadBufferQueue DvrReadBufferQueue; + +} // extern "C" + +namespace android { +namespace dvr { + +class BufferProducer; +class BufferConsumer; +class ConsumerQueue; +class IonBuffer; +class ProducerQueue; + +DvrBuffer* CreateDvrBufferFromIonBuffer( + const std::shared_ptr<IonBuffer>& ion_buffer); + +DvrReadBuffer* CreateDvrReadBufferFromBufferConsumer( + const std::shared_ptr<BufferConsumer>& buffer_consumer); +DvrWriteBuffer* CreateDvrWriteBufferFromBufferProducer( + const std::shared_ptr<BufferProducer>& buffer_producer); + +DvrReadBufferQueue* CreateDvrReadBufferQueueFromConsumerQueue( + const std::shared_ptr<ConsumerQueue>& consumer_queue); +DvrWriteBufferQueue* CreateDvrWriteBufferQueueFromProducerQueue( + const std::shared_ptr<ProducerQueue>& producer_queue); +ProducerQueue* GetProducerQueueFromDvrWriteBufferQueue( + DvrWriteBufferQueue* write_queue); + +} // namespace dvr +} // namespace android + +extern "C" { + +struct DvrWriteBuffer { + std::shared_ptr<android::dvr::BufferProducer> write_buffer; +}; + +struct DvrReadBuffer { + std::shared_ptr<android::dvr::BufferConsumer> read_buffer; +}; + +struct DvrBuffer { + std::shared_ptr<android::dvr::IonBuffer> buffer; +}; + +struct DvrWriteBufferQueue { + std::shared_ptr<android::dvr::ProducerQueue> producer_queue; + ANativeWindow* native_window{nullptr}; +}; + +struct DvrReadBufferQueue { + std::shared_ptr<android::dvr::ConsumerQueue> consumer_queue; +}; + +} // extern "C" + +#endif // ANDROID_DVR_INTERNAL_H_ diff --git a/libs/vr/libdvr/dvr_surface.cpp b/libs/vr/libdvr/dvr_surface.cpp new file mode 100644 index 0000000000..67e2ae88ac --- /dev/null +++ b/libs/vr/libdvr/dvr_surface.cpp @@ -0,0 +1,182 @@ +#include "include/dvr/dvr_surface.h" + +#include <inttypes.h> + +#include <private/dvr/display_client.h> + +#include "dvr_internal.h" + +using android::dvr::display::DisplayClient; +using android::dvr::display::Surface; +using android::dvr::display::SurfaceAttributes; +using android::dvr::display::SurfaceAttributeValue; +using android::dvr::CreateDvrReadBufferFromBufferConsumer; +using android::dvr::CreateDvrWriteBufferQueueFromProducerQueue; + +namespace { + +bool ConvertSurfaceAttributes(const DvrSurfaceAttribute* attributes, + size_t attribute_count, + SurfaceAttributes* surface_attributes, + size_t* error_index) { + for (size_t i = 0; i < attribute_count; i++) { + SurfaceAttributeValue value; + switch (attributes[i].value.type) { + case DVR_SURFACE_ATTRIBUTE_TYPE_INT32: + value = attributes[i].value.int32_value; + break; + case DVR_SURFACE_ATTRIBUTE_TYPE_INT64: + value = attributes[i].value.int64_value; + break; + case DVR_SURFACE_ATTRIBUTE_TYPE_BOOL: + value = attributes[i].value.bool_value; + break; + case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT: + value = attributes[i].value.float_value; + break; + case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2: + value = attributes[i].value.float2_value; + break; + case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3: + value = attributes[i].value.float3_value; + break; + case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4: + value = attributes[i].value.float4_value; + break; + case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8: + value = attributes[i].value.float8_value; + break; + case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16: + value = attributes[i].value.float16_value; + break; + default: + *error_index = i; + return false; + } + + surface_attributes->emplace(attributes[i].key, value); + } + + return true; +} + +} // anonymous namespace + +extern "C" { + +struct DvrSurface { + std::unique_ptr<Surface> surface; +}; + +int dvrSurfaceCreate(const DvrSurfaceAttribute* attributes, + size_t attribute_count, DvrSurface** out_surface) { + if (out_surface == nullptr) { + ALOGE("dvrSurfaceCreate: Invalid inputs: out_surface=%p.", out_surface); + return -EINVAL; + } + + size_t error_index; + SurfaceAttributes surface_attributes; + if (!ConvertSurfaceAttributes(attributes, attribute_count, + &surface_attributes, &error_index)) { + ALOGE("dvrSurfaceCreate: Invalid surface attribute type: %" PRIu64, + attributes[error_index].value.type); + return -EINVAL; + } + + auto status = Surface::CreateSurface(surface_attributes); + if (!status) { + ALOGE("dvrSurfaceCreate:: Failed to create display surface: %s", + status.GetErrorMessage().c_str()); + return -status.error(); + } + + *out_surface = new DvrSurface{status.take()}; + return 0; +} + +void dvrSurfaceDestroy(DvrSurface* surface) { delete surface; } + +int dvrSurfaceGetId(DvrSurface* surface) { + return surface->surface->surface_id(); +} + +int dvrSurfaceSetAttributes(DvrSurface* surface, + const DvrSurfaceAttribute* attributes, + size_t attribute_count) { + if (surface == nullptr || attributes == nullptr) { + ALOGE( + "dvrSurfaceSetAttributes: Invalid inputs: surface=%p attributes=%p " + "attribute_count=%zu", + surface, attributes, attribute_count); + return -EINVAL; + } + + size_t error_index; + SurfaceAttributes surface_attributes; + if (!ConvertSurfaceAttributes(attributes, attribute_count, + &surface_attributes, &error_index)) { + ALOGE("dvrSurfaceSetAttributes: Invalid surface attribute type: %" PRIu64, + attributes[error_index].value.type); + return -EINVAL; + } + + auto status = surface->surface->SetAttributes(surface_attributes); + if (!status) { + ALOGE("dvrSurfaceSetAttributes: Failed to set attributes: %s", + status.GetErrorMessage().c_str()); + return -status.error(); + } + + return 0; +} + +int dvrSurfaceCreateWriteBufferQueue(DvrSurface* surface, uint32_t width, + uint32_t height, uint32_t format, + uint32_t layer_count, uint64_t usage, + size_t capacity, + DvrWriteBufferQueue** out_writer) { + if (surface == nullptr || out_writer == nullptr) { + ALOGE( + "dvrSurfaceCreateWriteBufferQueue: Invalid inputs: surface=%p, " + "out_writer=%p.", + surface, out_writer); + return -EINVAL; + } + + auto status = surface->surface->CreateQueue(width, height, layer_count, + format, usage, capacity); + if (!status) { + ALOGE("dvrSurfaceCreateWriteBufferQueue: Failed to create queue: %s", + status.GetErrorMessage().c_str()); + return -status.error(); + } + + *out_writer = CreateDvrWriteBufferQueueFromProducerQueue(status.take()); + return 0; +} + +int dvrGetNamedBuffer(const char* name, DvrBuffer** out_buffer) { + auto client = DisplayClient::Create(); + if (!client) { + ALOGE("dvrGetNamedBuffer: Failed to create display client!"); + return -ECOMM; + } + + if (out_buffer == nullptr || name == nullptr) { + ALOGE("dvrGetNamedBuffer: Invalid inputs: name=%p, out_buffer=%p.", name, + out_buffer); + return -EINVAL; + } + + auto status = client->GetNamedBuffer(name); + if (!status) { + ALOGE("dvrGetNamedBuffer: Failed to find named buffer name=%s: %s", name, + status.GetErrorMessage().c_str()); + return -status.error(); + } + *out_buffer = CreateDvrBufferFromIonBuffer(status.take()); + return 0; +} + +} // extern "C" diff --git a/libs/vr/libdvr/dvr_vsync.cpp b/libs/vr/libdvr/dvr_vsync.cpp new file mode 100644 index 0000000000..099240e53a --- /dev/null +++ b/libs/vr/libdvr/dvr_vsync.cpp @@ -0,0 +1,33 @@ +#include "include/dvr/dvr_vsync.h" + +#include <utils/Log.h> + +#include <private/dvr/vsync_client.h> + +extern "C" { + +struct DvrVSyncClient { + std::unique_ptr<android::dvr::VSyncClient> client; +}; + +int dvrVSyncClientCreate(DvrVSyncClient** client_out) { + auto client = android::dvr::VSyncClient::Create(); + if (!client) { + ALOGE("dvrVSyncClientCreate: Failed to create vsync client!"); + return -EIO; + } + + *client_out = new DvrVSyncClient{std::move(client)}; + return 0; +} + +void dvrVSyncClientDestroy(DvrVSyncClient* client) { delete client; } + +int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns, + int64_t* next_timestamp_ns, + uint32_t* next_vsync_count) { + return client->client->GetSchedInfo(vsync_period_ns, next_timestamp_ns, + next_vsync_count); +} + +} // extern "C" diff --git a/libs/vr/libdvr/exported_apis.lds b/libs/vr/libdvr/exported_apis.lds new file mode 100644 index 0000000000..5ecb49861c --- /dev/null +++ b/libs/vr/libdvr/exported_apis.lds @@ -0,0 +1,9 @@ +{ + global: + # Whitelist the function to load the dvr api. + dvrGetApi; + + local: + # Hide everything else. + *; +}; diff --git a/libs/vr/libdvr/include/CPPLINT.cfg b/libs/vr/libdvr/include/CPPLINT.cfg new file mode 100644 index 0000000000..2f8a3c018c --- /dev/null +++ b/libs/vr/libdvr/include/CPPLINT.cfg @@ -0,0 +1 @@ +filter=-build/header_guard diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h new file mode 100644 index 0000000000..8a203e0694 --- /dev/null +++ b/libs/vr/libdvr/include/dvr/dvr_api.h @@ -0,0 +1,312 @@ +#ifndef ANDROID_DVR_API_H_ +#define ANDROID_DVR_API_H_ + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <sys/cdefs.h> +#include <unistd.h> + +#include <dvr/dvr_hardware_composer_defs.h> + +__BEGIN_DECLS + +typedef struct ANativeWindow ANativeWindow; + +typedef struct DvrPoseAsync DvrPoseAsync; + +typedef uint64_t DvrSurfaceUpdateFlags; +typedef struct DvrDisplayManager DvrDisplayManager; +typedef struct DvrSurfaceState DvrSurfaceState; +typedef struct DvrPose DvrPose; +typedef struct DvrVSyncClient DvrVSyncClient; +typedef struct DvrVirtualTouchpad DvrVirtualTouchpad; + +typedef struct DvrBuffer DvrBuffer; +typedef struct DvrWriteBuffer DvrWriteBuffer; +typedef struct DvrReadBuffer DvrReadBuffer; +typedef struct AHardwareBuffer AHardwareBuffer; + +typedef struct DvrReadBufferQueue DvrReadBufferQueue; +typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; + +typedef struct DvrSurface DvrSurface; +typedef uint64_t DvrSurfaceAttributeType; +typedef int32_t DvrSurfaceAttributeKey; + +typedef struct DvrSurfaceAttributeValue DvrSurfaceAttributeValue; +typedef struct DvrSurfaceAttribute DvrSurfaceAttribute; + +struct native_handle; + +// dvr_display_manager.h +typedef int (*DvrDisplayManagerCreatePtr)(DvrDisplayManager** client_out); +typedef void (*DvrDisplayManagerDestroyPtr)(DvrDisplayManager* client); +typedef int (*DvrDisplayManagerSetupNamedBufferPtr)(DvrDisplayManager* client, + const char* name, + size_t size, uint64_t usage, + DvrBuffer** buffer_out); +typedef int (*DvrDisplayManagerGetEventFdPtr)(DvrDisplayManager* client); +typedef int (*DvrDisplayManagerTranslateEpollEventMaskPtr)( + DvrDisplayManager* client, int in_events, int* out_events); +typedef int (*DvrDisplayManagerGetSurfaceStatePtr)( + DvrDisplayManager* client, DvrSurfaceState* surface_state); +typedef int (*DvrDisplayManagerGetReadBufferQueuePtr)( + DvrDisplayManager* client, int surface_id, int queue_id, + DvrReadBufferQueue** queue_out); +typedef int (*DvrSurfaceStateCreatePtr)(DvrSurfaceState** surface_state); +typedef void (*DvrSurfaceStateDestroyPtr)(DvrSurfaceState* surface_state); +typedef int (*DvrSurfaceStateGetSurfaceCountPtr)(DvrSurfaceState* surface_state, + size_t* count_out); +typedef int (*DvrSurfaceStateGetUpdateFlagsPtr)( + DvrSurfaceState* surface_state, size_t surface_index, + DvrSurfaceUpdateFlags* flags_out); +typedef int (*DvrSurfaceStateGetSurfaceIdPtr)(DvrSurfaceState* surface_state, + size_t surface_index, + int* surface_id_out); +typedef int (*DvrSurfaceStateGetProcessIdPtr)(DvrSurfaceState* surface_state, + size_t surface_index, + int* process_id_out); +typedef int (*DvrSurfaceStateGetQueueCountPtr)(DvrSurfaceState* surface_state, + size_t surface_index, + size_t* count_out); +typedef ssize_t (*DvrSurfaceStateGetQueueIdsPtr)(DvrSurfaceState* surface_state, + size_t surface_index, + int* queue_ids, + size_t max_count); +typedef int (*DvrSurfaceStateGetZOrderPtr)(DvrSurfaceState* surface_state, + size_t surface_index, + int* z_order_out); +typedef int (*DvrSurfaceStateGetVisiblePtr)(DvrSurfaceState* surface_state, + size_t surface_index, + bool* visible_out); +typedef int (*DvrSurfaceStateGetAttributeCountPtr)( + DvrSurfaceState* surface_state, size_t surface_index, size_t* count_out); +typedef ssize_t (*DvrSurfaceStateGetAttributesPtr)( + DvrSurfaceState* surface_state, size_t surface_index, + DvrSurfaceAttribute* attributes, size_t max_attribute_count); + +// dvr_buffer.h +typedef void (*DvrWriteBufferCreateEmptyPtr)(DvrWriteBuffer** write_buffer_out); +typedef void (*DvrWriteBufferDestroyPtr)(DvrWriteBuffer* write_buffer); +typedef int (*DvrWriteBufferIsValidPtr)(DvrWriteBuffer* write_buffer); +typedef int (*DvrWriteBufferClearPtr)(DvrWriteBuffer* write_buffer); +typedef int (*DvrWriteBufferGetIdPtr)(DvrWriteBuffer* write_buffer); +typedef int (*DvrWriteBufferGetAHardwareBufferPtr)( + DvrWriteBuffer* write_buffer, AHardwareBuffer** hardware_buffer); +typedef int (*DvrWriteBufferPostPtr)(DvrWriteBuffer* write_buffer, + int ready_fence_fd, const void* meta, + size_t meta_size_bytes); +typedef int (*DvrWriteBufferGainPtr)(DvrWriteBuffer* write_buffer, + int* release_fence_fd); +typedef int (*DvrWriteBufferGainAsyncPtr)(DvrWriteBuffer* write_buffer); +typedef const struct native_handle* (*DvrWriteBufferGetNativeHandlePtr)( + DvrWriteBuffer* write_buffer); + +typedef void (*DvrReadBufferCreateEmptyPtr)(DvrReadBuffer** read_buffer_out); +typedef void (*DvrReadBufferDestroyPtr)(DvrReadBuffer* read_buffer); +typedef int (*DvrReadBufferIsValidPtr)(DvrReadBuffer* read_buffer); +typedef int (*DvrReadBufferClearPtr)(DvrReadBuffer* read_buffer); +typedef int (*DvrReadBufferGetIdPtr)(DvrReadBuffer* read_buffer); +typedef int (*DvrReadBufferGetAHardwareBufferPtr)( + DvrReadBuffer* read_buffer, AHardwareBuffer** hardware_buffer); +typedef int (*DvrReadBufferAcquirePtr)(DvrReadBuffer* read_buffer, + int* ready_fence_fd, void* meta, + size_t meta_size_bytes); +typedef int (*DvrReadBufferReleasePtr)(DvrReadBuffer* read_buffer, + int release_fence_fd); +typedef int (*DvrReadBufferReleaseAsyncPtr)(DvrReadBuffer* read_buffer); +typedef const struct native_handle* (*DvrReadBufferGetNativeHandlePtr)( + DvrReadBuffer* read_buffer); + +typedef void (*DvrBufferDestroyPtr)(DvrBuffer* buffer); +typedef int (*DvrBufferGetAHardwareBufferPtr)( + DvrBuffer* buffer, AHardwareBuffer** hardware_buffer); +typedef const struct native_handle* (*DvrBufferGetNativeHandlePtr)( + DvrBuffer* buffer); + +// dvr_buffer_queue.h +typedef void (*DvrWriteBufferQueueDestroyPtr)(DvrWriteBufferQueue* write_queue); +typedef ssize_t (*DvrWriteBufferQueueGetCapacityPtr)( + DvrWriteBufferQueue* write_queue); +typedef int (*DvrWriteBufferQueueGetIdPtr)(DvrWriteBufferQueue* write_queue); +typedef int (*DvrWriteBufferQueueGetExternalSurfacePtr)( + DvrWriteBufferQueue* write_queue, ANativeWindow** out_window); +typedef int (*DvrWriteBufferQueueCreateReadQueuePtr)( + DvrWriteBufferQueue* write_queue, DvrReadBufferQueue** out_read_queue); +typedef int (*DvrWriteBufferQueueDequeuePtr)(DvrWriteBufferQueue* write_queue, + int timeout, + DvrWriteBuffer* out_buffer, + int* out_fence_fd); +typedef void (*DvrReadBufferQueueDestroyPtr)(DvrReadBufferQueue* read_queue); +typedef ssize_t (*DvrReadBufferQueueGetCapacityPtr)( + DvrReadBufferQueue* read_queue); +typedef int (*DvrReadBufferQueueGetIdPtr)(DvrReadBufferQueue* read_queue); +typedef int (*DvrReadBufferQueueCreateReadQueuePtr)( + DvrReadBufferQueue* read_queue, DvrReadBufferQueue** out_read_queue); +typedef int (*DvrReadBufferQueueDequeuePtr)(DvrReadBufferQueue* read_queue, + int timeout, + DvrReadBuffer* out_buffer, + int* out_fence_fd, void* out_meta, + size_t meta_size_bytes); + +// dvr_surface.h +typedef int (*DvrGetNamedBufferPtr)(const char* name, DvrBuffer** out_buffer); +typedef int (*DvrSurfaceCreatePtr)(const DvrSurfaceAttribute* attributes, + size_t attribute_count, + DvrSurface** surface_out); +typedef void (*DvrSurfaceDestroyPtr)(DvrSurface* surface); +typedef int (*DvrSurfaceGetIdPtr)(DvrSurface* surface); +typedef int (*DvrSurfaceSetAttributesPtr)(DvrSurface* surface, + const DvrSurfaceAttribute* attributes, + size_t attribute_count); +typedef int (*DvrSurfaceCreateWriteBufferQueuePtr)( + DvrSurface* surface, uint32_t width, uint32_t height, uint32_t format, + uint32_t layer_count, uint64_t usage, size_t capacity, + DvrWriteBufferQueue** queue_out); + +// vsync_client_api.h +typedef int (*DvrVSyncClientCreatePtr)(DvrVSyncClient** client_out); +typedef void (*DvrVSyncClientDestroyPtr)(DvrVSyncClient* client); +typedef int (*DvrVSyncClientGetSchedInfoPtr)(DvrVSyncClient* client, + int64_t* vsync_period_ns, + int64_t* next_timestamp_ns, + uint32_t* next_vsync_count); + +// pose_client.h +typedef DvrPose* (*DvrPoseCreatePtr)(void); +typedef void (*DvrPoseDestroyPtr)(DvrPose* client); +typedef int (*DvrPoseGetPtr)(DvrPose* client, uint32_t vsync_count, + DvrPoseAsync* out_pose); +typedef uint32_t (*DvrPoseGetVsyncCountPtr)(DvrPose* client); +typedef int (*DvrPoseGetControllerPtr)(DvrPose* client, int32_t controller_id, + uint32_t vsync_count, + DvrPoseAsync* out_pose); + +// virtual_touchpad_client.h +typedef DvrVirtualTouchpad* (*DvrVirtualTouchpadCreatePtr)(void); +typedef void (*DvrVirtualTouchpadDestroyPtr)(DvrVirtualTouchpad* client); +typedef int (*DvrVirtualTouchpadAttachPtr)(DvrVirtualTouchpad* client); +typedef int (*DvrVirtualTouchpadDetachPtr)(DvrVirtualTouchpad* client); +typedef int (*DvrVirtualTouchpadTouchPtr)(DvrVirtualTouchpad* client, + int touchpad, float x, float y, + float pressure); +typedef int (*DvrVirtualTouchpadButtonStatePtr)(DvrVirtualTouchpad* client, + int touchpad, int buttons); + +// dvr_hardware_composer_client.h +typedef struct DvrHwcClient DvrHwcClient; +typedef struct DvrHwcFrame DvrHwcFrame; +typedef int (*DvrHwcOnFrameCallback)(void* client_state, DvrHwcFrame* frame); +typedef DvrHwcClient* (*DvrHwcClientCreatePtr)(DvrHwcOnFrameCallback callback, + void* client_state); +typedef void (*DvrHwcClientDestroyPtr)(DvrHwcClient* client); +typedef void (*DvrHwcFrameDestroyPtr)(DvrHwcFrame* frame); +typedef DvrHwcDisplay (*DvrHwcFrameGetDisplayIdPtr)(DvrHwcFrame* frame); +typedef int32_t (*DvrHwcFrameGetDisplayWidthPtr)(DvrHwcFrame* frame); +typedef int32_t (*DvrHwcFrameGetDisplayHeightPtr)(DvrHwcFrame* frame); +typedef bool (*DvrHwcFrameGetDisplayRemovedPtr)(DvrHwcFrame* frame); +typedef size_t (*DvrHwcFrameGetLayerCountPtr)(DvrHwcFrame* frame); +typedef DvrHwcLayer (*DvrHwcFrameGetLayerIdPtr)(DvrHwcFrame* frame, + size_t layer_index); +typedef uint32_t (*DvrHwcFrameGetActiveConfigPtr)(DvrHwcFrame* frame); +typedef uint32_t (*DvrHwcFrameGetColorModePtr)(DvrHwcFrame* frame); +typedef void (*DvrHwcFrameGetColorTransformPtr)(DvrHwcFrame* frame, + float* out_matrix, + int32_t* out_hint); +typedef uint32_t (*DvrHwcFrameGetPowerModePtr)(DvrHwcFrame* frame); +typedef uint32_t (*DvrHwcFrameGetVsyncEnabledPtr)(DvrHwcFrame* frame); +typedef AHardwareBuffer* (*DvrHwcFrameGetLayerBufferPtr)(DvrHwcFrame* frame, + size_t layer_index); +typedef int (*DvrHwcFrameGetLayerFencePtr)(DvrHwcFrame* frame, + size_t layer_index); +typedef DvrHwcRecti (*DvrHwcFrameGetLayerDisplayFramePtr)(DvrHwcFrame* frame, + size_t layer_index); +typedef DvrHwcRectf (*DvrHwcFrameGetLayerCropPtr)(DvrHwcFrame* frame, + size_t layer_index); +typedef DvrHwcBlendMode (*DvrHwcFrameGetLayerBlendModePtr)(DvrHwcFrame* frame, + size_t layer_index); +typedef float (*DvrHwcFrameGetLayerAlphaPtr)(DvrHwcFrame* frame, + size_t layer_index); +typedef uint32_t (*DvrHwcFrameGetLayerTypePtr)(DvrHwcFrame* frame, + size_t layer_index); +typedef uint32_t (*DvrHwcFrameGetLayerApplicationIdPtr)(DvrHwcFrame* frame, + size_t layer_index); +typedef uint32_t (*DvrHwcFrameGetLayerZOrderPtr)(DvrHwcFrame* frame, + size_t layer_index); + +typedef void (*DvrHwcFrameGetLayerCursorPtr)(DvrHwcFrame* frame, + size_t layer_index, int32_t* out_x, + int32_t* out_y); + +typedef uint32_t (*DvrHwcFrameGetLayerTransformPtr)(DvrHwcFrame* frame, + size_t layer_index); + +typedef uint32_t (*DvrHwcFrameGetLayerDataspacePtr)(DvrHwcFrame* frame, + size_t layer_index); + +typedef uint32_t (*DvrHwcFrameGetLayerColorPtr)(DvrHwcFrame* frame, + size_t layer_index); + +typedef uint32_t (*DvrHwcFrameGetLayerNumVisibleRegionsPtr)(DvrHwcFrame* frame, + size_t layer_index); +typedef DvrHwcRecti (*DvrHwcFrameGetLayerVisibleRegionPtr)(DvrHwcFrame* frame, + size_t layer_index, + size_t index); + +typedef uint32_t (*DvrHwcFrameGetLayerNumDamagedRegionsPtr)(DvrHwcFrame* frame, + size_t layer_index); +typedef DvrHwcRecti (*DvrHwcFrameGetLayerDamagedRegionPtr)(DvrHwcFrame* frame, + size_t layer_index, + size_t index); + +// The buffer metadata that an Android Surface (a.k.a. ANativeWindow) +// will populate. A DvrWriteBufferQueue must be created with this metadata iff +// ANativeWindow access is needed. Please do not remove, modify, or reorder +// existing data members. If new fields need to be added, please take extra care +// to make sure that new data field is padded properly the size of the struct +// stays same. +struct DvrNativeBufferMetadata { + // Timestamp of the frame. + int64_t timestamp; + + // Whether the buffer is using auto timestamp. + int32_t is_auto_timestamp; + + // Must be one of the HAL_DATASPACE_XXX value defined in system/graphics.h + int32_t dataspace; + + // Crop extracted from an ACrop or android::Crop object. + int32_t crop_left; + int32_t crop_top; + int32_t crop_right; + int32_t crop_bottom; + + // Must be one of the NATIVE_WINDOW_SCALING_MODE_XXX value defined in + // system/window.h. + int32_t scaling_mode; + + // Must be one of the ANATIVEWINDOW_TRANSFORM_XXX value defined in + // android/native_window.h + int32_t transform; + + // Reserved bytes for so that the struct is forward compatible. + int32_t reserved[16]; +}; + +struct DvrApi_v1 { +// Defines an API entry for V1 (no version suffix). +#define DVR_V1_API_ENTRY(name) Dvr##name##Ptr name + +// Include file with API entries. +#include "dvr_api_entries.h" + +// Undefine macro definitions to play nice with Google3 style rules. +#undef DVR_V1_API_ENTRY +}; + +int dvrGetApi(void* api, size_t struct_size, int version); + +__END_DECLS + +#endif // ANDROID_DVR_API_H_ diff --git a/libs/vr/libdvr/include/dvr/dvr_api_entries.h b/libs/vr/libdvr/include/dvr/dvr_api_entries.h new file mode 100644 index 0000000000..09568fd9dc --- /dev/null +++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h @@ -0,0 +1,135 @@ +// dvr_api_entries.h +// +// Defines the DVR platform library API entries. +// +// Do not include this header directly. + +#ifndef DVR_V1_API_ENTRY +#error Do not include this header directly. +#endif + +// Display manager client +DVR_V1_API_ENTRY(DisplayManagerCreate); +DVR_V1_API_ENTRY(DisplayManagerDestroy); +DVR_V1_API_ENTRY(DisplayManagerSetupNamedBuffer); +DVR_V1_API_ENTRY(DisplayManagerGetEventFd); +DVR_V1_API_ENTRY(DisplayManagerTranslateEpollEventMask); +DVR_V1_API_ENTRY(DisplayManagerGetSurfaceState); +DVR_V1_API_ENTRY(DisplayManagerGetReadBufferQueue); +DVR_V1_API_ENTRY(SurfaceStateCreate); +DVR_V1_API_ENTRY(SurfaceStateDestroy); +DVR_V1_API_ENTRY(SurfaceStateGetSurfaceCount); +DVR_V1_API_ENTRY(SurfaceStateGetUpdateFlags); +DVR_V1_API_ENTRY(SurfaceStateGetSurfaceId); +DVR_V1_API_ENTRY(SurfaceStateGetProcessId); +DVR_V1_API_ENTRY(SurfaceStateGetQueueCount); +DVR_V1_API_ENTRY(SurfaceStateGetQueueIds); +DVR_V1_API_ENTRY(SurfaceStateGetZOrder); +DVR_V1_API_ENTRY(SurfaceStateGetVisible); +DVR_V1_API_ENTRY(SurfaceStateGetAttributeCount); +DVR_V1_API_ENTRY(SurfaceStateGetAttributes); + +// Write buffer +DVR_V1_API_ENTRY(WriteBufferCreateEmpty); +DVR_V1_API_ENTRY(WriteBufferDestroy); +DVR_V1_API_ENTRY(WriteBufferIsValid); +DVR_V1_API_ENTRY(WriteBufferClear); +DVR_V1_API_ENTRY(WriteBufferGetId); +DVR_V1_API_ENTRY(WriteBufferGetAHardwareBuffer); +DVR_V1_API_ENTRY(WriteBufferPost); +DVR_V1_API_ENTRY(WriteBufferGain); +DVR_V1_API_ENTRY(WriteBufferGainAsync); +DVR_V1_API_ENTRY(WriteBufferGetNativeHandle); + +// Read buffer +DVR_V1_API_ENTRY(ReadBufferCreateEmpty); +DVR_V1_API_ENTRY(ReadBufferDestroy); +DVR_V1_API_ENTRY(ReadBufferIsValid); +DVR_V1_API_ENTRY(ReadBufferClear); +DVR_V1_API_ENTRY(ReadBufferGetId); +DVR_V1_API_ENTRY(ReadBufferGetAHardwareBuffer); +DVR_V1_API_ENTRY(ReadBufferAcquire); +DVR_V1_API_ENTRY(ReadBufferRelease); +DVR_V1_API_ENTRY(ReadBufferReleaseAsync); +DVR_V1_API_ENTRY(ReadBufferGetNativeHandle); + +// Buffer +DVR_V1_API_ENTRY(BufferDestroy); +DVR_V1_API_ENTRY(BufferGetAHardwareBuffer); +DVR_V1_API_ENTRY(BufferGetNativeHandle); + +// Write buffer queue +DVR_V1_API_ENTRY(WriteBufferQueueDestroy); +DVR_V1_API_ENTRY(WriteBufferQueueGetCapacity); +DVR_V1_API_ENTRY(WriteBufferQueueGetId); +DVR_V1_API_ENTRY(WriteBufferQueueGetExternalSurface); +DVR_V1_API_ENTRY(WriteBufferQueueCreateReadQueue); +DVR_V1_API_ENTRY(WriteBufferQueueDequeue); + +// Read buffer queue +DVR_V1_API_ENTRY(ReadBufferQueueDestroy); +DVR_V1_API_ENTRY(ReadBufferQueueGetCapacity); +DVR_V1_API_ENTRY(ReadBufferQueueGetId); +DVR_V1_API_ENTRY(ReadBufferQueueCreateReadQueue); +DVR_V1_API_ENTRY(ReadBufferQueueDequeue); + +// V-Sync client +DVR_V1_API_ENTRY(VSyncClientCreate); +DVR_V1_API_ENTRY(VSyncClientDestroy); +DVR_V1_API_ENTRY(VSyncClientGetSchedInfo); + +// Display surface +DVR_V1_API_ENTRY(SurfaceCreate); +DVR_V1_API_ENTRY(SurfaceDestroy); +DVR_V1_API_ENTRY(SurfaceGetId); +DVR_V1_API_ENTRY(SurfaceSetAttributes); +DVR_V1_API_ENTRY(SurfaceCreateWriteBufferQueue); +DVR_V1_API_ENTRY(GetNamedBuffer); + +// Pose client +DVR_V1_API_ENTRY(PoseCreate); +DVR_V1_API_ENTRY(PoseDestroy); +DVR_V1_API_ENTRY(PoseGet); +DVR_V1_API_ENTRY(PoseGetVsyncCount); +DVR_V1_API_ENTRY(PoseGetController); + +// Virtual touchpad client +DVR_V1_API_ENTRY(VirtualTouchpadCreate); +DVR_V1_API_ENTRY(VirtualTouchpadDestroy); +DVR_V1_API_ENTRY(VirtualTouchpadAttach); +DVR_V1_API_ENTRY(VirtualTouchpadDetach); +DVR_V1_API_ENTRY(VirtualTouchpadTouch); +DVR_V1_API_ENTRY(VirtualTouchpadButtonState); + +// VR HWComposer client +DVR_V1_API_ENTRY(HwcClientCreate); +DVR_V1_API_ENTRY(HwcClientDestroy); +DVR_V1_API_ENTRY(HwcFrameDestroy); +DVR_V1_API_ENTRY(HwcFrameGetDisplayId); +DVR_V1_API_ENTRY(HwcFrameGetDisplayWidth); +DVR_V1_API_ENTRY(HwcFrameGetDisplayHeight); +DVR_V1_API_ENTRY(HwcFrameGetDisplayRemoved); +DVR_V1_API_ENTRY(HwcFrameGetActiveConfig); +DVR_V1_API_ENTRY(HwcFrameGetColorMode); +DVR_V1_API_ENTRY(HwcFrameGetColorTransform); +DVR_V1_API_ENTRY(HwcFrameGetPowerMode); +DVR_V1_API_ENTRY(HwcFrameGetVsyncEnabled); +DVR_V1_API_ENTRY(HwcFrameGetLayerCount); +DVR_V1_API_ENTRY(HwcFrameGetLayerId); +DVR_V1_API_ENTRY(HwcFrameGetLayerBuffer); +DVR_V1_API_ENTRY(HwcFrameGetLayerFence); +DVR_V1_API_ENTRY(HwcFrameGetLayerDisplayFrame); +DVR_V1_API_ENTRY(HwcFrameGetLayerCrop); +DVR_V1_API_ENTRY(HwcFrameGetLayerBlendMode); +DVR_V1_API_ENTRY(HwcFrameGetLayerAlpha); +DVR_V1_API_ENTRY(HwcFrameGetLayerType); +DVR_V1_API_ENTRY(HwcFrameGetLayerApplicationId); +DVR_V1_API_ENTRY(HwcFrameGetLayerZOrder); +DVR_V1_API_ENTRY(HwcFrameGetLayerCursor); +DVR_V1_API_ENTRY(HwcFrameGetLayerTransform); +DVR_V1_API_ENTRY(HwcFrameGetLayerDataspace); +DVR_V1_API_ENTRY(HwcFrameGetLayerColor); +DVR_V1_API_ENTRY(HwcFrameGetLayerNumVisibleRegions); +DVR_V1_API_ENTRY(HwcFrameGetLayerVisibleRegion); +DVR_V1_API_ENTRY(HwcFrameGetLayerNumDamagedRegions); +DVR_V1_API_ENTRY(HwcFrameGetLayerDamagedRegion); diff --git a/libs/vr/libdvr/include/dvr/dvr_buffer.h b/libs/vr/libdvr/include/dvr/dvr_buffer.h new file mode 100644 index 0000000000..af55698b74 --- /dev/null +++ b/libs/vr/libdvr/include/dvr/dvr_buffer.h @@ -0,0 +1,104 @@ +#ifndef ANDROID_DVR_BUFFER_H_ +#define ANDROID_DVR_BUFFER_H_ + +#include <stdbool.h> +#include <stdint.h> +#include <sys/cdefs.h> +#include <memory> + +__BEGIN_DECLS + +typedef struct DvrWriteBuffer DvrWriteBuffer; +typedef struct DvrReadBuffer DvrReadBuffer; +typedef struct DvrBuffer DvrBuffer; +typedef struct AHardwareBuffer AHardwareBuffer; +struct native_handle; + +// Creates an empty write buffer that may be filled with an acutal buffer by +// other functions. +void dvrWriteBufferCreateEmpty(DvrWriteBuffer** write_buffer); + +// Destroys the write buffer. +void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer); + +// Returns 1 if the given write buffer object contains a buffer, 0 otherwise. +int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer); + +// Clears the contents of the buffer object. After a call to this function +// dvrWriteBufferIsValid on the same buffer object returns 0. +int dvrWriteBufferClear(DvrWriteBuffer* write_buffer); + +// Returns the global BufferHub id of this buffer. +int dvrWriteBufferGetId(DvrWriteBuffer* write_buffer); + +// Returns an AHardwareBuffer for the underlying buffer. +// Caller must call AHardwareBuffer_release on hardware_buffer. +int dvrWriteBufferGetAHardwareBuffer(DvrWriteBuffer* write_buffer, + AHardwareBuffer** hardware_buffer); + +// Posts the buffer, notifying any connected read buffers. Takes ownership of +// |ready_fence_fd|. +int dvrWriteBufferPost(DvrWriteBuffer* write_buffer, int ready_fence_fd, + const void* meta, size_t meta_size_bytes); + +// Gains a buffer that has been released by all connected read buffers. +int dvrWriteBufferGain(DvrWriteBuffer* write_buffer, int* release_fence_fd); +int dvrWriteBufferGainAsync(DvrWriteBuffer* write_buffer); + +// TODO(eieio): Switch to return int and take an out parameter for the native +// handle. +const struct native_handle* dvrWriteBufferGetNativeHandle( + DvrWriteBuffer* write_buffer); + +// Creates an empty read buffer that may be filled with and actual buffer by +// other functions. +void dvrReadBufferCreateEmpty(DvrReadBuffer** read_buffer); + +// Destroys the read buffer. +void dvrReadBufferDestroy(DvrReadBuffer* read_buffer); + +// Returns 1 if the given write buffer object contains a buffer, 0 otherwise. +int dvrReadBufferIsValid(DvrReadBuffer* read_buffer); + +// Clears the contents of the buffer object. After a call to this function +// dvrReadBufferIsValid on the same buffer object returns 0. +int dvrReadBufferClear(DvrReadBuffer* read_buffer); + +// Returns the global BufferHub id of this buffer. +int dvrReadBufferGetId(DvrReadBuffer* read_buffer); + +// Returns an AHardwareBuffer for the underlying buffer. +// Caller must call AHardwareBuffer_release on hardware_buffer. +int dvrReadBufferGetAHardwareBuffer(DvrReadBuffer* read_buffer, + AHardwareBuffer** hardware_buffer); + +// Acquires the read buffer after it has been posted by the write buffer it is +// connected to. +int dvrReadBufferAcquire(DvrReadBuffer* read_buffer, int* ready_fence_fd, + void* meta, size_t meta_size_bytes); + +// Releases the read buffer, notifying the write buffer it is connected to. +// Takes ownership of |release_fence_fd|. +int dvrReadBufferRelease(DvrReadBuffer* read_buffer, int release_fence_fd); +int dvrReadBufferReleaseAsync(DvrReadBuffer* read_buffer); + +// TODO(eieio): Switch to return int and take an out parameter for the native +// handle. +const struct native_handle* dvrReadBufferGetNativeHandle( + DvrReadBuffer* read_buffer); + +// Destroys the buffer. +void dvrBufferDestroy(DvrBuffer* buffer); + +// Gets an AHardwareBuffer from the buffer. +// Caller must call AHardwareBuffer_release on hardware_buffer. +int dvrBufferGetAHardwareBuffer(DvrBuffer* buffer, + AHardwareBuffer** hardware_buffer); + +// TODO(eieio): Switch to return int and take an out parameter for the native +// handle. +const struct native_handle* dvrBufferGetNativeHandle(DvrBuffer* buffer); + +__END_DECLS + +#endif // ANDROID_DVR_BUFFER_H_ diff --git a/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h new file mode 100644 index 0000000000..dd669dc30f --- /dev/null +++ b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h @@ -0,0 +1,44 @@ +#ifndef ANDROID_DVR_BUFFER_QUEUE_H_ +#define ANDROID_DVR_BUFFER_QUEUE_H_ + +#include <sys/cdefs.h> + +#include <dvr/dvr_buffer.h> + +__BEGIN_DECLS + +typedef struct ANativeWindow ANativeWindow; + +typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; +typedef struct DvrReadBufferQueue DvrReadBufferQueue; + +// WriteBufferQueue +void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue); +ssize_t dvrWriteBufferQueueGetCapacity(DvrWriteBufferQueue* write_queue); +int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue); + +// Returns ANativeWindow. Can be casted to a Java Surface using +// ANativeWindow_toSurface NDK API. Note that this method does not acquire an +// additional reference to the ANativeWindow returned, don't call +// ANativeWindow_release on it. +int dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue, + ANativeWindow** out_window); + +int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue, + DvrReadBufferQueue** out_read_queue); +int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout, + DvrWriteBuffer* out_buffer, int* out_fence_fd); + +// ReadeBufferQueue +void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue); +ssize_t dvrReadBufferQueueGetCapacity(DvrReadBufferQueue* read_queue); +int dvrReadBufferQueueGetId(DvrReadBufferQueue* read_queue); +int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue, + DvrReadBufferQueue** out_read_queue); +int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout, + DvrReadBuffer* out_buffer, int* out_fence_fd, + void* out_meta, size_t meta_size_bytes); + +__END_DECLS + +#endif // ANDROID_DVR_BUFFER_QUEUE_H_ diff --git a/libs/vr/libdvr/include/dvr/dvr_deleter.h b/libs/vr/libdvr/include/dvr/dvr_deleter.h new file mode 100644 index 0000000000..943384f802 --- /dev/null +++ b/libs/vr/libdvr/include/dvr/dvr_deleter.h @@ -0,0 +1,86 @@ +#ifndef ANDROID_DVR_DELETER_H_ +#define ANDROID_DVR_DELETER_H_ + +#include <sys/cdefs.h> + +#include <memory> + +// Header-only C++ helper to delete opaque DVR objects. + +__BEGIN_DECLS + +// Use forward declarations to avoid dependency on other headers. +typedef struct DvrBuffer DvrBuffer; +typedef struct DvrReadBuffer DvrReadBuffer; +typedef struct DvrWriteBuffer DvrWriteBuffer; +typedef struct DvrReadBufferQueue DvrReadBufferQueue; +typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; +typedef struct DvrDisplayManager DvrDisplayManager; +typedef struct DvrSurfaceState DvrSurfaceState; +typedef struct DvrSurface DvrSurface; +typedef struct DvrHwcClient DvrHwcClient; +typedef struct DvrHwcFrame DvrHwcFrame; +typedef struct DvrVSyncClient DvrVSyncClient; + +void dvrBufferDestroy(DvrBuffer* buffer); +void dvrReadBufferDestroy(DvrReadBuffer* read_buffer); +void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer); +void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue); +void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue); +void dvrDisplayManagerDestroy(DvrDisplayManager* client); +void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state); +void dvrSurfaceDestroy(DvrSurface* surface); +void dvrHwcClientDestroy(DvrHwcClient* client); +void dvrHwcFrameDestroy(DvrHwcFrame* frame); +void dvrVSyncClientDestroy(DvrVSyncClient* client); + +__END_DECLS + +// Avoid errors if this header is included in C code. +#if defined(__cplusplus) + +namespace android { +namespace dvr { + +// Universal DVR object deleter. May be passed to smart pointer types to handle +// deletion of DVR API objects. +struct DvrObjectDeleter { + void operator()(DvrBuffer* p) { dvrBufferDestroy(p); } + void operator()(DvrReadBuffer* p) { dvrReadBufferDestroy(p); } + void operator()(DvrWriteBuffer* p) { dvrWriteBufferDestroy(p); } + void operator()(DvrReadBufferQueue* p) { dvrReadBufferQueueDestroy(p); } + void operator()(DvrWriteBufferQueue* p) { dvrWriteBufferQueueDestroy(p); } + void operator()(DvrDisplayManager* p) { dvrDisplayManagerDestroy(p); } + void operator()(DvrSurfaceState* p) { dvrSurfaceStateDestroy(p); } + void operator()(DvrSurface* p) { dvrSurfaceDestroy(p); } + void operator()(DvrHwcClient* p) { dvrHwcClientDestroy(p); } + void operator()(DvrHwcFrame* p) { dvrHwcFrameDestroy(p); } + void operator()(DvrVSyncClient* p) { dvrVSyncClientDestroy(p); } +}; + +// Helper to define unique pointers for DVR object types. +template <typename T> +using MakeUniqueDvrPointer = std::unique_ptr<T, DvrObjectDeleter>; + +// Unique pointer types for DVR objects. +using UniqueDvrBuffer = MakeUniqueDvrPointer<DvrBuffer>; +using UniqueDvrReadBuffer = MakeUniqueDvrPointer<DvrReadBuffer>; +using UniqueDvrWriteBuffer = MakeUniqueDvrPointer<DvrWriteBuffer>; +using UniqueDvrReadBufferQueue = MakeUniqueDvrPointer<DvrReadBufferQueue>; +using UniqueDvrWriteBufferQueue = MakeUniqueDvrPointer<DvrWriteBufferQueue>; +using UniqueDvrDisplayManager = MakeUniqueDvrPointer<DvrDisplayManager>; +using UniqueDvrSurfaceState = MakeUniqueDvrPointer<DvrSurfaceState>; +using UniqueDvrSurface = MakeUniqueDvrPointer<DvrSurface>; +using UniqueDvrHwcClient = MakeUniqueDvrPointer<DvrHwcClient>; +using UniqueDvrHwcFrame = MakeUniqueDvrPointer<DvrHwcFrame>; +using UniqueDvrVSyncClient = MakeUniqueDvrPointer<DvrVSyncClient>; + +// TODO(eieio): Add an adapter for std::shared_ptr that injects the deleter into +// the relevant constructors. + +} // namespace dvr +} // namespace android + +#endif // defined(__cplusplus) + +#endif // ANDROID_DVR_DELETER_H_ diff --git a/libs/vr/libdvr/include/dvr/dvr_display_manager.h b/libs/vr/libdvr/include/dvr/dvr_display_manager.h new file mode 100644 index 0000000000..d5aef8b18b --- /dev/null +++ b/libs/vr/libdvr/include/dvr/dvr_display_manager.h @@ -0,0 +1,140 @@ +#ifndef DVR_DISPLAY_MANAGER_CLIENT_H_ +#define DVR_DISPLAY_MANAGER_CLIENT_H_ + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <sys/cdefs.h> + +#include <dvr/dvr_display_types.h> +#include <dvr/dvr_surface.h> + +__BEGIN_DECLS + +typedef struct DvrBuffer DvrBuffer; +typedef struct DvrDisplayManager DvrDisplayManager; +typedef struct DvrSurfaceState DvrSurfaceState; +typedef struct DvrReadBufferQueue DvrReadBufferQueue; + +typedef uint64_t DvrSurfaceUpdateFlags; + +// Attempts to connect to the display manager service. +// @return 0 on success. Otherwise returns a negative error value. +int dvrDisplayManagerCreate(DvrDisplayManager** client_out); + +// Destroys the display manager client object. +void dvrDisplayManagerDestroy(DvrDisplayManager* client); + +// Sets up a named buffer for shared memory data transfer between display +// clients and the display manager. +// @return 0 on success. Otherwise returns a negative error value. +int dvrDisplayManagerSetupNamedBuffer(DvrDisplayManager* client, + const char* name, size_t size, + uint64_t usage, DvrBuffer** buffer_out); + +// Returns an fd used to signal when surface updates occur. Note that depending +// on the underlying transport, only a subset of the real event bits may be +// supported. Use dvrDisplayManagerClientTranslateEpollEventMask to get the real +// event flags. +// @return the fd on success. Otherwise returns a negative error value. +int dvrDisplayManagerGetEventFd(DvrDisplayManager* client); + +// @param in_events pass in the epoll revents that were initially returned by +// poll/epoll. +// @param on success, this value will be overwritten with the true poll/epoll +// values. +// @return 0 on success. Otherwise returns a negative error value. +int dvrDisplayManagerTranslateEpollEventMask(DvrDisplayManager* client, + int in_events, int* out_events); + +// Queries the display manager service for the current state of the display +// surfaces and stores the results in the given surface state object. +// @return 0 on success. Otherwise returns a negative error value. +int dvrDisplayManagerGetSurfaceState(DvrDisplayManager* client, + DvrSurfaceState* surface_state); + +// Gets a read buffer queue from the surface |surface_id| named |queue_id|. Each +// call returns a different read buffer queue connected to the same write buffer +// queue. Callers should cache these instead of requesting new ones when +// possible. +int dvrDisplayManagerGetReadBufferQueue(DvrDisplayManager* client, + int surface_id, int queue_id, + DvrReadBufferQueue** queue_out); + +// Creates a new surface state object. This object may be used to receive the +// results of a surface state query. More than one state object may be created +// to keep multiple snapshots, if desired. +// @return 0 on success. Otherwise returns a negative error value. +int dvrSurfaceStateCreate(DvrSurfaceState** surface_state); + +// Destorys the surface state object. +void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state); + +// Writes the number of surfaces described in the state object into |count_out|. +// @return 0 on success. Otherwise returns a negative error value. +int dvrSurfaceStateGetSurfaceCount(DvrSurfaceState* surface_state, + size_t* count_out); + +// Returns the update flags for the surface at |surface_index| in the state +// object. The flags may be used to determine what changes, if any, occured to +// the surface since the last state update. +// @return 0 on success. Otherwise returns a negative error value. +int dvrSurfaceStateGetUpdateFlags(DvrSurfaceState* surface_state, + size_t surface_index, + DvrSurfaceUpdateFlags* flags_out); + +// Returns the unique identifier of surface at |surface_index| in the state +// object. The identifier may be used to distinguish between surfaces. +// @return 0 on success. Otherwise returns a negative error value. +int dvrSurfaceStateGetSurfaceId(DvrSurfaceState* surface_state, + size_t surface_index, int* surface_id_out); + +// Returns the process id of surface at |surface_index| in the state object. +// @return 0 on success. Otherwise returns a negative error value. +int dvrSurfaceStateGetProcessId(DvrSurfaceState* surface_state, + size_t surface_index, int* process_id_out); + +// Writes the number of queues in the surface at |surface_index| in the state +// object into |count_out|. +// @return 0 on success. Otherwise returns a negative error value. +int dvrSurfaceStateGetQueueCount(DvrSurfaceState* surface_state, + size_t surface_index, size_t* count_out); + +// Returns up to |max_count| queue ids for the queues belonging to the surface +// at |surface_index| in the state object. +// @return The number of queue ids written on success. Otherwise returns a +// negative error value. +ssize_t dvrSurfaceStateGetQueueIds(DvrSurfaceState* surface_state, + size_t surface_index, int* queue_ids, + size_t max_count); + +// Writes the z-order of the surface at |surface_index| in surface state object +// into |z_order_out|. +// @return 0 on success. Otherwise returns a negative error value. +int dvrSurfaceStateGetZOrder(DvrSurfaceState* surface_state, + size_t surface_index, int* z_order_out); + +// Writes the visible state of the surface at |surface_index| in the surface +// state object into |visible_out|. +// @return 0 on success. Otherwise it returns a negative error value. +int dvrSurfaceStateGetVisible(DvrSurfaceState* surface_state, + size_t surface_index, bool* visible_out); + +// Writes the number of attributes on the surface at |surface_index| in the +// state object into |count_out|. +// @return 0 on success. Otherwise it returns a negative error value. +int dvrSurfaceStateGetAttributeCount(DvrSurfaceState* surface_state, + size_t surface_index, size_t* count_out); + +// Writes the list of attribute key/value pairs for the surface at +// |surface_index| in the surface state object into |attributes|. +// @return The number of attributes written on success. Otherwise returns a +// negative error value. +ssize_t dvrSurfaceStateGetAttributes(DvrSurfaceState* surface_state, + size_t surface_index, + DvrSurfaceAttribute* attributes, + size_t max_count); + +__END_DECLS + +#endif // DVR_DISPLAY_MANAGER_CLIENT_H_ diff --git a/libs/vr/libdvr/include/dvr/dvr_hardware_composer_client.h b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_client.h new file mode 100644 index 0000000000..7ee7f9ede7 --- /dev/null +++ b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_client.h @@ -0,0 +1,107 @@ +#ifndef ANDROID_DVR_HARDWARE_COMPOSER_CLIENT_H +#define ANDROID_DVR_HARDWARE_COMPOSER_CLIENT_H + +#include <dvr/dvr_hardware_composer_defs.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct AHardwareBuffer AHardwareBuffer; +typedef struct DvrHwcClient DvrHwcClient; +typedef struct DvrHwcFrame DvrHwcFrame; + +// Called when a new frame has arrived. +// +// @param client_state Pointer to client state passed in |dvrHwcCreateClient()|. +// @param frame New frame. Owned by the client. +// @return fence FD for the release of the last frame. +typedef int(*DvrHwcOnFrameCallback)(void* client_state, DvrHwcFrame* frame); + +// @param callback Called when a new frame is available. +// @param client_state Pointer to client state passed back in the callback. +DvrHwcClient* dvrHwcClientCreate(DvrHwcOnFrameCallback callback, + void* client_state); + +void dvrHwcClientDestroy(DvrHwcClient* client); + +// Called to free the frame information. +void dvrHwcFrameDestroy(DvrHwcFrame* frame); + +DvrHwcDisplay dvrHwcFrameGetDisplayId(DvrHwcFrame* frame); + +int32_t dvrHwcFrameGetDisplayWidth(DvrHwcFrame* frame); + +int32_t dvrHwcFrameGetDisplayHeight(DvrHwcFrame* frame); + +// @return True if the display has been removed. In this case the current frame +// does not contain any valid layers to display. It is a signal to clean up any +// display related state. +bool dvrHwcFrameGetDisplayRemoved(DvrHwcFrame* frame); + +// @return Number of layers in the frame. +size_t dvrHwcFrameGetLayerCount(DvrHwcFrame* frame); + +uint32_t dvrHwcFrameGetActiveConfig(DvrHwcFrame* frame); +uint32_t dvrHwcFrameGetColorMode(DvrHwcFrame* frame); +void dvrHwcFrameGetColorTransform(DvrHwcFrame* frame, float* out_matrix, + int32_t* out_hint); +uint32_t dvrHwcFrameGetPowerMode(DvrHwcFrame* frame); +uint32_t dvrHwcFrameGetVsyncEnabled(DvrHwcFrame* frame); + +DvrHwcLayer dvrHwcFrameGetLayerId(DvrHwcFrame* frame, size_t layer_index); + +// Return the graphic buffer associated with the layer at |layer_index| in +// |frame|. +// +// @return Graphic buffer. Caller owns the buffer and is responsible for freeing +// it. (see AHardwareBuffer_release()) +AHardwareBuffer* dvrHwcFrameGetLayerBuffer(DvrHwcFrame* frame, + size_t layer_index); + +// Returns the fence FD for the layer at index |layer_index| in |frame|. +// +// @return Fence FD. Caller owns the FD and is responsible for closing it. +int dvrHwcFrameGetLayerFence(DvrHwcFrame* frame, size_t layer_index); + +DvrHwcRecti dvrHwcFrameGetLayerDisplayFrame(DvrHwcFrame* frame, + size_t layer_index); + +DvrHwcRectf dvrHwcFrameGetLayerCrop(DvrHwcFrame* frame, size_t layer_index); + +DvrHwcBlendMode dvrHwcFrameGetLayerBlendMode(DvrHwcFrame* frame, + size_t layer_index); + +float dvrHwcFrameGetLayerAlpha(DvrHwcFrame* frame, size_t layer_index); + +uint32_t dvrHwcFrameGetLayerType(DvrHwcFrame* frame, size_t layer_index); + +uint32_t dvrHwcFrameGetLayerApplicationId(DvrHwcFrame* frame, + size_t layer_index); + +uint32_t dvrHwcFrameGetLayerZOrder(DvrHwcFrame* frame, size_t layer_index); + +void dvrHwcFrameGetLayerCursor(DvrHwcFrame* frame, size_t layer_index, + int32_t* out_x, int32_t* out_y); + +uint32_t dvrHwcFrameGetLayerTransform(DvrHwcFrame* frame, size_t layer_index); + +uint32_t dvrHwcFrameGetLayerDataspace(DvrHwcFrame* frame, size_t layer_index); + +uint32_t dvrHwcFrameGetLayerColor(DvrHwcFrame* frame, size_t layer_index); + +uint32_t dvrHwcFrameGetLayerNumVisibleRegions(DvrHwcFrame* frame, + size_t layer_index); +DvrHwcRecti dvrHwcFrameGetLayerVisibleRegion(DvrHwcFrame* frame, + size_t layer_index, size_t index); + +uint32_t dvrHwcFrameGetLayerNumDamagedRegions(DvrHwcFrame* frame, + size_t layer_index); +DvrHwcRecti dvrHwcFrameGetLayerDamagedRegion(DvrHwcFrame* frame, + size_t layer_index, size_t index); +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ANDROID_DVR_HARDWARE_COMPOSER_CLIENT_H diff --git a/libs/vr/libdvr/include/dvr/dvr_hardware_composer_defs.h b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_defs.h new file mode 100644 index 0000000000..36c30f9be5 --- /dev/null +++ b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_defs.h @@ -0,0 +1,50 @@ +#ifndef ANDROID_VR_HARDWARE_COMPOSER_DEFS_H +#define ANDROID_VR_HARDWARE_COMPOSER_DEFS_H + +#include <inttypes.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// NOTE: These definitions must match the ones in +// //hardware/libhardware/include/hardware/hwcomposer2.h. They are used by the +// client side which does not have access to hwc2 headers. +enum DvrHwcBlendMode { + DVR_HWC_BLEND_MODE_INVALID = 0, + DVR_HWC_BLEND_MODE_NONE = 1, + DVR_HWC_BLEND_MODE_PREMULTIPLIED = 2, + DVR_HWC_BLEND_MODE_COVERAGE = 3, +}; + +enum DvrHwcComposition { + DVR_HWC_COMPOSITION_INVALID = 0, + DVR_HWC_COMPOSITION_CLIENT = 1, + DVR_HWC_COMPOSITION_DEVICE = 2, + DVR_HWC_COMPOSITION_SOLID_COLOR = 3, + DVR_HWC_COMPOSITION_CURSOR = 4, + DVR_HWC_COMPOSITION_SIDEBAND = 5, +}; + +typedef uint64_t DvrHwcDisplay; +typedef uint64_t DvrHwcLayer; + +struct DvrHwcRecti { + int32_t left; + int32_t top; + int32_t right; + int32_t bottom; +}; + +struct DvrHwcRectf { + float left; + float top; + float right; + float bottom; +}; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ANDROID_DVR_HARDWARE_COMPOSER_DEFS_H diff --git a/libs/vr/libdvr/include/dvr/dvr_surface.h b/libs/vr/libdvr/include/dvr/dvr_surface.h new file mode 100644 index 0000000000..361488e671 --- /dev/null +++ b/libs/vr/libdvr/include/dvr/dvr_surface.h @@ -0,0 +1,91 @@ +#ifndef ANDROID_DVR_SURFACE_H_ +#define ANDROID_DVR_SURFACE_H_ + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <sys/cdefs.h> + +#include <dvr/dvr_buffer.h> +#include <dvr/dvr_buffer_queue.h> +#include <dvr/dvr_display_types.h> + +__BEGIN_DECLS + +typedef struct DvrBuffer DvrBuffer; +typedef struct DvrSurface DvrSurface; +typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; + +// Attribute types. The values are one-hot encoded to support singluar types or +// masks of supported types. +enum { + DVR_SURFACE_ATTRIBUTE_TYPE_NONE = 0, + DVR_SURFACE_ATTRIBUTE_TYPE_INT32 = (1 << 0), + DVR_SURFACE_ATTRIBUTE_TYPE_INT64 = (1 << 1), + DVR_SURFACE_ATTRIBUTE_TYPE_BOOL = (1 << 2), + DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT = (1 << 3), + DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2 = (1 << 4), + DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3 = (1 << 5), + DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4 = (1 << 6), + DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8 = (1 << 7), + DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16 = (1 << 8), +}; + +typedef uint64_t DvrSurfaceAttributeType; +typedef int32_t DvrSurfaceAttributeKey; + +typedef struct DvrSurfaceAttributeValue { + DvrSurfaceAttributeType type; + union { + int32_t int32_value; + int64_t int64_value; + bool bool_value; + float float_value; + float float2_value[2]; + float float3_value[3]; + float float4_value[4]; + float float8_value[8]; + float float16_value[16]; + }; +} DvrSurfaceAttributeValue; + +typedef struct DvrSurfaceAttribute { + DvrSurfaceAttributeKey key; + DvrSurfaceAttributeValue value; +} DvrSurfaceAttribute; + +// Creates a new display surface with the given attributes. +// @return 0 on success. Otherwise returns a negative error value. +int dvrSurfaceCreate(const DvrSurfaceAttribute* attributes, + size_t attribute_count, DvrSurface** surface_out); + +// Destroys the display surface. +void dvrSurfaceDestroy(DvrSurface* surface); + +// Gets the DisplayService global id for this surface. +int dvrSurfaceGetId(DvrSurface* surface); + +// Sets attributes on the given display surface. +// @return 0 on success. Otherwise returns a negative error value. +int dvrSurfaceSetAttributes(DvrSurface* surface, + const DvrSurfaceAttribute* attributes, + size_t attribute_count); + +// Creates a new write-side buffer queue on the given surface. Direct surfaces +// may only have one queue, the latest call replacing any prior queue. Replaced +// queues are still referenced and should be destryoed using the queue destroy +// API. +// @return 0 on success. Otherwise returns a negative error value. +int dvrSurfaceCreateWriteBufferQueue(DvrSurface* surface, uint32_t width, + uint32_t height, uint32_t format, + uint32_t layer_count, uint64_t usage, + size_t capacity, + DvrWriteBufferQueue** queue_out); + +// Get a named buffer from the display service. +// @return 0 on success. Otherwise returns a negative error value. +int dvrGetNamedBuffer(const char* name, DvrBuffer** out_buffer); + +__END_DECLS + +#endif // ANDROID_DVR_SURFACE_H_ diff --git a/libs/vr/libdvr/include/dvr/dvr_vsync.h b/libs/vr/libdvr/include/dvr/dvr_vsync.h new file mode 100644 index 0000000000..1eea3d9133 --- /dev/null +++ b/libs/vr/libdvr/include/dvr/dvr_vsync.h @@ -0,0 +1,26 @@ +#ifndef ANDROID_DVR_VSYNC_H_ +#define ANDROID_DVR_VSYNC_H_ + +#include <stdint.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +typedef struct DvrVSyncClient DvrVSyncClient; + +// Creates a new client to the system vsync service. +int dvrVSyncClientCreate(DvrVSyncClient** client_out); + +// Destroys the vsync client. +void dvrVSyncClientDestroy(DvrVSyncClient* client); + +// Get the estimated timestamp of the next GPU lens warp preemption event in/ +// ns. Also returns the corresponding vsync count that the next lens warp +// operation will target. +int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns, + int64_t* next_timestamp_ns, + uint32_t* next_vsync_count); + +__END_DECLS + +#endif // ANDROID_DVR_VSYNC_H_ diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp new file mode 100644 index 0000000000..ef746e207a --- /dev/null +++ b/libs/vr/libdvr/tests/Android.bp @@ -0,0 +1,53 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +shared_libraries = [ + "libbase", + "libbinder", + "libcutils", + "libgui", + "liblog", + "libhardware", + "libui", + "libutils", + "libnativewindow", +] + +static_libraries = [ + "libdvr_static", + "libbufferhubqueue", + "libbufferhub", + "libchrome", + "libdvrcommon", + "libdisplay", + "libpdx_default_transport", +] + +cc_test { + srcs: [ + "dvr_buffer_queue-test.cpp", + "dvr_display_manager-test.cpp", + "dvr_named_buffer-test.cpp", + ], + + static_libs: static_libraries, + shared_libs: shared_libraries, + cflags: [ + "-DLOG_TAG=\"dvr_api-test\"", + "-DTRACE=0", + "-O0", + "-g", + ], + name: "dvr_api-test", +} diff --git a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp new file mode 100644 index 0000000000..474e9684c2 --- /dev/null +++ b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp @@ -0,0 +1,217 @@ +#include <dvr/dvr_api.h> +#include <dvr/dvr_buffer_queue.h> +#include <gui/Surface.h> +#include <private/dvr/buffer_hub_queue_client.h> + +#include <base/logging.h> +#include <gtest/gtest.h> + +#include "../dvr_internal.h" + +namespace android { +namespace dvr { + +namespace { + +static constexpr int kBufferWidth = 100; +static constexpr int kBufferHeight = 1; +static constexpr int kLayerCount = 1; +static constexpr int kBufferFormat = HAL_PIXEL_FORMAT_BLOB; +static constexpr int kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY; +static constexpr size_t kQueueCapacity = 3; + +typedef uint64_t TestMeta; + +class DvrBufferQueueTest : public ::testing::Test { + protected: + void SetUp() override { + write_queue_ = CreateDvrWriteBufferQueueFromProducerQueue( + ProducerQueue::Create<TestMeta>(0, 0, 0, 0)); + ASSERT_NE(nullptr, write_queue_); + } + + void TearDown() override { + if (write_queue_ != nullptr) { + dvrWriteBufferQueueDestroy(write_queue_); + write_queue_ = nullptr; + } + } + + void AllocateBuffers(size_t buffer_count) { + size_t out_slot; + for (size_t i = 0; i < buffer_count; i++) { + int ret = GetProducerQueueFromDvrWriteBufferQueue(write_queue_) + ->AllocateBuffer(kBufferWidth, kBufferHeight, kLayerCount, + kBufferFormat, kBufferUsage, &out_slot); + ASSERT_EQ(0, ret); + } + } + + DvrWriteBufferQueue* write_queue_{nullptr}; +}; + +TEST_F(DvrBufferQueueTest, TestWrite_QueueDestroy) { + dvrWriteBufferQueueDestroy(write_queue_); + write_queue_ = nullptr; +} + +TEST_F(DvrBufferQueueTest, TestWrite_QueueGetCapacity) { + AllocateBuffers(kQueueCapacity); + size_t capacity = dvrWriteBufferQueueGetCapacity(write_queue_); + + ALOGD_IF(TRACE, "TestWrite_QueueGetCapacity, capacity=%zu", capacity); + ASSERT_EQ(kQueueCapacity, capacity); +} + +TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromWriteQueue) { + DvrReadBufferQueue* read_queue = nullptr; + int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); + + ASSERT_EQ(0, ret); + ASSERT_NE(nullptr, read_queue); + + dvrReadBufferQueueDestroy(read_queue); +} + +TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromReadQueue) { + DvrReadBufferQueue* read_queue1 = nullptr; + DvrReadBufferQueue* read_queue2 = nullptr; + int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue1); + + ASSERT_EQ(0, ret); + ASSERT_NE(nullptr, read_queue1); + + ret = dvrReadBufferQueueCreateReadQueue(read_queue1, &read_queue2); + ASSERT_EQ(0, ret); + ASSERT_NE(nullptr, read_queue2); + ASSERT_NE(read_queue1, read_queue2); + + dvrReadBufferQueueDestroy(read_queue1); + dvrReadBufferQueueDestroy(read_queue2); +} + +TEST_F(DvrBufferQueueTest, CreateEmptyBuffer) { + AllocateBuffers(3); + + DvrReadBuffer* read_buffer = nullptr; + DvrWriteBuffer* write_buffer = nullptr; + + EXPECT_FALSE(dvrReadBufferIsValid(read_buffer)); + EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer)); + + dvrReadBufferCreateEmpty(&read_buffer); + ASSERT_NE(nullptr, read_buffer); + + dvrWriteBufferCreateEmpty(&write_buffer); + ASSERT_NE(nullptr, write_buffer); + + EXPECT_FALSE(dvrReadBufferIsValid(read_buffer)); + EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer)); + + DvrReadBufferQueue* read_queue = nullptr; + + ASSERT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue)); + + const int kTimeoutMs = 0; + int fence_fd = -1; + ASSERT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, kTimeoutMs, + write_buffer, &fence_fd)); + EXPECT_EQ(-1, fence_fd); + EXPECT_TRUE(dvrWriteBufferIsValid(write_buffer)); + + ASSERT_EQ(0, dvrWriteBufferClear(write_buffer)); + EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer)); +} + +TEST_F(DvrBufferQueueTest, TestDequeuePostDequeueRelease) { + static constexpr int kTimeout = 0; + DvrReadBufferQueue* read_queue = nullptr; + DvrReadBuffer* rb = nullptr; + DvrWriteBuffer* wb = nullptr; + int fence_fd = -1; + + int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); + + ASSERT_EQ(0, ret); + ASSERT_NE(nullptr, read_queue); + + dvrWriteBufferCreateEmpty(&wb); + ASSERT_NE(nullptr, wb); + + dvrReadBufferCreateEmpty(&rb); + ASSERT_NE(nullptr, rb); + + AllocateBuffers(kQueueCapacity); + + // Gain buffer for writing. + ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb, &fence_fd); + ASSERT_EQ(0, ret); + ASSERT_TRUE(dvrWriteBufferIsValid(wb)); + ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, gain buffer %p, fence_fd=%d", + wb, fence_fd); + pdx::LocalHandle release_fence(fence_fd); + + // Post buffer to the read_queue. + TestMeta seq = 42U; + ret = dvrWriteBufferPost(wb, /* fence */ -1, &seq, sizeof(seq)); + ASSERT_EQ(0, ret); + dvrWriteBufferDestroy(wb); + wb = nullptr; + + // Acquire buffer for reading. + TestMeta acquired_seq = 0U; + ret = dvrReadBufferQueueDequeue(read_queue, kTimeout, rb, &fence_fd, + &acquired_seq, sizeof(acquired_seq)); + ASSERT_EQ(0, ret); + ASSERT_TRUE(dvrReadBufferIsValid(rb)); + ASSERT_EQ(seq, acquired_seq); + ALOGD_IF(TRACE, + "TestDequeuePostDequeueRelease, acquire buffer %p, fence_fd=%d", rb, + fence_fd); + pdx::LocalHandle acquire_fence(fence_fd); + + // Release buffer to the write_queue. + ret = dvrReadBufferRelease(rb, -1); + ASSERT_EQ(0, ret); + dvrReadBufferDestroy(rb); + rb = nullptr; + + // TODO(b/34387835) Currently buffer allocation has to happen after all queues + // are initialized. + size_t capacity = dvrReadBufferQueueGetCapacity(read_queue); + + ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, capacity=%zu", capacity); + ASSERT_EQ(kQueueCapacity, capacity); + + dvrReadBufferQueueDestroy(read_queue); +} + +TEST_F(DvrBufferQueueTest, TestGetExternalSurface) { + ANativeWindow* window = nullptr; + + // The |write_queue_| doesn't have proper metadata (must be + // DvrNativeBufferMetadata) configured during creation. + int ret = dvrWriteBufferQueueGetExternalSurface(write_queue_, &window); + ASSERT_EQ(-EINVAL, ret); + ASSERT_EQ(nullptr, window); + + // A write queue with DvrNativeBufferMetadata should work fine. + std::unique_ptr<DvrWriteBufferQueue, decltype(&dvrWriteBufferQueueDestroy)> + write_queue( + CreateDvrWriteBufferQueueFromProducerQueue( + ProducerQueue::Create<DvrNativeBufferMetadata>(0, 0, 0, 0)), + dvrWriteBufferQueueDestroy); + ASSERT_NE(nullptr, write_queue.get()); + + ret = dvrWriteBufferQueueGetExternalSurface(write_queue.get(), &window); + ASSERT_EQ(0, ret); + ASSERT_NE(nullptr, window); + + sp<Surface> surface = static_cast<Surface*>(window); + ASSERT_TRUE(Surface::isValid(surface)); +} + +} // namespace + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp new file mode 100644 index 0000000000..a2414d65d8 --- /dev/null +++ b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp @@ -0,0 +1,579 @@ +#include <base/logging.h> +#include <gtest/gtest.h> +#include <poll.h> + +#include <android/hardware_buffer.h> + +#include <algorithm> +#include <set> +#include <thread> +#include <vector> + +#include <dvr/dvr_deleter.h> +#include <dvr/dvr_display_manager.h> +#include <dvr/dvr_surface.h> + +#include <pdx/status.h> + +using android::pdx::ErrorStatus; +using android::pdx::Status; + +namespace android { +namespace dvr { + +namespace { + +DvrSurfaceAttribute GetAttribute(DvrSurfaceAttributeKey key, bool value) { + DvrSurfaceAttribute attribute; + attribute.key = key; + attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL; + attribute.value.bool_value = value; + return attribute; +} + +DvrSurfaceAttribute GetAttribute(DvrSurfaceAttributeKey key, int32_t value) { + DvrSurfaceAttribute attribute; + attribute.key = key; + attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32; + attribute.value.bool_value = value; + return attribute; +} + +Status<UniqueDvrSurface> CreateApplicationSurface(bool visible = false, + int32_t z_order = 0) { + DvrSurface* surface = nullptr; + DvrSurfaceAttribute attributes[] = { + GetAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, z_order), + GetAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, visible)}; + + const int ret = dvrSurfaceCreate( + attributes, std::extent<decltype(attributes)>::value, &surface); + if (ret < 0) + return ErrorStatus(-ret); + else + return {UniqueDvrSurface(surface)}; +} + +Status<UniqueDvrWriteBufferQueue> CreateSurfaceQueue( + const UniqueDvrSurface& surface, uint32_t width, uint32_t height, + uint32_t format, uint32_t layer_count, uint64_t usage, size_t capacity) { + DvrWriteBufferQueue* queue; + const int ret = + dvrSurfaceCreateWriteBufferQueue(surface.get(), width, height, format, + layer_count, usage, capacity, &queue); + if (ret < 0) + return ErrorStatus(-ret); + else + return {UniqueDvrWriteBufferQueue(queue)}; +} + +class TestDisplayManager { + public: + TestDisplayManager(UniqueDvrDisplayManager display_manager, + UniqueDvrSurfaceState surface_state) + : display_manager_(std::move(display_manager)), + surface_state_(std::move(surface_state)) { + const int fd = dvrDisplayManagerGetEventFd(display_manager_.get()); + LOG_IF(INFO, fd < 0) << "Failed to get event fd: " << strerror(-fd); + display_manager_event_fd_ = fd; + } + + Status<UniqueDvrReadBufferQueue> GetReadBufferQueue(int surface_id, + int queue_id) { + DvrReadBufferQueue* queue; + const int ret = dvrDisplayManagerGetReadBufferQueue( + display_manager_.get(), surface_id, queue_id, &queue); + if (ret < 0) + return ErrorStatus(-ret); + else + return {UniqueDvrReadBufferQueue(queue)}; + } + + Status<void> UpdateSurfaceState() { + const int ret = dvrDisplayManagerGetSurfaceState(display_manager_.get(), + surface_state_.get()); + if (ret < 0) + return ErrorStatus(-ret); + else + return {}; + } + + Status<void> WaitForUpdate() { + if (display_manager_event_fd_ < 0) + return ErrorStatus(-display_manager_event_fd_); + + const int kTimeoutMs = 10000; // 10s + pollfd pfd = {display_manager_event_fd_, POLLIN, 0}; + const int count = poll(&pfd, 1, kTimeoutMs); + if (count < 0) + return ErrorStatus(errno); + else if (count == 0) + return ErrorStatus(ETIMEDOUT); + + int events; + const int ret = dvrDisplayManagerTranslateEpollEventMask( + display_manager_.get(), pfd.revents, &events); + if (ret < 0) + return ErrorStatus(-ret); + else if (events & POLLIN) + return UpdateSurfaceState(); + else + return ErrorStatus(EPROTO); + } + + Status<size_t> GetSurfaceCount() { + size_t count = 0; + const int ret = + dvrSurfaceStateGetSurfaceCount(surface_state_.get(), &count); + if (ret < 0) + return ErrorStatus(-ret); + else + return {count}; + } + + Status<DvrSurfaceUpdateFlags> GetUpdateFlags(size_t surface_index) { + DvrSurfaceUpdateFlags update_flags; + const int ret = dvrSurfaceStateGetUpdateFlags(surface_state_.get(), + surface_index, &update_flags); + if (ret < 0) + return ErrorStatus(-ret); + else + return {update_flags}; + } + + Status<int> GetSurfaceId(size_t surface_index) { + int surface_id; + const int ret = dvrSurfaceStateGetSurfaceId(surface_state_.get(), + surface_index, &surface_id); + if (ret < 0) + return ErrorStatus(-ret); + else + return {surface_id}; + } + + Status<int> GetProcessId(size_t surface_index) { + int process_id; + const int ret = dvrSurfaceStateGetProcessId(surface_state_.get(), + surface_index, &process_id); + if (ret < 0) + return ErrorStatus(-ret); + else + return {process_id}; + } + + Status<std::vector<DvrSurfaceAttribute>> GetAttributes(size_t surface_index) { + std::vector<DvrSurfaceAttribute> attributes; + size_t count = 0; + const int ret = dvrSurfaceStateGetAttributeCount(surface_state_.get(), + surface_index, &count); + if (ret < 0) + return ErrorStatus(-ret); + + attributes.resize(count); + const ssize_t return_count = dvrSurfaceStateGetAttributes( + surface_state_.get(), surface_index, attributes.data(), count); + if (return_count < 0) + return ErrorStatus(-return_count); + + attributes.resize(return_count); + return {std::move(attributes)}; + } + + Status<std::vector<int>> GetQueueIds(size_t surface_index) { + std::vector<int> queue_ids; + size_t count = 0; + const int ret = dvrSurfaceStateGetQueueCount(surface_state_.get(), + surface_index, &count); + if (ret < 0) + return ErrorStatus(-ret); + + if (count > 0) { + queue_ids.resize(count); + const ssize_t return_count = dvrSurfaceStateGetQueueIds( + surface_state_.get(), surface_index, queue_ids.data(), count); + if (return_count < 0) + return ErrorStatus(-return_count); + + queue_ids.resize(return_count); + } + + return {std::move(queue_ids)}; + } + + private: + UniqueDvrDisplayManager display_manager_; + UniqueDvrSurfaceState surface_state_; + + // Owned by object in display_manager_, do not explicitly close. + int display_manager_event_fd_; + + TestDisplayManager(const TestDisplayManager&) = delete; + void operator=(const TestDisplayManager&) = delete; +}; + +class DvrDisplayManagerTest : public ::testing::Test { + protected: + void SetUp() override { + int ret; + DvrDisplayManager* display_manager; + DvrSurfaceState* surface_state; + + ret = dvrDisplayManagerCreate(&display_manager); + ASSERT_EQ(0, ret) << "Failed to create display manager client"; + ASSERT_NE(nullptr, display_manager); + + ret = dvrSurfaceStateCreate(&surface_state); + ASSERT_EQ(0, ret) << "Failed to create surface state object"; + ASSERT_NE(nullptr, surface_state); + + manager_.reset( + new TestDisplayManager(UniqueDvrDisplayManager(display_manager), + UniqueDvrSurfaceState(surface_state))); + } + void TearDown() override {} + + std::unique_ptr<TestDisplayManager> manager_; +}; + +// TODO(eieio): Consider moving these somewhere more central because they are +// broadly useful. + +template <typename T> +testing::AssertionResult StatusOk(const char* status_expression, + const Status<T>& status) { + if (!status.ok()) { + return testing::AssertionFailure() + << "(" << status_expression + << ") expected to indicate success but actually contains error (" + << status.error() << ")"; + } else { + return testing::AssertionSuccess(); + } +} + +template <typename T> +testing::AssertionResult StatusError(const char* status_expression, + const Status<T>& status) { + if (status.ok()) { + return testing::AssertionFailure() + << "(" << status_expression + << ") expected to indicate error but instead indicates success."; + } else { + return testing::AssertionSuccess(); + } +} + +template <typename T> +testing::AssertionResult StatusHasError(const char* status_expression, + const char* /*error_code_expression*/, + const Status<T>& status, + int error_code) { + if (status.ok()) { + return StatusError(status_expression, status); + } else if (status.error() != error_code) { + return testing::AssertionFailure() + << "(" << status_expression << ") expected to indicate error (" + << error_code << ") but actually indicates error (" << status.error() + << ")"; + } else { + return testing::AssertionSuccess(); + } +} + +template <typename T, typename U> +testing::AssertionResult StatusHasValue(const char* status_expression, + const char* /*value_expression*/, + const Status<T>& status, + const U& value) { + if (!status.ok()) { + return StatusOk(status_expression, status); + } else if (status.get() != value) { + return testing::AssertionFailure() + << "(" << status_expression << ") expected to contain value (" + << testing::PrintToString(value) << ") but actually contains value (" + << testing::PrintToString(status.get()) << ")"; + } else { + return testing::AssertionSuccess(); + } +} + +template <typename T, typename Op> +testing::AssertionResult StatusPred(const char* status_expression, + const char* pred_expression, + const Status<T>& status, Op pred) { + if (!status.ok()) { + return StatusOk(status_expression, status); + } else if (!pred(status.get())) { + return testing::AssertionFailure() + << status_expression << " value (" + << testing::PrintToString(status.get()) + << ") failed to pass predicate " << pred_expression; + } else { + return testing::AssertionSuccess(); + } +} + +#define ASSERT_STATUS_OK(status) ASSERT_PRED_FORMAT1(StatusOk, status) +#define ASSERT_STATUS_ERROR(status) ASSERT_PRED_FORMAT1(StatusError, status) + +#define ASSERT_STATUS_ERROR_VALUE(value, status) \ + ASSERT_PRED_FORMAT2(StatusHasError, status, value) + +#define ASSERT_STATUS_EQ(value, status) \ + ASSERT_PRED_FORMAT2(StatusHasValue, status, value) + +#define EXPECT_STATUS_OK(status) EXPECT_PRED_FORMAT1(StatusOk, status) +#define EXPECT_STATUS_ERROR(status) EXPECT_PRED_FORMAT1(StatusError, status) + +#define EXPECT_STATUS_ERROR_VALUE(value, status) \ + EXPECT_PRED_FORMAT2(StatusHasError, status, value) + +#define EXPECT_STATUS_EQ(value, status) \ + EXPECT_PRED_FORMAT2(StatusHasValue, status, value) + +#define EXPECT_STATUS_PRED(pred, status) \ + EXPECT_PRED_FORMAT2(StatusPred, status, pred) + +#if 0 +// Verify utility predicate/macro functionality. This section is commented out +// because it is designed to fail in some cases to validate the helpers. +TEST_F(DvrDisplayManagerTest, ExpectVoid) { + Status<void> status_error{ErrorStatus{EINVAL}}; + Status<void> status_ok{}; + + EXPECT_STATUS_ERROR(status_error); + EXPECT_STATUS_ERROR(status_ok); + EXPECT_STATUS_OK(status_error); + EXPECT_STATUS_OK(status_ok); + + EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error); + EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error); + EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok); + EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok); +} + +TEST_F(DvrDisplayManagerTest, ExpectInt) { + Status<int> status_error{ErrorStatus{EINVAL}}; + Status<int> status_ok{10}; + + EXPECT_STATUS_ERROR(status_error); + EXPECT_STATUS_ERROR(status_ok); + EXPECT_STATUS_OK(status_error); + EXPECT_STATUS_OK(status_ok); + + EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error); + EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error); + EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok); + EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok); + + EXPECT_STATUS_EQ(10, status_error); + EXPECT_STATUS_EQ(20, status_error); + EXPECT_STATUS_EQ(10, status_ok); + EXPECT_STATUS_EQ(20, status_ok); + + auto pred1 = [](const auto& value) { return value < 15; }; + auto pred2 = [](const auto& value) { return value > 5; }; + auto pred3 = [](const auto& value) { return value > 15; }; + auto pred4 = [](const auto& value) { return value < 5; }; + + EXPECT_STATUS_PRED(pred1, status_error); + EXPECT_STATUS_PRED(pred2, status_error); + EXPECT_STATUS_PRED(pred3, status_error); + EXPECT_STATUS_PRED(pred4, status_error); + EXPECT_STATUS_PRED(pred1, status_ok); + EXPECT_STATUS_PRED(pred2, status_ok); + EXPECT_STATUS_PRED(pred3, status_ok); + EXPECT_STATUS_PRED(pred4, status_ok); +} +#endif + +TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) { + // Get surface state and verify there are no surfaces. + ASSERT_STATUS_OK(manager_->UpdateSurfaceState()); + ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount()); + + // Get flags for invalid surface index. + EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetUpdateFlags(0)); + + // Create an application surface. + auto surface_status = CreateApplicationSurface(); + ASSERT_STATUS_OK(surface_status); + UniqueDvrSurface surface = surface_status.take(); + ASSERT_NE(nullptr, surface.get()); + + const int surface_id = dvrSurfaceGetId(surface.get()); + ASSERT_GE(surface_id, 0); + + // Now there should be one new surface. + ASSERT_STATUS_OK(manager_->WaitForUpdate()); + EXPECT_STATUS_EQ(1u, manager_->GetSurfaceCount()); + + // Verify the new surface flag is set. + auto check_flags = [](const auto& value) { + return value & DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE; + }; + EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0)); + + // Verify the surface id matches. + EXPECT_STATUS_EQ(surface_id, manager_->GetSurfaceId(0)); + + // Verify the owning process of the surface. + EXPECT_STATUS_EQ(getpid(), manager_->GetProcessId(0)); + + surface.reset(); + + ASSERT_STATUS_OK(manager_->WaitForUpdate()); + EXPECT_STATUS_EQ(0u, manager_->GetSurfaceCount()); +} + +TEST_F(DvrDisplayManagerTest, SurfaceAttributeEvent) { + // Get surface state and verify there are no surfaces. + ASSERT_STATUS_OK(manager_->UpdateSurfaceState()); + ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount()); + + // Get attributes for an invalid surface index. + EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetAttributes(0)); + + const bool kInitialVisibility = true; + const int32_t kInitialZOrder = 10; + auto surface_status = + CreateApplicationSurface(kInitialVisibility, kInitialZOrder); + ASSERT_STATUS_OK(surface_status); + auto surface = surface_status.take(); + ASSERT_NE(nullptr, surface.get()); + + ASSERT_STATUS_OK(manager_->WaitForUpdate()); + ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount()); + + // Check the initial attribute values. + auto attribute_status = manager_->GetAttributes(0); + ASSERT_STATUS_OK(attribute_status); + auto attributes = attribute_status.take(); + EXPECT_GE(attributes.size(), 2u); + + const std::set<int32_t> expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, + DVR_SURFACE_ATTRIBUTE_VISIBLE}; + + // Collect all the keys in attributes that match the expected keys. + std::set<int32_t> actual_keys; + std::for_each(attributes.begin(), attributes.end(), + [&expected_keys, &actual_keys](const auto& attribute) { + if (expected_keys.find(attribute.key) != expected_keys.end()) + actual_keys.emplace(attribute.key); + }); + + // If the sets match then attributes contained at least the expected keys, + // even if other keys were also present. + EXPECT_EQ(expected_keys, actual_keys); +} + +TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) { + // Create an application surface. + auto surface_status = CreateApplicationSurface(); + ASSERT_STATUS_OK(surface_status); + UniqueDvrSurface surface = surface_status.take(); + ASSERT_NE(nullptr, surface.get()); + + const int surface_id = dvrSurfaceGetId(surface.get()); + ASSERT_GE(surface_id, 0); + // Get surface state and verify there is one surface. + ASSERT_STATUS_OK(manager_->WaitForUpdate()); + ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount()); + + // Verify there are no queues for the surface recorded in the state snapshot. + EXPECT_STATUS_EQ(std::vector<int>{}, manager_->GetQueueIds(0)); + + // Create a new queue in the surface. + auto write_queue_status = CreateSurfaceQueue( + surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, 1, + AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1); + ASSERT_STATUS_OK(write_queue_status); + UniqueDvrWriteBufferQueue write_queue = write_queue_status.take(); + ASSERT_NE(nullptr, write_queue.get()); + + const int queue_id = dvrWriteBufferQueueGetId(write_queue.get()); + ASSERT_GE(queue_id, 0); + + // Update surface state. + ASSERT_STATUS_OK(manager_->WaitForUpdate()); + ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount()); + + // Verify the buffers changed flag is set. + auto check_flags = [](const auto& value) { + return value & DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED; + }; + EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0)); + + auto queue_ids_status = manager_->GetQueueIds(0); + ASSERT_STATUS_OK(queue_ids_status); + + auto queue_ids = queue_ids_status.take(); + ASSERT_EQ(1u, queue_ids.size()); + EXPECT_EQ(queue_id, queue_ids[0]); + + auto read_queue_status = manager_->GetReadBufferQueue(surface_id, queue_id); + ASSERT_STATUS_OK(read_queue_status); + UniqueDvrReadBufferQueue read_queue = read_queue_status.take(); + ASSERT_NE(nullptr, read_queue.get()); + EXPECT_EQ(queue_id, dvrReadBufferQueueGetId(read_queue.get())); + + write_queue.reset(); + + // Verify that destroying the queue generates a surface update event. + ASSERT_STATUS_OK(manager_->WaitForUpdate()); + ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount()); + + // Verify that the buffers changed flag is set. + EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0)); + + // Verify that the queue ids reflect the change. + queue_ids_status = manager_->GetQueueIds(0); + ASSERT_STATUS_OK(queue_ids_status); + + queue_ids = queue_ids_status.take(); + ASSERT_EQ(0u, queue_ids.size()); +} + +TEST_F(DvrDisplayManagerTest, MultiLayerBufferQueue) { + // Create an application surface. + auto surface_status = CreateApplicationSurface(); + ASSERT_STATUS_OK(surface_status); + UniqueDvrSurface surface = surface_status.take(); + ASSERT_NE(nullptr, surface.get()); + + // Get surface state and verify there is one surface. + ASSERT_STATUS_OK(manager_->WaitForUpdate()); + ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount()); + + // Create a new queue in the surface. + const uint32_t kLayerCount = 3; + auto write_queue_status = CreateSurfaceQueue( + surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, kLayerCount, + AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1); + ASSERT_STATUS_OK(write_queue_status); + UniqueDvrWriteBufferQueue write_queue = write_queue_status.take(); + ASSERT_NE(nullptr, write_queue.get()); + + DvrWriteBuffer* buffer = nullptr; + dvrWriteBufferCreateEmpty(&buffer); + int fence_fd = -1; + int error = + dvrWriteBufferQueueDequeue(write_queue.get(), 1000, buffer, &fence_fd); + ASSERT_EQ(0, error); + + AHardwareBuffer* hardware_buffer = nullptr; + error = dvrWriteBufferGetAHardwareBuffer(buffer, &hardware_buffer); + ASSERT_EQ(0, error); + + AHardwareBuffer_Desc desc = {}; + AHardwareBuffer_describe(hardware_buffer, &desc); + ASSERT_EQ(kLayerCount, desc.layers); + + AHardwareBuffer_release(hardware_buffer); + dvrWriteBufferDestroy(buffer); +} + +} // namespace + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp new file mode 100644 index 0000000000..e65f6d5b01 --- /dev/null +++ b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp @@ -0,0 +1,160 @@ +#include <android/hardware_buffer.h> +#include <dvr/dvr_buffer.h> +#include <dvr/dvr_display_manager.h> +#include <dvr/dvr_surface.h> +#include <system/graphics.h> + +#include <base/logging.h> +#include <gtest/gtest.h> + +namespace android { +namespace dvr { + +namespace { + +class DvrNamedBufferTest : public ::testing::Test { + protected: + void SetUp() override { + const int ret = dvrDisplayManagerCreate(&client_); + ASSERT_EQ(0, ret); + ASSERT_NE(nullptr, client_); + } + + void TearDown() override { + dvrDisplayManagerDestroy(client_); + client_ = nullptr; + } + + DvrDisplayManager* client_ = nullptr; +}; + +TEST_F(DvrNamedBufferTest, TestNamedBuffersSameName) { + const char* buffer_name = "same_name"; + DvrBuffer* buffer1 = nullptr; + int ret1 = + dvrDisplayManagerSetupNamedBuffer(client_, buffer_name, 10, 0, &buffer1); + ASSERT_EQ(0, ret1); + ASSERT_NE(nullptr, buffer1); + + DvrBuffer* buffer2 = nullptr; + int ret2 = + dvrDisplayManagerSetupNamedBuffer(client_, buffer_name, 10, 0, &buffer2); + ASSERT_EQ(0, ret1); + ASSERT_NE(nullptr, buffer2); + + AHardwareBuffer* hardware_buffer1 = nullptr; + int e1 = dvrBufferGetAHardwareBuffer(buffer1, &hardware_buffer1); + ASSERT_EQ(0, e1); + ASSERT_NE(nullptr, hardware_buffer1); + + AHardwareBuffer* hardware_buffer2 = nullptr; + int e2 = dvrBufferGetAHardwareBuffer(buffer2, &hardware_buffer2); + ASSERT_EQ(0, e2); + ASSERT_NE(nullptr, hardware_buffer2); + + AHardwareBuffer_Desc desc1 = {}; + AHardwareBuffer_describe(hardware_buffer1, &desc1); + AHardwareBuffer_Desc desc2 = {}; + AHardwareBuffer_describe(hardware_buffer2, &desc2); + ASSERT_EQ(desc1.width, 10u); + ASSERT_EQ(desc1.height, 1u); + ASSERT_EQ(desc1.layers, 1u); + ASSERT_EQ(desc1.format, HAL_PIXEL_FORMAT_BLOB); + ASSERT_EQ(desc1.usage, 0u); + ASSERT_EQ(desc2.width, 10u); + ASSERT_EQ(desc2.height, 1u); + ASSERT_EQ(desc2.layers, 1u); + ASSERT_EQ(desc2.format, HAL_PIXEL_FORMAT_BLOB); + ASSERT_EQ(desc2.usage, 0u); + + dvrBufferDestroy(buffer1); + dvrBufferDestroy(buffer2); + + DvrBuffer* buffer3 = nullptr; + int e3 = dvrGetNamedBuffer(buffer_name, &buffer3); + ASSERT_NE(nullptr, buffer3); + ASSERT_EQ(0, e3); + + AHardwareBuffer* hardware_buffer3 = nullptr; + int e4 = dvrBufferGetAHardwareBuffer(buffer3, &hardware_buffer3); + ASSERT_EQ(0, e4); + ASSERT_NE(nullptr, hardware_buffer3); + + AHardwareBuffer_Desc desc3 = {}; + AHardwareBuffer_describe(hardware_buffer3, &desc3); + ASSERT_EQ(desc3.width, 10u); + ASSERT_EQ(desc3.height, 1u); + ASSERT_EQ(desc3.layers, 1u); + ASSERT_EQ(desc3.format, HAL_PIXEL_FORMAT_BLOB); + ASSERT_EQ(desc3.usage, 0u); + + dvrBufferDestroy(buffer3); + + AHardwareBuffer_release(hardware_buffer1); + AHardwareBuffer_release(hardware_buffer2); + AHardwareBuffer_release(hardware_buffer3); +} + +TEST_F(DvrNamedBufferTest, TestMultipleNamedBuffers) { + const char* buffer_name1 = "test1"; + const char* buffer_name2 = "test2"; + DvrBuffer* setup_buffer1 = nullptr; + int ret1 = dvrDisplayManagerSetupNamedBuffer(client_, buffer_name1, 10, 0, + &setup_buffer1); + ASSERT_EQ(0, ret1); + ASSERT_NE(nullptr, setup_buffer1); + dvrBufferDestroy(setup_buffer1); + + DvrBuffer* setup_buffer2 = nullptr; + int ret2 = dvrDisplayManagerSetupNamedBuffer(client_, buffer_name2, 10, 0, + &setup_buffer2); + ASSERT_EQ(0, ret2); + ASSERT_NE(nullptr, setup_buffer2); + dvrBufferDestroy(setup_buffer2); + + DvrBuffer* buffer1 = nullptr; + int e1 = dvrGetNamedBuffer(buffer_name1, &buffer1); + ASSERT_NE(nullptr, buffer1); + ASSERT_EQ(0, e1); + dvrBufferDestroy(buffer1); + + DvrBuffer* buffer2 = nullptr; + int e2 = dvrGetNamedBuffer(buffer_name2, &buffer2); + ASSERT_NE(nullptr, buffer2); + ASSERT_EQ(0, e2); + dvrBufferDestroy(buffer2); +} + +TEST_F(DvrNamedBufferTest, TestNamedBufferUsage) { + const char* buffer_name = "buffer_usage"; + + // Set usage to AHARDWAREBUFFER_USAGE_VIDEO_ENCODE. We use this because + // internally AHARDWAREBUFFER_USAGE_VIDEO_ENCODE is converted to + // GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER, and these two values are different. + // If all is good, when we get the AHardwareBuffer, it should be converted + // back to AHARDWAREBUFFER_USAGE_VIDEO_ENCODE. + const uint64_t usage = AHARDWAREBUFFER_USAGE_VIDEO_ENCODE; + + DvrBuffer* setup_buffer = nullptr; + int e1 = dvrDisplayManagerSetupNamedBuffer(client_, buffer_name, 10, usage, + &setup_buffer); + ASSERT_NE(nullptr, setup_buffer); + ASSERT_EQ(0, e1); + + AHardwareBuffer* hardware_buffer = nullptr; + int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer); + ASSERT_EQ(0, e2); + ASSERT_NE(nullptr, hardware_buffer); + + AHardwareBuffer_Desc desc = {}; + AHardwareBuffer_describe(hardware_buffer, &desc); + ASSERT_EQ(usage, desc.usage); + + dvrBufferDestroy(setup_buffer); + AHardwareBuffer_release(hardware_buffer); +} + +} // namespace + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libdvrcommon/Android.bp b/libs/vr/libdvrcommon/Android.bp new file mode 100644 index 0000000000..527cdbdd2a --- /dev/null +++ b/libs/vr/libdvrcommon/Android.bp @@ -0,0 +1,71 @@ +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +localIncludeFiles = [ + "include", +] + +sharedLibraries = [ + "libbase", + "libcutils", + "liblog", + "libutils", + "libEGL", + "libGLESv2", + "libui", + "libgui", + "libhardware", +] + +staticLibraries = ["libpdx_default_transport"] + +headerLibraries = [ + "libeigen", +] + +cc_library { + local_include_dirs: localIncludeFiles, + + cflags: [ + "-DLOG_TAG=\"libdvrcommon\"", + "-DTRACE=0", + ], + export_include_dirs: localIncludeFiles, + + header_libs: headerLibraries, + export_header_lib_headers: headerLibraries, + + name: "libdvrcommon", +} + +testFiles = [ + "tests/numeric_test.cpp", + "tests/pose_test.cpp", +] + +cc_test { + name: "libdvrcommon_test", + tags: ["optional"], + + srcs: testFiles, + + shared_libs: sharedLibraries, + + static_libs: [ + "libgmock_main", + "libgmock", + "libgtest", + "libdvrcommon", + ] + staticLibraries, +} diff --git a/libs/vr/libdvrcommon/include/private/dvr/benchmark.h b/libs/vr/libdvrcommon/include/private/dvr/benchmark.h new file mode 100644 index 0000000000..7eeab16030 --- /dev/null +++ b/libs/vr/libdvrcommon/include/private/dvr/benchmark.h @@ -0,0 +1,86 @@ +#ifndef ANDROID_DVR_BENCHMARK_H_ +#define ANDROID_DVR_BENCHMARK_H_ + +#include <stdio.h> +#include <time.h> + +#include <cutils/trace.h> + +#include <private/dvr/clock_ns.h> + +// Set benchmark traces, using Android systrace. +// +// The simplest one-parameter version of btrace automatically sets the +// timestamp with the system clock. The other versions can optionally set the +// timestamp manually, or pass additional data to be written to the log line. +// +// Example: +// Btrace("Start execution"); +// ... code to benchmark ... +// Btrace("End execution"); +// +// Use compute_benchmarks.py +// with the trace path "Start execution,End execution", +// to report the elapsed time between the two calls. +// +// Btrace will either output to standard atrace, or to a file if specified. +// The versions BtraceData also allow an int64_t to be included in the trace. + +// Btrace without data payload. +static inline void Btrace(const char* name, int64_t nanoseconds_monotonic); +static inline void Btrace(const char* name); +static inline void Btrace(FILE* file, const char* name, + int64_t nanoseconds_monotonic); +static inline void Btrace(FILE* file, const char* name); + +// Btrace with data payload. +static inline void BtraceData(const char* name, int64_t nanoseconds_monotonic, + int64_t data); +static inline void BtraceData(const char* name, int64_t data); +static inline void BtraceData(FILE* file, const char* name, + int64_t nanoseconds_monotonic, int64_t data); +static inline void BtraceData(FILE* file, const char* name, int64_t data); + +static inline void Btrace(const char* name, int64_t nanoseconds_monotonic) { + const int kLogMessageLength = 256; + char log_message[kLogMessageLength]; + snprintf(log_message, kLogMessageLength, "#btrace#%s", name); + atrace_int64(ATRACE_TAG_WEBVIEW, log_message, nanoseconds_monotonic); +} + +static inline void Btrace(const char* name) { + Btrace(name, android::dvr::GetSystemClockNs()); +} + +static inline void Btrace(FILE* file, const char* name, + int64_t nanoseconds_monotonic) { + fprintf(file, "#btrace#%s|%" PRId64 "\n", name, nanoseconds_monotonic); +} + +static inline void Btrace(FILE* file, const char* name) { + Btrace(file, name, android::dvr::GetSystemClockNs()); +} + +static inline void BtraceData(const char* name, int64_t nanoseconds_monotonic, + int64_t data) { + const int kLogMessageLength = 256; + char log_message[kLogMessageLength]; + snprintf(log_message, kLogMessageLength, "#btrace#%s|%" PRId64, name, data); + atrace_int64(ATRACE_TAG_WEBVIEW, log_message, nanoseconds_monotonic); +} + +static inline void BtraceData(const char* name, int64_t data) { + BtraceData(name, android::dvr::GetSystemClockNs(), data); +} + +static inline void BtraceData(FILE* file, const char* name, + int64_t nanoseconds_monotonic, int64_t data) { + fprintf(file, "#btrace#%s|%" PRId64 "|%" PRId64 "\n", name, data, + nanoseconds_monotonic); +} + +static inline void BtraceData(FILE* file, const char* name, int64_t data) { + BtraceData(file, name, android::dvr::GetSystemClockNs(), data); +} + +#endif // ANDROID_DVR_BENCHMARK_H_ diff --git a/libs/vr/libdvrcommon/include/private/dvr/clock_ns.h b/libs/vr/libdvrcommon/include/private/dvr/clock_ns.h new file mode 100644 index 0000000000..8e777edf6d --- /dev/null +++ b/libs/vr/libdvrcommon/include/private/dvr/clock_ns.h @@ -0,0 +1,84 @@ +#ifndef ANDROID_DVR_CLOCK_NS_H_ +#define ANDROID_DVR_CLOCK_NS_H_ + +#include <stdint.h> +#include <time.h> + +namespace android { +namespace dvr { + +constexpr int64_t kNanosPerSecond = 1000000000ll; + +// Returns the standard Dream OS monotonic system time that corresponds with all +// timestamps found in Dream OS APIs. +static inline timespec GetSystemClock() { + timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + return t; +} + +static inline timespec GetSystemClockRaw() { + timespec t; + clock_gettime(CLOCK_MONOTONIC_RAW, &t); + return t; +} + +static inline int64_t GetSystemClockNs() { + timespec t = GetSystemClock(); + int64_t ns = kNanosPerSecond * (int64_t)t.tv_sec + (int64_t)t.tv_nsec; + return ns; +} + +static inline int64_t GetSystemClockRawNs() { + timespec t = GetSystemClockRaw(); + int64_t ns = kNanosPerSecond * (int64_t)t.tv_sec + (int64_t)t.tv_nsec; + return ns; +} + +static inline double NsToSec(int64_t nanoseconds) { + return nanoseconds / static_cast<double>(kNanosPerSecond); +} + +static inline double GetSystemClockSec() { return NsToSec(GetSystemClockNs()); } + +static inline double GetSystemClockMs() { return GetSystemClockSec() * 1000.0; } + +// Converts a nanosecond timestamp to a timespec. Based on the kernel function +// of the same name. +static inline timespec NsToTimespec(int64_t ns) { + timespec t; + int32_t remainder; + + t.tv_sec = ns / kNanosPerSecond; + remainder = ns % kNanosPerSecond; + if (remainder < 0) { + t.tv_nsec--; + remainder += kNanosPerSecond; + } + t.tv_nsec = remainder; + + return t; +} + +// Timestamp comparison functions that handle wrapping values correctly. +static inline bool TimestampLT(int64_t a, int64_t b) { + return static_cast<int64_t>(static_cast<uint64_t>(a) - + static_cast<uint64_t>(b)) < 0; +} +static inline bool TimestampLE(int64_t a, int64_t b) { + return static_cast<int64_t>(static_cast<uint64_t>(a) - + static_cast<uint64_t>(b)) <= 0; +} +static inline bool TimestampGT(int64_t a, int64_t b) { + return static_cast<int64_t>(static_cast<uint64_t>(a) - + static_cast<uint64_t>(b)) > 0; +} +static inline bool TimestampGE(int64_t a, int64_t b) { + return static_cast<int64_t>(static_cast<uint64_t>(a) - + static_cast<uint64_t>(b)) >= 0; +} + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_CLOCK_NS_H_ diff --git a/libs/vr/libdvrcommon/include/private/dvr/debug.h b/libs/vr/libdvrcommon/include/private/dvr/debug.h new file mode 100644 index 0000000000..c31a385ffb --- /dev/null +++ b/libs/vr/libdvrcommon/include/private/dvr/debug.h @@ -0,0 +1,37 @@ +#ifndef ANDROID_DVR_DEBUG_H_ +#define ANDROID_DVR_DEBUG_H_ + +#include <GLES3/gl3.h> +#include <math.h> + +#include <log/log.h> + +#ifndef NDEBUG +#define CHECK_GL() \ + do { \ + const GLenum err = glGetError(); \ + if (err != GL_NO_ERROR) { \ + ALOGE("OpenGL error %d", err); \ + } \ + } while (0) + +#define CHECK_GL_FBO() \ + do { \ + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); \ + switch (status) { \ + case GL_FRAMEBUFFER_COMPLETE: \ + break; \ + case GL_FRAMEBUFFER_UNSUPPORTED: \ + ALOGE("GL_FRAMEBUFFER_UNSUPPORTED"); \ + break; \ + default: \ + ALOGE("FBO user error: %d", status); \ + break; \ + } \ + } while (0) +#else +#define CHECK_GL() +#define CHECK_GL_FBO() +#endif + +#endif // ANDROID_DVR_DEBUG_H_ diff --git a/libs/vr/libdvrcommon/include/private/dvr/eigen.h b/libs/vr/libdvrcommon/include/private/dvr/eigen.h new file mode 100644 index 0000000000..defaf583c4 --- /dev/null +++ b/libs/vr/libdvrcommon/include/private/dvr/eigen.h @@ -0,0 +1,57 @@ +#ifndef ANDROID_DVR_EIGEN_H_ +#define ANDROID_DVR_EIGEN_H_ + +#include <Eigen/Core> +#include <Eigen/Geometry> + +namespace Eigen { + +// Eigen doesn't take advantage of C++ template typedefs, but we can +template <class T, int N> +using Vector = Matrix<T, N, 1>; + +template <class T> +using Vector2 = Vector<T, 2>; + +template <class T> +using Vector3 = Vector<T, 3>; + +template <class T> +using Vector4 = Vector<T, 4>; + +template <class T, int N> +using RowVector = Matrix<T, 1, N>; + +template <class T> +using RowVector2 = RowVector<T, 2>; + +template <class T> +using RowVector3 = RowVector<T, 3>; + +template <class T> +using RowVector4 = RowVector<T, 4>; + +// In Eigen, the type you should be using for transformation matrices is the +// `Transform` class, instead of a raw `Matrix`. +// The `Projective` option means this will not make any assumptions about the +// last row of the object, making this suitable for use as general OpenGL +// projection matrices (which is the most common use-case). The one caveat +// is that in order to apply this transformation to non-homogeneous vectors +// (e.g., vec3), you must use the `.linear()` method to get the affine part of +// the matrix. +// +// Example: +// mat4 transform; +// vec3 position; +// vec3 transformed = transform.linear() * position; +// +// Note, the use of N-1 is because the parameter passed to Eigen is the ambient +// dimension of the transformation, not the size of the matrix iself. +// However graphics programmers sometimes get upset when they see a 3 next +// to a matrix when they expect a 4, so I'm hoping this will avoid that. +template <class T, int N> +using AffineMatrix = Transform<T, N-1, Projective>; + +} // namespace Eigen + +#endif // ANDROID_DVR_EIGEN_H_ diff --git a/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h b/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h new file mode 100644 index 0000000000..099a409005 --- /dev/null +++ b/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h @@ -0,0 +1,64 @@ +#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_ +#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_ + +#include <android-base/unique_fd.h> +#include <log/log.h> +#include <sys/epoll.h> + +namespace android { +namespace dvr { + +class EpollFileDescriptor { + public: + static const int CTL_ADD = EPOLL_CTL_ADD; + static const int CTL_MOD = EPOLL_CTL_MOD; + static const int CTL_DEL = EPOLL_CTL_DEL; + + EpollFileDescriptor() : fd_(-1) {} + + // Constructs an EpollFileDescriptor from an integer file descriptor and + // takes ownership. + explicit EpollFileDescriptor(int fd) : fd_(fd) {} + + bool IsValid() const { return fd_.get() >= 0; } + + int Create() { + if (IsValid()) { + ALOGW("epoll fd has already been created."); + return -EALREADY; + } + + fd_.reset(epoll_create(64)); + + if (fd_.get() < 0) + return -errno; + else + return 0; + } + + int Control(int op, int target_fd, epoll_event* ev) { + if (epoll_ctl(fd_.get(), op, target_fd, ev) < 0) + return -errno; + else + return 0; + } + + int Wait(epoll_event* events, int maxevents, int timeout) { + int ret = epoll_wait(fd_.get(), events, maxevents, timeout); + + if (ret < 0) + return -errno; + else + return ret; + } + + int Get() const { return fd_.get(); } + + private: + base::unique_fd fd_; +}; + +} // namespace dvr +} // namespace android + +#endif // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_ diff --git a/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h b/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h new file mode 100644 index 0000000000..d0ee69c770 --- /dev/null +++ b/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h @@ -0,0 +1,95 @@ +#ifndef ANDROID_DVR_FIELD_OF_VIEW_H_ +#define ANDROID_DVR_FIELD_OF_VIEW_H_ + +#include <cmath> + +#include <private/dvr/eigen.h> + +namespace android { +namespace dvr { + +// Encapsulates a generalized, asymmetric field of view with four half angles. +// Each half angle denotes the angle between the corresponding frustum plane. +// Together with a near and far plane, a FieldOfView forms the frustum of an +// off-axis perspective projection. +class FieldOfView { + public: + // The default constructor sets an angle of 0 (in any unit) for all four + // half-angles. + FieldOfView() : left_(0.0f), right_(0.0f), bottom_(0.0f), top_(0.0f) {} + + // Constructs a FieldOfView from four angles. + FieldOfView(float left, float right, float bottom, float top) + : left_(left), right_(right), bottom_(bottom), top_(top) {} + + explicit FieldOfView(const float* fov) + : FieldOfView(fov[0], fov[1], fov[2], fov[3]) {} + + // Accessors for all four half-angles. + float GetLeft() const { return left_; } + float GetRight() const { return right_; } + float GetBottom() const { return bottom_; } + float GetTop() const { return top_; } + + // Setters for all four half-angles. + void SetLeft(float left) { left_ = left; } + void SetRight(float right) { right_ = right; } + void SetBottom(float bottom) { bottom_ = bottom; } + void SetTop(float top) { top_ = top; } + + Eigen::AffineMatrix<float, 4> GetProjectionMatrix(float z_near, + float z_far) const { + float x_left = -std::tan(left_) * z_near; + float x_right = std::tan(right_) * z_near; + float y_bottom = -std::tan(bottom_) * z_near; + float y_top = std::tan(top_) * z_near; + + float zero = 0.0f; + if (x_left == x_right || y_bottom == y_top || z_near == z_far || + z_near <= zero || z_far <= zero) { + return Eigen::AffineMatrix<float, 4>::Identity(); + } + + float x = (2 * z_near) / (x_right - x_left); + float y = (2 * z_near) / (y_top - y_bottom); + float a = (x_right + x_left) / (x_right - x_left); + float b = (y_top + y_bottom) / (y_top - y_bottom); + float c = (z_near + z_far) / (z_near - z_far); + float d = (2 * z_near * z_far) / (z_near - z_far); + + // Note: Eigen matrix initialization syntax is always 'column-major' + // even if the storage is row-major. Or in other words, just write the + // matrix like you'd see in a math textbook. + Eigen::AffineMatrix<float, 4> result; + result.matrix() << x, 0, a, 0, + 0, y, b, 0, + 0, 0, c, d, + 0, 0, -1, 0; + return result; + } + + static FieldOfView FromProjectionMatrix( + const Eigen::AffineMatrix<float, 4>& m) { + // Compute tangents. + float tan_vert_fov = 1.0f / m(1, 1); + float tan_horz_fov = 1.0f / m(0, 0); + float t = (m(1, 2) + 1.0f) * tan_vert_fov; + float b = (m(1, 2) - 1.0f) * tan_vert_fov; + float l = (m(0, 2) - 1.0f) * tan_horz_fov; + float r = (m(0, 2) + 1.0f) * tan_horz_fov; + + return FieldOfView(std::atan(-l), std::atan(r), std::atan(-b), + std::atan(t)); + } + + private: + float left_; + float right_; + float bottom_; + float top_; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_FIELD_OF_VIEW_H_ diff --git a/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h b/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h new file mode 100644 index 0000000000..12ef622aaa --- /dev/null +++ b/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h @@ -0,0 +1,64 @@ +#ifndef ANDROID_DVR_LOG_HELPERS_H_ +#define ANDROID_DVR_LOG_HELPERS_H_ + +#include <iomanip> +#include <ostream> + +#include <private/dvr/eigen.h> +#include <private/dvr/field_of_view.h> + +namespace android { +namespace dvr { + +template <typename T> +inline std::ostream& operator<<(std::ostream& out, + const Eigen::Vector<T, 2>& vec) { + return out << "vec2(" << vec.x() << ',' << vec.y() << ')'; +} + +template <typename T> +inline std::ostream& operator<<(std::ostream& out, + const Eigen::Vector<T, 3>& vec) { + return out << "vec3(" << vec.x() << ',' << vec.y() << ',' << vec.z() << ')'; +} + +template <typename T> +inline std::ostream& operator<<(std::ostream& out, + const Eigen::Vector<T, 4>& vec) { + return out << "vec4(" << vec.x() << ',' << vec.y() << ',' << vec.z() << ',' + << vec.w() << ')'; +} + +template <typename T> +inline std::ostream& operator<<(std::ostream& out, + const Eigen::AffineMatrix<T, 4>& mat) { + out << std::setfill(' ') << std::setprecision(4) << std::fixed + << std::showpos; + out << "\nmat4["; + out << std::setw(10) << mat(0, 0) << " " << std::setw(10) << mat(0, 1) << " " + << std::setw(10) << mat(0, 2) << " " << std::setw(10) << mat(0, 3); + out << "]\n ["; + out << std::setw(10) << mat(1, 0) << " " << std::setw(10) << mat(1, 1) << " " + << std::setw(10) << mat(1, 2) << " " << std::setw(10) << mat(1, 3); + out << "]\n ["; + out << std::setw(10) << mat(2, 0) << " " << std::setw(10) << mat(2, 1) << " " + << std::setw(10) << mat(2, 2) << " " << std::setw(10) << mat(2, 3); + out << "]\n ["; + out << std::setw(10) << mat(3, 0) << " " << std::setw(10) << mat(3, 1) << " " + << std::setw(10) << mat(3, 2) << " " << std::setw(10) << mat(3, 3); + out << "]\n"; + + return out; +} + +inline std::ostream& operator<<(std::ostream& out, const FieldOfView& fov) { + return out << "fov(" << (fov.GetLeft() * 180.0f / M_PI) << ',' + << (fov.GetRight() * 180.0f / M_PI) << ',' + << (fov.GetBottom() * 180.0f / M_PI) << ',' + << (fov.GetTop() * 180.0f / M_PI) << ')'; +} + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_LOG_HELPERS_H_ diff --git a/libs/vr/libdvrcommon/include/private/dvr/matrix_helpers.h b/libs/vr/libdvrcommon/include/private/dvr/matrix_helpers.h new file mode 100644 index 0000000000..aef7146e22 --- /dev/null +++ b/libs/vr/libdvrcommon/include/private/dvr/matrix_helpers.h @@ -0,0 +1,26 @@ +#ifndef ANDROID_DVR_MATRIX_HELPERS_H_ +#define ANDROID_DVR_MATRIX_HELPERS_H_ + +#include <private/dvr/eigen.h> +#include <private/dvr/types.h> + +namespace android { +namespace dvr { + +// A helper function for creating a mat4 directly. +inline mat4 MakeMat4(float m00, float m01, float m02, float m03, float m10, + float m11, float m12, float m13, float m20, float m21, + float m22, float m23, float m30, float m31, float m32, + float m33) { + Eigen::Matrix4f matrix; + + matrix << m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, + m31, m32, m33; + + return mat4(matrix); +} + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_LOG_HELPERS_H_ diff --git a/libs/vr/libdvrcommon/include/private/dvr/numeric.h b/libs/vr/libdvrcommon/include/private/dvr/numeric.h new file mode 100644 index 0000000000..45458939c7 --- /dev/null +++ b/libs/vr/libdvrcommon/include/private/dvr/numeric.h @@ -0,0 +1,175 @@ +#ifndef ANDROID_DVR_NUMERIC_H_ +#define ANDROID_DVR_NUMERIC_H_ + +#include <cmath> +#include <limits> +#include <random> +#include <type_traits> + +#include <private/dvr/eigen.h> +#include <private/dvr/types.h> + +namespace android { +namespace dvr { + +template <typename FT> +static inline FT ToDeg(FT f) { + return f * static_cast<FT>(180.0 / M_PI); +} + +template <typename FT> +static inline FT ToRad(FT f) { + return f * static_cast<FT>(M_PI / 180.0); +} + +// Adjusts `x` to the periodic range `[lo, hi]` (to normalize angle values +// for example). +template <typename T> +T NormalizePeriodicRange(T x, T lo, T hi) { + T range_size = hi - lo; + + while (x < lo) { + x += range_size; + } + + while (x > hi) { + x -= range_size; + } + + return x; +} + +// Normalizes a measurement in radians. +// @param x the angle to be normalized +// @param centre the point around which to normalize the range +// @return the value of x, normalized to the range [centre - 180, centre + 180] +template <typename T> +T NormalizeDegrees(T x, T centre = static_cast<T>(180.0)) { + return NormalizePeriodicRange(x, centre - static_cast<T>(180.0), + centre + static_cast<T>(180.0)); +} + +// Normalizes a measurement in radians. +// @param x the angle to be normalized +// @param centre the point around which to normalize the range +// @return the value of x, normalized to the range +// [centre - M_PI, centre + M_PI] +// @remark the centre parameter is to make it possible to specify a different +// periodic range. This is useful if you are planning on comparing two +// angles close to 0 or M_PI, so that one might not accidentally end +// up on the other side of the range +template <typename T> +T NormalizeRadians(T x, T centre = static_cast<T>(M_PI)) { + return NormalizePeriodicRange(x, centre - static_cast<T>(M_PI), + centre + static_cast<T>(M_PI)); +} + +static inline vec2i Round(const vec2& v) { + return vec2i(roundf(v.x()), roundf(v.y())); +} + +static inline vec2i Scale(const vec2i& v, float scale) { + return vec2i(roundf(static_cast<float>(v.x()) * scale), + roundf(static_cast<float>(v.y()) * scale)); +} + +// Re-maps `x` from `[lba,uba]` to `[lbb,ubb]`. +template <typename T> +T ConvertRange(T x, T lba, T uba, T lbb, T ubb) { + return (((x - lba) * (ubb - lbb)) / (uba - lba)) + lbb; +} + +template <typename R1, typename R2> +static inline vec2 MapPoint(const vec2& pt, const R1& from, const R2& to) { + vec2 normalized((pt - vec2(from.p1)).array() / vec2(from.GetSize()).array()); + return (normalized * vec2(to.GetSize())) + vec2(to.p1); +} + +template <typename T> +inline bool IsZero(const T& v, + const T& tol = std::numeric_limits<T>::epsilon()) { + return std::abs(v) <= tol; +} + +template <typename T> +inline bool IsEqual(const T& a, const T& b, + const T& tol = std::numeric_limits<T>::epsilon()) { + return std::abs(b - a) <= tol; +} + +template <typename T> +T Square(const T& x) { + return x * x; +} + +template <typename T> +T RandomInRange(T lo, T hi, + typename + std::enable_if<std::is_floating_point<T>::value>::type* = 0) { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution<T> distro(lo, hi); + return distro(gen); +} + +template <typename T> +T RandomInRange(T lo, T hi, + typename + std::enable_if<std::is_integral<T>::value>::type* = 0) { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<T> distro(lo, hi); + return distro(gen); +} + +template <typename Derived1, typename Derived2> +Derived1 RandomInRange( + const Eigen::MatrixBase<Derived1>& lo, + const Eigen::MatrixBase<Derived2>& hi) { + EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(Derived1, Derived2); + + Derived1 result = Eigen::MatrixBase<Derived1>::Zero(); + + for (int row = 0; row < result.rows(); ++row) { + for (int col = 0; col < result.cols(); ++col) { + result(row, col) = RandomInRange(lo(row, col), hi(row, col)); + } + } + + return result; +} + +template <typename T> +T RandomRange(T x) { + return RandomInRange(-x, x); +} + +template <typename T> +T Clamp(T x, T lo, T hi) { + return std::min(std::max(x, lo), hi); +} + +inline mat3 ScaleMatrix(const vec2& scale_xy) { + return mat3(Eigen::Scaling(scale_xy[0], scale_xy[1], 1.0f)); +} + +inline mat3 TranslationMatrix(const vec2& translation) { + return mat3(Eigen::Translation2f(translation)); +} + +inline mat4 TranslationMatrix(const vec3& translation) { + return mat4(Eigen::Translation3f(translation)); +} + +inline vec2 TransformPoint(const mat3& m, const vec2& p) { + return m.linear() * p + m.translation(); +} + +inline vec2 TransformVector(const mat3& m, const vec2& p) { + return m.linear() * p; +} + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_NUMERIC_H_ diff --git a/libs/vr/libdvrcommon/include/private/dvr/ortho.h b/libs/vr/libdvrcommon/include/private/dvr/ortho.h new file mode 100644 index 0000000000..fc0bce3792 --- /dev/null +++ b/libs/vr/libdvrcommon/include/private/dvr/ortho.h @@ -0,0 +1,31 @@ +#ifndef ANDROID_DVR_ORTHO_H_ +#define ANDROID_DVR_ORTHO_H_ + +#include <private/dvr/types.h> + +namespace android { +namespace dvr { + +template <class T> +Eigen::AffineMatrix<T, 4> OrthoMatrix(T left, T right, T bottom, T top, + T znear, T zfar) { + Eigen::AffineMatrix<T, 4> result; + const T t2 = static_cast<T>(2); + const T a = t2 / (right - left); + const T b = t2 / (top - bottom); + const T c = t2 / (zfar - znear); + const T xoff = -(right + left) / (right - left); + const T yoff = -(top + bottom) / (top - bottom); + const T zoff = -(zfar + znear) / (zfar - znear); + const T t1 = static_cast<T>(1); + result.matrix() << a, 0, 0, xoff, + 0, b, 0, yoff, + 0, 0, c, zoff, + 0, 0, 0, t1; + return result; +} + +} // namespace android +} // namespace dvr + +#endif // ANDROID_DVR_ORTHO_H_ diff --git a/libs/vr/libdvrcommon/include/private/dvr/pose.h b/libs/vr/libdvrcommon/include/private/dvr/pose.h new file mode 100644 index 0000000000..97944e8928 --- /dev/null +++ b/libs/vr/libdvrcommon/include/private/dvr/pose.h @@ -0,0 +1,118 @@ +#ifndef ANDROID_DVR_POSE_H_ +#define ANDROID_DVR_POSE_H_ + +#include <private/dvr/eigen.h> + +namespace android { +namespace dvr { + +// Encapsulates a 3D pose (rotation and position). +// +// @tparam T Data type for storing the position coordinate and rotation +// quaternion. +template <typename T> +class Pose { + public: + // Creates identity pose. + Pose() + : rotation_(Eigen::Quaternion<T>::Identity()), + position_(Eigen::Vector3<T>::Zero()) {} + + // Initializes a pose with given rotation and position. + // + // rotation Initial rotation. + // position Initial position. + Pose(Eigen::Quaternion<T> rotation, Eigen::Vector3<T> position) + : rotation_(rotation), position_(position) {} + + void Invert() { + rotation_ = rotation_.inverse(); + position_ = rotation_ * -position_; + } + + Pose Inverse() const { + Pose result(*this); + result.Invert(); + return result; + } + + // Compute the composition of this pose with another, storing the result + // in the current object + void ComposeInPlace(const Pose& other) { + position_ = position_ + rotation_ * other.position_; + rotation_ = rotation_ * other.rotation_; + } + + // Computes the composition of this pose with another, and returns the result + Pose Compose(const Pose& other) const { + Pose result(*this); + result.ComposeInPlace(other); + return result; + } + + Eigen::Vector3<T> TransformPoint(const Eigen::Vector3<T>& v) const { + return rotation_ * v + position_; + } + + Eigen::Vector3<T> Transform(const Eigen::Vector3<T>& v) const { + return rotation_ * v; + } + + Pose& operator*=(const Pose& other) { + ComposeInPlace(other); + return *this; + } + + Pose operator*(const Pose& other) const { return Compose(other); } + + // Gets the rotation of the 3D pose. + Eigen::Quaternion<T> GetRotation() const { return rotation_; } + + // Gets the position of the 3D pose. + Eigen::Vector3<T> GetPosition() const { return position_; } + + // Sets the rotation of the 3D pose. + void SetRotation(Eigen::Quaternion<T> rotation) { rotation_ = rotation; } + + // Sets the position of the 3D pose. + void SetPosition(Eigen::Vector3<T> position) { position_ = position; } + + // Gets a 4x4 matrix representing a transform from the reference space (that + // the rotation and position of the pose are relative to) to the object space. + Eigen::AffineMatrix<T, 4> GetObjectFromReferenceMatrix() const; + + // Gets a 4x4 matrix representing a transform from the object space to the + // reference space (that the rotation and position of the pose are relative + // to). + Eigen::AffineMatrix<T, 4> GetReferenceFromObjectMatrix() const; + + private: + Eigen::Quaternion<T> rotation_; + Eigen::Vector3<T> position_; +}; + +template <typename T> +Eigen::AffineMatrix<T, 4> Pose<T>::GetObjectFromReferenceMatrix() const { + // The transfrom from the reference is the inverse of the pose. + Eigen::AffineMatrix<T, 4> matrix(rotation_.inverse().toRotationMatrix()); + return matrix.translate(-position_); +} + +template <typename T> +Eigen::AffineMatrix<T, 4> Pose<T>::GetReferenceFromObjectMatrix() const { + // The transfrom to the reference. + Eigen::AffineMatrix<T, 4> matrix(rotation_.toRotationMatrix()); + return matrix.pretranslate(position_); +} + +//------------------------------------------------------------------------------ +// Type-specific typedefs. +//------------------------------------------------------------------------------ + +using Posef = Pose<float>; +using Posed = Pose<double>; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_POSE_H_ diff --git a/libs/vr/libdvrcommon/include/private/dvr/range.h b/libs/vr/libdvrcommon/include/private/dvr/range.h new file mode 100644 index 0000000000..1d06c96ec5 --- /dev/null +++ b/libs/vr/libdvrcommon/include/private/dvr/range.h @@ -0,0 +1,43 @@ +#ifndef ANDROID_DVR_RANGE_H_ +#define ANDROID_DVR_RANGE_H_ + +#include <private/dvr/eigen.h> + +namespace android { +namespace dvr { + +// TODO(skiazyk): Replace all instances of this with Eigen::AlignedBox + +// Container of two points that define a 2D range. +template <class T, int d> +struct Range { + // Construct an uninitialized Range. + Range() {} + Range(Eigen::Vector<T, d> p1, Eigen::Vector<T, d> p2) : p1(p1), p2(p2) {} + + static Range<T, d> FromSize(Eigen::Vector<T, d> p1, + Eigen::Vector<T, d> size) { + return Range<T, d>(p1, p1 + size); + } + + bool operator==(const Range<T, d>& rhs) const { + return p1 == rhs.p1 && p2 == rhs.p2; + } + + Eigen::Vector<T, d> GetMinPoint() const { return p1; } + + Eigen::Vector<T, d> GetMaxPoint() const { return p2; } + + Eigen::Vector<T, d> GetSize() const { return p2 - p1; } + + Eigen::Vector<T, d> p1; + Eigen::Vector<T, d> p2; +}; + +typedef Range<int, 2> Range2i; +typedef Range<float, 2> Range2f; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_RANGE_H_ diff --git a/libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h b/libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h new file mode 100644 index 0000000000..44485a734c --- /dev/null +++ b/libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h @@ -0,0 +1,99 @@ +#ifndef ANDROID_DVR_RING_BUFFER_H_ +#define ANDROID_DVR_RING_BUFFER_H_ + +#include <utility> +#include <vector> + +namespace android { +namespace dvr { + +// A simple ring buffer implementation. +// +// A vector works but you either have to keep track of start_ and size_ yourself +// or erase() from the front which is inefficient. +// +// A deque works but the common usage pattern of Append() PopFront() Append() +// PopFront() looks like it allocates each time size goes from 0 --> 1, which we +// don't want. This class allocates only once. +template <typename T> +class RingBuffer { + public: + RingBuffer() { Reset(0); } + + explicit RingBuffer(size_t capacity) { Reset(capacity); } + + RingBuffer(const RingBuffer& other) = default; + RingBuffer(RingBuffer&& other) = default; + RingBuffer& operator=(const RingBuffer& other) = default; + RingBuffer& operator=(RingBuffer&& other) = default; + + void Append(const T& val) { + if (IsFull()) + PopFront(); + Get(size_) = val; + size_++; + } + + void Append(T&& val) { + if (IsFull()) + PopFront(); + Get(size_) = std::move(val); + size_++; + } + + bool IsEmpty() const { return size_ == 0; } + + bool IsFull() const { return size_ == buffer_.size(); } + + size_t GetSize() const { return size_; } + + size_t GetCapacity() const { return buffer_.size(); } + + T& Get(size_t i) { return buffer_[(start_ + i) % buffer_.size()]; } + + const T& Get(size_t i) const { + return buffer_[(start_ + i) % buffer_.size()]; + } + + const T& Back() const { return Get(size_ - 1); } + + T& Back() { return Get(size_ - 1); } + + const T& Front() const { return Get(0); } + + T& Front() { return Get(0); } + + void PopBack() { + if (size_ != 0) { + Get(size_ - 1) = T(); + size_--; + } + } + + void PopFront() { + if (size_ != 0) { + Get(0) = T(); + start_ = (start_ + 1) % buffer_.size(); + size_--; + } + } + + void Clear() { Reset(GetCapacity()); } + + void Reset(size_t capacity) { + start_ = size_ = 0; + buffer_.clear(); + buffer_.resize(capacity); + } + + private: + // Ideally we'd allocate our own memory and use placement new to instantiate + // instances of T instead of using a vector, but the vector is simpler. + std::vector<T> buffer_; + size_t start_, size_; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_RING_BUFFER_H_ diff --git a/libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h b/libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h new file mode 100644 index 0000000000..6048652f92 --- /dev/null +++ b/libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h @@ -0,0 +1,124 @@ +#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_ +#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_ + +#include <gtest/gtest.h> + +#include <cmath> + +#include <private/dvr/numeric.h> + +namespace android { +namespace dvr { + +template <int N, typename A, typename B, typename T> +::testing::AssertionResult CmpArrayLikeFloatEq( + const char* expectedStr, const char* actualStr, const char* toleranceStr, + const A& expected, const B& actual, const T& tolerance) { + for (int i = 0; i < N; ++i) { + if (!IsEqual(expected[i], actual[i], tolerance)) { + return ::testing::AssertionFailure() + << "\"" << expectedStr << "\" and \"" << actualStr + << "\" differ at element " << i << " by at least " << tolerance + << " : " + << " Expected \"" << expected[i] << "\", was \"" << actual[i] + << "\"."; + } + } + + return ::testing::AssertionSuccess(); +} + +template <int N, typename A, typename B, typename T> +::testing::AssertionResult CmpMatrixLikeFloatEq( + const char* expectedStr, const char* actualStr, const char* toleranceStr, + const A& expected, const B& actual, const T& tolerance) { + for (int r = 0; r < N; ++r) { + for (int c = 0; c < N; ++c) { + if (!IsEqual(expected(r, c), actual(r, c), tolerance)) { + return ::testing::AssertionFailure() + << "\"" << expectedStr << "\" and \"" << actualStr + << "\" differ at (" << r << "," << c << ")" + << " by at least " << tolerance << " : " + << " Expected \"" << expected(r, c) << "\", was \"" + << actual(r, c) << "\"."; + } + } + } + + return ::testing::AssertionSuccess(); +} + +template <int N, typename A, typename B, typename T> +::testing::AssertionResult CmpArrayLikeFloatNe( + const char* expectedStr, const char* actualStr, const char* toleranceStr, + const A& expected, const B& actual, const T& tolerance) { + for (int i = 0; i < N; ++i) { + if (!IsEqual(expected[i], actual[i], tolerance)) { + return ::testing::AssertionSuccess(); + } + } + + ::testing::Message message; + message << "Expected \"" << expectedStr + << "\" to differ from provided value \"" << actualStr + << "\" by at least " << tolerance << "."; + + return ::testing::AssertionFailure(message); +} + +template <int N, typename A, typename B, typename T> +::testing::AssertionResult CmpMatrixLikeFloatNe( + const char* expectedStr, const char* actualStr, const char* toleranceStr, + const A& expected, const B& actual, const T& tolerance) { + for (int r = 0; r < N; ++r) { + for (int c = 0; c < N; ++c) { + if (!IsEqual(expected(r, c), actual(r, c), tolerance)) { + return ::testing::AssertionSuccess(); + } + } + } + + ::testing::Message message; + message << "Expected \"" << expectedStr + << "\" to differ from provided value \"" << actualStr + << "\" by at least " << tolerance << "."; + + return ::testing::AssertionFailure(message); +} + +} // namespace dvr +} // namespace android + +#define EXPECT_VEC3_NEAR(expected, actual, tol) \ + EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatEq<3>, expected, actual, \ + tol) + +#define EXPECT_VEC3_NOT_NEAR(expected, actual, tol) \ + EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatNe<3>, expected, actual, \ + tol) + +#define EXPECT_QUAT_NEAR(expected, actual, tol) \ + EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatEq<3>, expected.coeffs(), \ + actual.coeffs(), tol) + +#define EXPECT_QUAT_NOT_NEAR(expected, actual, tol) \ + EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatNe<3>, expected.coeffs(), \ + actual.coeffs(), tol) + +#define EXPECT_MAT4_NEAR(expected, actual, tol) \ + EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatEq<4>, expected, actual, \ + tol) + +#define EXPECT_MAT4_NOT_NEAR(expected, actual, tol) \ + EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatNe<4>, expected, actual, \ + tol) + +#define EXPECT_MAT3_NEAR(expected, actual, tol) \ + EXPECT_PRED_FORMAT3(android::dvr \ + : CmpMatrixLikeFloatEq<3>, expected, actual, tol) + +#define EXPECT_MAT3_NOT_NEAR(expected, actual, tol) \ + EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatNe<3>, expected, actual, \ + tol) + +#endif // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_ diff --git a/libs/vr/libdvrcommon/include/private/dvr/types.h b/libs/vr/libdvrcommon/include/private/dvr/types.h new file mode 100644 index 0000000000..1fa54afe92 --- /dev/null +++ b/libs/vr/libdvrcommon/include/private/dvr/types.h @@ -0,0 +1,51 @@ +#ifndef ANDROID_DVR_TYPES_H_ +#define ANDROID_DVR_TYPES_H_ + +// All basic types used by VR code. + +#include <private/dvr/eigen.h> +#include <private/dvr/field_of_view.h> +#include <private/dvr/pose.h> +#include <private/dvr/range.h> + +namespace android { +namespace dvr { + +enum RgbColorChannel { kRed, kGreen, kBlue }; + +// EyeType: 0 for left, 1 for right. +enum EyeType { kLeftEye = 0, kRightEye = 1 }; + +// In the context of VR, vector types are used as much as base types. + +using vec2f = Eigen::Vector2f; +using vec2d = Eigen::Vector2d; +using vec2i = Eigen::Vector2i; +using vec2 = vec2f; + +using vec3f = Eigen::Vector3f; +using vec3d = Eigen::Vector3d; +using vec3i = Eigen::Vector3i; +using vec3 = vec3f; + +using vec4f = Eigen::Vector4f; +using vec4d = Eigen::Vector4d; +using vec4i = Eigen::Vector4i; +using vec4 = vec4f; + +using mat3f = Eigen::AffineMatrix<float, 3>; +using mat3d = Eigen::AffineMatrix<double, 3>; +using mat3 = mat3f; + +using mat4f = Eigen::AffineMatrix<float, 4>; +using mat4d = Eigen::AffineMatrix<double, 4>; +using mat4 = mat4f; + +using quatf = Eigen::Quaternionf; +using quatd = Eigen::Quaterniond; +using quat = quatf; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_TYPES_H_ diff --git a/libs/vr/libdvrcommon/tests/numeric_test.cpp b/libs/vr/libdvrcommon/tests/numeric_test.cpp new file mode 100644 index 0000000000..1ee1447703 --- /dev/null +++ b/libs/vr/libdvrcommon/tests/numeric_test.cpp @@ -0,0 +1,67 @@ +#include <gtest/gtest.h> + +#include <private/dvr/numeric.h> + +using TestTypes = ::testing::Types<float, double, int>; + +using android::dvr::RandomInRange; + +template <typename T> +class NumericTest : public ::testing::TestWithParam<T> { + public: + using FT = T; +}; + +TYPED_TEST_CASE(NumericTest, TestTypes); + +TYPED_TEST(NumericTest, RandomInRange) { + using FT = typename TestFixture::FT; + + const int kNumTrials = 50; + const FT kLowRange = static_cast<FT>(-100); + const FT kHighRange = static_cast<FT>(100); + + for (int i = 0; i < kNumTrials; ++i) { + FT value = RandomInRange(kLowRange, kHighRange); + + EXPECT_LE(kLowRange, value); + EXPECT_GE(kHighRange, value); + } +} + +TEST(RandomInRange, TestIntVersion) { + // This checks specifically that the function does not always give the lo + // value (this was previously a bug) + + const int kNumTrials = 50; + const int kLowRange = -100; + const int kHighRange = 100; + + for (int i = 0; i < kNumTrials; ++i) { + int value = RandomInRange(kLowRange, kHighRange); + + if (value != kLowRange) { + SUCCEED(); + return; + } + } + + FAIL() << "Did not produce a value other than the range minimum for " + << "integers."; +} + +TEST(RandomInRange, TestVectorVersion) { + Eigen::Vector3d lo(-3.0, -4.0, -5.0); + Eigen::Vector3d hi(5.0, 4.0, 3.0); + + const int kNumTrials = 50; + + for (int i = 0; i < kNumTrials; ++i) { + Eigen::Vector3d result = RandomInRange(lo, hi); + + for (int j = 0; j < 3; ++j) { + EXPECT_LE(lo[j], result[j]); + EXPECT_GE(hi[j], result[j]); + } + } +} diff --git a/libs/vr/libdvrcommon/tests/pose_test.cpp b/libs/vr/libdvrcommon/tests/pose_test.cpp new file mode 100644 index 0000000000..aa1896da15 --- /dev/null +++ b/libs/vr/libdvrcommon/tests/pose_test.cpp @@ -0,0 +1,154 @@ +#include <gtest/gtest.h> + +#include <private/dvr/eigen.h> +#include <private/dvr/pose.h> +#include <private/dvr/test/test_macros.h> + +using PoseTypes = ::testing::Types<float, double>; + +template <class T> +class PoseTest : public ::testing::TestWithParam<T> { + public: + using FT = T; + using Pose_t = android::dvr::Pose<FT>; + using quat_t = Eigen::Quaternion<FT>; + using vec3_t = Eigen::Vector3<FT>; + using mat4_t = Eigen::AffineMatrix<FT, 4>; +}; + +TYPED_TEST_CASE(PoseTest, PoseTypes); + +// Check that the two matrix methods are inverses of each other +TYPED_TEST(PoseTest, SelfInverse) { + using quat_t = typename TestFixture::quat_t; + using vec3_t = typename TestFixture::vec3_t; + using Pose_t = typename TestFixture::Pose_t; + using mat4_t = typename TestFixture::mat4_t; + using FT = typename TestFixture::FT; + + const auto tolerance = FT(0.0001); + + const quat_t initial_rotation(Eigen::AngleAxis<FT>( + FT(M_PI / 3.0), vec3_t(FT(3.0), FT(4.0), FT(5.0)).normalized())); + const vec3_t initial_position = vec3_t(FT(2.0), FT(10.0), FT(-4.0)); + const Pose_t initial_pose(initial_rotation, initial_position); + + auto result_pose = initial_pose.GetReferenceFromObjectMatrix() * + initial_pose.GetObjectFromReferenceMatrix(); + + EXPECT_MAT4_NEAR(result_pose, mat4_t::Identity(), tolerance); +} + +TYPED_TEST(PoseTest, TransformPoint) { + using quat_t = typename TestFixture::quat_t; + using vec3_t = typename TestFixture::vec3_t; + using Pose_t = typename TestFixture::Pose_t; + using FT = typename TestFixture::FT; + + const auto tolerance = FT(0.0001); + + const quat_t pose_rotation( + Eigen::AngleAxis<FT>(FT(M_PI / 2.0), vec3_t(FT(0.0), FT(0.0), FT(1.0)))); + const auto pose_position = vec3_t(FT(1.0), FT(1.0), FT(2.0)); + + const Pose_t test_pose(pose_rotation, pose_position); + + for (int axis = 0; axis < 3; ++axis) { + vec3_t start_position = vec3_t::Zero(); + start_position[axis] = FT(1.0); + const vec3_t expected_transformed = + (pose_rotation * start_position) + pose_position; + const vec3_t actual_transformed = test_pose.TransformPoint(start_position); + EXPECT_VEC3_NEAR(expected_transformed, actual_transformed, tolerance); + } +} + +TYPED_TEST(PoseTest, TransformVector) { + using quat_t = typename TestFixture::quat_t; + using vec3_t = typename TestFixture::vec3_t; + using Pose_t = typename TestFixture::Pose_t; + using FT = typename TestFixture::FT; + + const auto tolerance = FT(0.0001); + + const quat_t pose_rotation(Eigen::AngleAxis<FT>( + FT(M_PI / 6.0), vec3_t(FT(3.0), FT(4.0), FT(5.0)).normalized())); + + const auto pose_position = vec3_t(FT(500.0), FT(-500.0), FT(300.0)); + + const Pose_t test_pose(pose_rotation, pose_position); + + for (int axis = 0; axis < 3; ++axis) { + vec3_t start_position = vec3_t::Zero(); + start_position[axis] = FT(1.0); + const vec3_t expected_rotated = pose_rotation * start_position; + const vec3_t actual_rotated = test_pose.Transform(start_position); + EXPECT_VEC3_NEAR(expected_rotated, actual_rotated, tolerance); + } +} + +TYPED_TEST(PoseTest, Composition) { + using quat_t = typename TestFixture::quat_t; + using Pose_t = typename TestFixture::Pose_t; + using vec3_t = typename TestFixture::vec3_t; + using FT = typename TestFixture::FT; + + const auto tolerance = FT(0.0001); + + const quat_t first_rotation( + Eigen::AngleAxis<FT>(FT(M_PI / 2.0), vec3_t(FT(0.0), FT(0.0), FT(1.0)))); + const auto first_offset = vec3_t(FT(-3.0), FT(2.0), FT(-1.0)); + const quat_t second_rotation(Eigen::AngleAxis<FT>( + FT(M_PI / 3.0), vec3_t(FT(1.0), FT(-1.0), FT(0.0)).normalized())); + const auto second_offset = vec3_t(FT(6.0), FT(-7.0), FT(-8.0)); + + const Pose_t first_pose(first_rotation, first_offset); + const Pose_t second_pose(second_rotation, second_offset); + + const auto combined_pose(second_pose.Compose(first_pose)); + + for (int axis = 0; axis < 3; ++axis) { + vec3_t start_position = vec3_t::Zero(); + start_position[axis] = FT(1.0); + const vec3_t expected_transformed = + second_pose.TransformPoint(first_pose.TransformPoint(start_position)); + const vec3_t actual_transformed = + combined_pose.TransformPoint(start_position); + EXPECT_VEC3_NEAR(expected_transformed, actual_transformed, tolerance); + } +} + +TYPED_TEST(PoseTest, Inverse) { + using quat_t = typename TestFixture::quat_t; + using vec3_t = typename TestFixture::vec3_t; + using Pose_t = typename TestFixture::Pose_t; + using FT = typename TestFixture::FT; + + const auto tolerance = FT(0.0001); + + const quat_t pose_rotation(Eigen::AngleAxis<FT>( + FT(M_PI / 2.0), vec3_t(FT(4.0), FT(-2.0), FT(-1.0)).normalized())); + const auto pose_position = vec3_t(FT(-1.0), FT(2.0), FT(-4.0)); + + Pose_t pose(pose_rotation, pose_position); + const Pose_t pose_inverse = pose.Inverse(); + + for (int axis = 0; axis < 3; ++axis) { + vec3_t start_position = vec3_t::Zero(); + start_position[axis] = FT(1.0); + const vec3_t transformed = pose.Transform(start_position); + const vec3_t inverted = pose_inverse.Transform(transformed); + EXPECT_VEC3_NEAR(start_position, inverted, tolerance); + } + + Pose_t nullified_pose[2] = { + pose.Compose(pose_inverse), pose_inverse.Compose(pose), + }; + + for (int i = 0; i < 2; ++i) { + EXPECT_QUAT_NEAR(quat_t::Identity(), nullified_pose[i].GetRotation(), + tolerance); + EXPECT_VEC3_NEAR(vec3_t::Zero(), nullified_pose[i].GetPosition(), + tolerance); + } +} diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp new file mode 100644 index 0000000000..f55e9942db --- /dev/null +++ b/libs/vr/libpdx/Android.bp @@ -0,0 +1,60 @@ +cc_library_static { + name: "libpdx", + clang: true, + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + export_include_dirs: ["private"], + local_include_dirs: ["private"], + srcs: [ + "client.cpp", + "service.cpp", + "status.cpp", + ], +} + +cc_test { + name: "pdx_tests", + clang: true, + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + srcs: [ + "client_tests.cpp", + "mock_tests.cpp", + "serialization_tests.cpp", + "service_tests.cpp", + "status_tests.cpp", + "thread_local_buffer_tests.cpp", + "variant_tests.cpp", + ], + static_libs: [ + "libgmock", + "libpdx", + "liblog", + "libutils", + "libvndksupport", + ], +} + +// Code analysis target. +cc_test { + name: "pdx_encoder_performance_test", + clang: true, + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + "-O2", + ], + srcs: [ + "encoder_performance_test.cpp", + ], + static_libs: [ + "libpdx", + ], +} diff --git a/libs/vr/libpdx/client.cpp b/libs/vr/libpdx/client.cpp new file mode 100644 index 0000000000..bfa2d879b5 --- /dev/null +++ b/libs/vr/libpdx/client.cpp @@ -0,0 +1,287 @@ +#include "pdx/client.h" + +#define LOG_TAG "ServiceFramework" +#include <log/log.h> + +#include <pdx/trace.h> + +namespace android { +namespace pdx { + +void Client::EnableAutoReconnect(int64_t reconnect_timeout_ms) { + if (channel_factory_) { + reconnect_timeout_ms_ = reconnect_timeout_ms; + auto_reconnect_enabled_ = true; + } +} + +void Client::DisableAutoReconnect() { auto_reconnect_enabled_ = false; } + +bool Client::IsConnected() const { return channel_.get() != nullptr; } + +Status<void> Client::CheckReconnect() { + Status<void> ret; + bool was_disconnected = !IsConnected(); + if (auto_reconnect_enabled_ && was_disconnected && channel_factory_) { + auto status = channel_factory_->Connect(reconnect_timeout_ms_); + if (!status) { + error_ = -status.error(); + ret.SetError(status.error()); + return ret; + } + channel_ = status.take(); + } + + if (!IsConnected()) { + ret.SetError(ESHUTDOWN); + } else { + // Call the subclass OnConnect handler. The subclass may choose to close the + // connection in the handler, in which case error_ will be non-zero. + if (was_disconnected) + OnConnect(); + if (!IsConnected()) + ret.SetError(-error_); + else + ret.SetValue(); + } + + return ret; +} + +bool Client::NeedToDisconnectChannel(int error) const { + return error == ESHUTDOWN && auto_reconnect_enabled_; +} + +void Client::CheckDisconnect(int error) { + if (NeedToDisconnectChannel(error)) + Close(error); +} + +Client::Client(std::unique_ptr<ClientChannel> channel) + : channel_{std::move(channel)} {} + +Client::Client(std::unique_ptr<ClientChannelFactory> channel_factory, + int64_t timeout_ms) + : channel_factory_{std::move(channel_factory)} { + auto status = channel_factory_->Connect(timeout_ms); + if (!status) { + ALOGE("Client::Client: Failed to connect to service because: %s", + status.GetErrorMessage().c_str()); + error_ = -status.error(); + } else { + channel_ = status.take(); + } +} + +bool Client::IsInitialized() const { + return IsConnected() || (channel_factory_ && auto_reconnect_enabled_); +} + +void Client::OnConnect() {} + +int Client::error() const { return error_; } + +Status<void> Client::SendImpulse(int opcode) { + PDX_TRACE_NAME("Client::SendImpulse"); + + auto status = CheckReconnect(); + if (!status) + return status; + + status = channel_->SendImpulse(opcode, nullptr, 0); + CheckDisconnect(status); + return status; +} + +Status<void> Client::SendImpulse(int opcode, const void* buffer, + size_t length) { + PDX_TRACE_NAME("Client::SendImpulse"); + + auto status = CheckReconnect(); + if (!status) + return status; + + status = channel_->SendImpulse(opcode, buffer, length); + CheckDisconnect(status); + return status; +} + +void Client::Close(int error) { + channel_.reset(); + // Normalize error codes to negative integer space. + error_ = error <= 0 ? error : -error; +} + +int Client::event_fd() const { + return IsConnected() ? channel_->event_fd() : -1; +} + +LocalChannelHandle& Client::GetChannelHandle() { + return channel_->GetChannelHandle(); +} + +///////////////////////////// Transaction implementation ////////////////////// + +Transaction::Transaction(Client& client) : client_{client} {} + +Transaction::~Transaction() { + if (state_allocated_ && client_.GetChannel()) + client_.GetChannel()->FreeTransactionState(state_); +} + +bool Transaction::EnsureStateAllocated() { + if (!state_allocated_ && client_.GetChannel()) { + state_ = client_.GetChannel()->AllocateTransactionState(); + state_allocated_ = true; + } + return state_allocated_; +} + +void Transaction::SendTransaction(int opcode, Status<void>* ret, + const iovec* send_vector, size_t send_count, + const iovec* receive_vector, + size_t receive_count) { + *ret = client_.CheckReconnect(); + if (!*ret) + return; + + if (!EnsureStateAllocated()) { + ret->SetError(ESHUTDOWN); + return; + } + + auto status = client_.GetChannel()->SendWithInt( + state_, opcode, send_vector, send_count, receive_vector, receive_count); + + if (status) { + ret->SetValue(); + } else { + ret->SetError(status.error()); + } + CheckDisconnect(status); +} + +void Transaction::SendTransaction(int opcode, Status<int>* ret, + const iovec* send_vector, size_t send_count, + const iovec* receive_vector, + size_t receive_count) { + auto status = client_.CheckReconnect(); + if (!status) { + ret->SetError(status.error()); + return; + } + + if (!EnsureStateAllocated()) { + ret->SetError(ESHUTDOWN); + return; + } + + *ret = client_.GetChannel()->SendWithInt( + state_, opcode, send_vector, send_count, receive_vector, receive_count); + + CheckDisconnect(*ret); +} + +void Transaction::SendTransaction(int opcode, Status<LocalHandle>* ret, + const iovec* send_vector, size_t send_count, + const iovec* receive_vector, + size_t receive_count) { + auto status = client_.CheckReconnect(); + if (!status) { + ret->SetError(status.error()); + return; + } + + if (!EnsureStateAllocated()) { + ret->SetError(ESHUTDOWN); + return; + } + + *ret = client_.GetChannel()->SendWithFileHandle( + state_, opcode, send_vector, send_count, receive_vector, receive_count); + + CheckDisconnect(*ret); +} + +void Transaction::SendTransaction(int opcode, Status<LocalChannelHandle>* ret, + const iovec* send_vector, size_t send_count, + const iovec* receive_vector, + size_t receive_count) { + auto status = client_.CheckReconnect(); + if (!status) { + ret->SetError(status.error()); + return; + } + + if (!EnsureStateAllocated()) { + ret->SetError(ESHUTDOWN); + return; + } + + *ret = client_.GetChannel()->SendWithChannelHandle( + state_, opcode, send_vector, send_count, receive_vector, receive_count); + + CheckDisconnect(*ret); +} + +Status<FileReference> Transaction::PushFileHandle(const LocalHandle& handle) { + if (client_.CheckReconnect() && EnsureStateAllocated()) + return client_.GetChannel()->PushFileHandle(state_, handle); + return ErrorStatus{ESHUTDOWN}; +} + +Status<FileReference> Transaction::PushFileHandle( + const BorrowedHandle& handle) { + if (client_.CheckReconnect() && EnsureStateAllocated()) + return client_.GetChannel()->PushFileHandle(state_, handle); + return ErrorStatus{ESHUTDOWN}; +} + +Status<FileReference> Transaction::PushFileHandle(const RemoteHandle& handle) { + return handle.Get(); +} + +Status<ChannelReference> Transaction::PushChannelHandle( + const LocalChannelHandle& handle) { + if (client_.CheckReconnect() && EnsureStateAllocated()) + return client_.GetChannel()->PushChannelHandle(state_, handle); + return ErrorStatus{ESHUTDOWN}; +} + +Status<ChannelReference> Transaction::PushChannelHandle( + const BorrowedChannelHandle& handle) { + if (client_.CheckReconnect() && EnsureStateAllocated()) + return client_.GetChannel()->PushChannelHandle(state_, handle); + return ErrorStatus{ESHUTDOWN}; +} + +Status<ChannelReference> Transaction::PushChannelHandle( + const RemoteChannelHandle& handle) { + return handle.value(); +} + +bool Transaction::GetFileHandle(FileReference ref, LocalHandle* handle) { + return client_.CheckReconnect() && EnsureStateAllocated() && + client_.GetChannel()->GetFileHandle(state_, ref, handle); +} + +bool Transaction::GetChannelHandle(ChannelReference ref, + LocalChannelHandle* handle) { + return client_.CheckReconnect() && EnsureStateAllocated() && + client_.GetChannel()->GetChannelHandle(state_, ref, handle); +} + +void Transaction::CheckDisconnect(int error) { + if (client_.NeedToDisconnectChannel(error)) { + if (state_allocated_) { + if (client_.GetChannel()) + client_.GetChannel()->FreeTransactionState(state_); + state_ = nullptr; + state_allocated_ = false; + } + client_.Close(error); + } +} + +} // namespace pdx +} // namespace android diff --git a/libs/vr/libpdx/client_tests.cpp b/libs/vr/libpdx/client_tests.cpp new file mode 100644 index 0000000000..99ccc698c4 --- /dev/null +++ b/libs/vr/libpdx/client_tests.cpp @@ -0,0 +1,567 @@ +#include <pdx/client.h> + +#include <gmock/gmock.h> +#include <sys/eventfd.h> + +#include <pdx/mock_client_channel.h> +#include <pdx/mock_client_channel_factory.h> +#include <pdx/rpc/remote_method.h> + +using android::pdx::BorrowedChannelHandle; +using android::pdx::BorrowedHandle; +using android::pdx::ClientBase; +using android::pdx::ClientChannel; +using android::pdx::ClientChannelFactory; +using android::pdx::ErrorStatus; +using android::pdx::LocalChannelHandle; +using android::pdx::LocalHandle; +using android::pdx::MockClientChannel; +using android::pdx::MockClientChannelFactory; +using android::pdx::RemoteChannelHandle; +using android::pdx::RemoteHandle; +using android::pdx::Status; +using android::pdx::Transaction; +using android::pdx::rpc::Void; + +using testing::A; +using testing::AnyNumber; +using testing::ByMove; +using testing::Invoke; +using testing::Ne; +using testing::Return; +using testing::_; + +namespace { + +inline void* IntToPtr(intptr_t addr) { return reinterpret_cast<void*>(addr); } +inline const void* IntToConstPtr(intptr_t addr) { + return reinterpret_cast<const void*>(addr); +} + +struct TestInterface final { + // Op codes. + enum { + kOpAdd = 0, + kOpSendFile, + kOpGetFile, + kOpPushChannel, + }; + + // Methods. + PDX_REMOTE_METHOD(Add, kOpAdd, int(int, int)); + PDX_REMOTE_METHOD(SendFile, kOpSendFile, void(const LocalHandle& fd)); + PDX_REMOTE_METHOD(GetFile, kOpGetFile, LocalHandle(const std::string&, int)); + PDX_REMOTE_METHOD(PushChannel, kOpPushChannel, LocalChannelHandle(Void)); + + PDX_REMOTE_API(API, Add, SendFile, GetFile, PushChannel); +}; + +class SimpleClient : public ClientBase<SimpleClient> { + public: + explicit SimpleClient(std::unique_ptr<ClientChannel> channel) + : BASE{std::move(channel)} {} + SimpleClient(std::unique_ptr<ClientChannelFactory> channel_factory, + int64_t timeout_ms) + : BASE{std::move(channel_factory), timeout_ms} { + EnableAutoReconnect(timeout_ms); + } + + using BASE::SendImpulse; + using BASE::InvokeRemoteMethod; + using BASE::InvokeRemoteMethodInPlace; + using BASE::Close; + using BASE::IsConnected; + using BASE::EnableAutoReconnect; + using BASE::DisableAutoReconnect; + using BASE::event_fd; + using BASE::GetChannel; + + MOCK_METHOD0(OnConnect, void()); +}; + +class FailingClient : public ClientBase<FailingClient> { + public: + explicit FailingClient(std::unique_ptr<ClientChannel> channel, int error_code) + : BASE{std::move(channel)} { + Close(error_code); + } +}; + +class ClientChannelTest : public testing::Test { + public: + ClientChannelTest() + : client_{SimpleClient::Create( + std::make_unique<testing::StrictMock<MockClientChannel>>())} {} + + MockClientChannel* mock_channel() { + return static_cast<MockClientChannel*>(client_->GetChannel()); + } + + std::unique_ptr<SimpleClient> client_; +}; + +class ClientChannelFactoryTest : public testing::Test { + public: + ClientChannelFactoryTest() { + auto factory = + std::make_unique<testing::NiceMock<MockClientChannelFactory>>(); + ON_CALL(*factory, Connect(kTimeout)) + .WillByDefault(Invoke(this, &ClientChannelFactoryTest::OnConnect)); + client_ = SimpleClient::Create(std::move(factory), kTimeout); + } + + MockClientChannel* mock_channel() { + return static_cast<MockClientChannel*>(client_->GetChannel()); + } + + Status<std::unique_ptr<ClientChannel>> OnConnect(int64_t /*timeout_ms*/) { + if (on_connect_error_) + return ErrorStatus(on_connect_error_); + std::unique_ptr<MockClientChannel> channel = + std::make_unique<testing::StrictMock<MockClientChannel>>(); + if (on_connect_callback_) + on_connect_callback_(channel.get()); + return Status<std::unique_ptr<ClientChannel>>{std::move(channel)}; + } + + void OnConnectCallback(std::function<void(MockClientChannel*)> callback) { + on_connect_callback_ = callback; + } + void SetOnConnectError(int error) { on_connect_error_ = error; } + void ResetOnConnectError() { on_connect_error_ = 0; } + + constexpr static int64_t kTimeout = 123; + std::unique_ptr<SimpleClient> client_; + std::function<void(MockClientChannel*)> on_connect_callback_; + int on_connect_error_{0}; +}; + +constexpr int64_t ClientChannelFactoryTest::kTimeout; + +class ClientTransactionTest : public ClientChannelTest { + public: + ClientTransactionTest() : transaction_{*client_} {} + + Transaction transaction_; +}; + +} // anonymous namespace + +TEST_F(ClientChannelTest, IsInitialized) { + ASSERT_NE(client_.get(), nullptr); + EXPECT_TRUE(client_->IsInitialized()); + EXPECT_TRUE(client_->IsConnected()); +} + +TEST_F(ClientChannelTest, CloseOnConstruction) { + FailingClient failed_client1{std::make_unique<MockClientChannel>(), EACCES}; + ASSERT_FALSE(failed_client1.IsInitialized()); + EXPECT_EQ(-EACCES, failed_client1.error()); + + FailingClient failed_client2{std::make_unique<MockClientChannel>(), -EACCES}; + ASSERT_FALSE(failed_client2.IsInitialized()); + EXPECT_EQ(-EACCES, failed_client2.error()); + + auto failed_client3 = + FailingClient::Create(std::make_unique<MockClientChannel>(), EIO); + ASSERT_EQ(failed_client3.get(), nullptr); +} + +TEST_F(ClientChannelTest, IsConnected) { + EXPECT_TRUE(client_->IsConnected()); + EXPECT_EQ(0, client_->error()); + client_->Close(-EINVAL); + EXPECT_FALSE(client_->IsConnected()); + EXPECT_EQ(-EINVAL, client_->error()); +} + +TEST_F(ClientChannelTest, event_fd) { + EXPECT_CALL(*mock_channel(), event_fd()).WillOnce(Return(12)); + EXPECT_EQ(12, client_->event_fd()); +} + +TEST_F(ClientChannelTest, SendImpulse) { + EXPECT_CALL(*mock_channel(), SendImpulse(123, nullptr, 0)) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(client_->SendImpulse(123)); + + EXPECT_CALL(*mock_channel(), SendImpulse(17, nullptr, 0)) + .WillOnce(Return(ErrorStatus{EIO})); + auto status = client_->SendImpulse(17); + ASSERT_FALSE(status); + EXPECT_EQ(EIO, status.error()); + + const void* const kTestPtr = IntToConstPtr(1234); + EXPECT_CALL(*mock_channel(), SendImpulse(1, kTestPtr, 17)) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(client_->SendImpulse(1, kTestPtr, 17)); +} + +TEST_F(ClientChannelTest, InvokeRemoteMethodNullTransactionState) { + EXPECT_CALL(*mock_channel(), AllocateTransactionState()) + .WillOnce(Return(nullptr)); + EXPECT_CALL(*mock_channel(), + SendWithInt(nullptr, TestInterface::kOpAdd, _, _, nullptr, 0)) + .WillOnce(Return(9)); + EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr)); + EXPECT_TRUE(client_->InvokeRemoteMethod<TestInterface::Add>(4, 5)); +} + +TEST_F(ClientChannelTest, InvokeRemoteMethodAddSuccess) { + void* const kTransactionState = IntToPtr(123); + EXPECT_CALL(*mock_channel(), AllocateTransactionState()) + .WillOnce(Return(kTransactionState)); + EXPECT_CALL( + *mock_channel(), + SendWithInt(kTransactionState, TestInterface::kOpAdd, _, _, nullptr, 0)) + .WillOnce(Return(3)); + EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); + Status<int> status = client_->InvokeRemoteMethod<TestInterface::Add>(1, 2); + ASSERT_TRUE(status); + EXPECT_EQ(3, status.get()); +} + +TEST_F(ClientChannelTest, InvokeRemoteMethodAddFailure) { + void* const kTransactionState = IntToPtr(123); + EXPECT_CALL(*mock_channel(), AllocateTransactionState()) + .WillOnce(Return(kTransactionState)); + EXPECT_CALL( + *mock_channel(), + SendWithInt(kTransactionState, TestInterface::kOpAdd, _, _, nullptr, 0)) + .WillOnce(Return(ErrorStatus{EIO})); + EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); + Status<int> status = client_->InvokeRemoteMethod<TestInterface::Add>(1, 2); + ASSERT_FALSE(status); + EXPECT_EQ(EIO, status.error()); +} + +TEST_F(ClientChannelTest, InvokeRemoteMethodGetFileSuccess) { + void* const kTransactionState = IntToPtr(123); + int fd = eventfd(0, 0); + EXPECT_CALL(*mock_channel(), AllocateTransactionState()) + .WillOnce(Return(kTransactionState)); + EXPECT_CALL(*mock_channel(), + SendWithFileHandle(kTransactionState, TestInterface::kOpGetFile, + _, _, nullptr, 0)) + .WillOnce(Return(ByMove(LocalHandle{fd}))); + EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); + Status<LocalHandle> status = + client_->InvokeRemoteMethod<TestInterface::GetFile>(); + ASSERT_TRUE(status); + EXPECT_EQ(fd, status.get().Get()); +} + +TEST_F(ClientChannelTest, InvokeRemoteMethodGetFileFailure) { + void* const kTransactionState = IntToPtr(123); + EXPECT_CALL(*mock_channel(), AllocateTransactionState()) + .WillOnce(Return(kTransactionState)); + EXPECT_CALL(*mock_channel(), + SendWithFileHandle(kTransactionState, TestInterface::kOpGetFile, + _, _, nullptr, 0)) + .WillOnce(Return(ByMove(ErrorStatus{EACCES}))); + EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); + Status<LocalHandle> status = + client_->InvokeRemoteMethod<TestInterface::GetFile>("file", 0); + ASSERT_FALSE(status); + EXPECT_EQ(EACCES, status.error()); +} + +TEST_F(ClientChannelTest, InvokeRemoteMethodPushChannelSuccess) { + void* const kTransactionState = IntToPtr(123); + const int32_t kHandleValue = 17; + EXPECT_CALL(*mock_channel(), AllocateTransactionState()) + .WillOnce(Return(kTransactionState)); + EXPECT_CALL( + *mock_channel(), + SendWithChannelHandle(kTransactionState, TestInterface::kOpPushChannel, _, + _, nullptr, 0)) + .WillOnce(Return(ByMove(LocalChannelHandle{nullptr, kHandleValue}))); + EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); + Status<LocalChannelHandle> status = + client_->InvokeRemoteMethod<TestInterface::PushChannel>(); + ASSERT_TRUE(status); + EXPECT_EQ(kHandleValue, status.get().value()); +} + +TEST_F(ClientChannelTest, InvokeRemoteMethodPushChannelFailure) { + void* const kTransactionState = IntToPtr(123); + EXPECT_CALL(*mock_channel(), AllocateTransactionState()) + .WillOnce(Return(kTransactionState)); + EXPECT_CALL( + *mock_channel(), + SendWithChannelHandle(kTransactionState, TestInterface::kOpPushChannel, _, + _, nullptr, 0)) + .WillOnce(Return(ByMove(ErrorStatus{EACCES}))); + EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); + Status<LocalChannelHandle> status = + client_->InvokeRemoteMethod<TestInterface::PushChannel>(); + ASSERT_FALSE(status); + EXPECT_EQ(EACCES, status.error()); +} + +TEST_F(ClientChannelTest, InvokeRemoteMethodSendFileSuccess) { + void* const kTransactionState = IntToPtr(123); + LocalHandle fd{eventfd(0, 0)}; + EXPECT_CALL(*mock_channel(), AllocateTransactionState()) + .WillOnce(Return(kTransactionState)); + EXPECT_CALL(*mock_channel(), + PushFileHandle(kTransactionState, A<const LocalHandle&>())) + .WillOnce(Return(1)); + EXPECT_CALL(*mock_channel(), + SendWithInt(kTransactionState, TestInterface::kOpSendFile, _, _, + nullptr, 0)) + .WillOnce(Return(0)); + EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); + EXPECT_TRUE(client_->InvokeRemoteMethod<TestInterface::SendFile>(fd)); +} + +TEST_F(ClientChannelTest, InvokeRemoteMethodSendFileFailure) { + void* const kTransactionState = IntToPtr(123); + LocalHandle fd{eventfd(0, 0)}; + EXPECT_CALL(*mock_channel(), AllocateTransactionState()) + .WillOnce(Return(kTransactionState)); + EXPECT_CALL(*mock_channel(), + PushFileHandle(kTransactionState, A<const LocalHandle&>())) + .WillOnce(Return(1)); + EXPECT_CALL(*mock_channel(), + SendWithInt(kTransactionState, TestInterface::kOpSendFile, _, _, + nullptr, 0)) + .WillOnce(Return(ErrorStatus{EACCES})); + EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); + EXPECT_FALSE(client_->InvokeRemoteMethod<TestInterface::SendFile>(fd)); +} + +TEST_F(ClientChannelFactoryTest, IsInitialized) { + ASSERT_NE(client_.get(), nullptr); + EXPECT_TRUE(client_->IsInitialized()); + EXPECT_TRUE(client_->IsConnected()); +} + +TEST_F(ClientChannelFactoryTest, NotConnectedButInitialized) { + auto factory = + std::make_unique<testing::NiceMock<MockClientChannelFactory>>(); + EXPECT_CALL(*factory, Connect(kTimeout)) + .WillOnce(Return(ByMove(ErrorStatus(ESHUTDOWN)))) + .WillOnce(Invoke(this, &ClientChannelFactoryTest::OnConnect)); + client_ = SimpleClient::Create(std::move(factory), kTimeout); + ASSERT_NE(client_.get(), nullptr); + EXPECT_TRUE(client_->IsInitialized()); + EXPECT_FALSE(client_->IsConnected()); + client_->DisableAutoReconnect(); + ASSERT_FALSE(client_->SendImpulse(17)); + EXPECT_FALSE(client_->IsConnected()); + client_->EnableAutoReconnect(kTimeout); + EXPECT_CALL(*client_, OnConnect()); + OnConnectCallback([](auto* mock) { + EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0)) + .WillOnce(Return(Status<void>{})); + }); + ASSERT_TRUE(client_->SendImpulse(17)); + EXPECT_TRUE(client_->IsConnected()); +} + +TEST_F(ClientChannelFactoryTest, CheckDisconnect) { + EXPECT_CALL(*mock_channel(), SendImpulse(17, nullptr, 0)) + .WillOnce(Return(ErrorStatus{ESHUTDOWN})); + ASSERT_FALSE(client_->SendImpulse(17)); + EXPECT_FALSE(client_->IsConnected()); + EXPECT_EQ(-ESHUTDOWN, client_->error()); +} + +TEST_F(ClientChannelFactoryTest, CheckReconnect) { + client_->Close(ESHUTDOWN); + ASSERT_FALSE(client_->IsConnected()); + + EXPECT_CALL(*client_, OnConnect()); + OnConnectCallback([](auto* mock) { + EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0)) + .WillOnce(Return(Status<void>{})); + }); + ASSERT_TRUE(client_->SendImpulse(17)); + EXPECT_TRUE(client_->IsConnected()); +} + +TEST_F(ClientChannelFactoryTest, CloseOnConnect) { + client_->Close(ESHUTDOWN); + + EXPECT_CALL(*client_, OnConnect()).WillOnce(Invoke([this] { + client_->Close(EIO); + })); + auto status = client_->SendImpulse(17); + ASSERT_FALSE(status); + EXPECT_EQ(EIO, status.error()); + EXPECT_FALSE(client_->IsConnected()); + EXPECT_EQ(-EIO, client_->error()); +} + +TEST_F(ClientChannelFactoryTest, DisableAutoReconnect) { + client_->Close(EIO); + ASSERT_FALSE(client_->IsConnected()); + client_->DisableAutoReconnect(); + auto status = client_->SendImpulse(17); + ASSERT_FALSE(status); + EXPECT_EQ(ESHUTDOWN, status.error()); + EXPECT_FALSE(client_->IsConnected()); + client_->EnableAutoReconnect(kTimeout); + EXPECT_CALL(*client_, OnConnect()); + OnConnectCallback([](auto* mock) { + EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0)) + .WillOnce(Return(Status<void>{})); + }); + ASSERT_TRUE(client_->SendImpulse(17)); + EXPECT_TRUE(client_->IsConnected()); +} + +TEST_F(ClientTransactionTest, SendNoData) { + void* const kTransactionState = IntToPtr(123); + EXPECT_CALL(*mock_channel(), AllocateTransactionState()) + .WillOnce(Return(kTransactionState)); + EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); + EXPECT_CALL(*mock_channel(), + SendWithInt(kTransactionState, 1, nullptr, 0, nullptr, 0)) + .WillOnce(Return(0)); + EXPECT_TRUE(transaction_.Send<void>(1)); + EXPECT_CALL(*mock_channel(), + SendWithFileHandle(kTransactionState, 2, nullptr, 0, nullptr, 0)) + .WillOnce(Return(ByMove(LocalHandle{-1}))); + EXPECT_TRUE(transaction_.Send<LocalHandle>(2)); + EXPECT_CALL(*mock_channel(), SendWithChannelHandle(kTransactionState, 3, + nullptr, 0, nullptr, 0)) + .WillOnce(Return(ByMove(LocalChannelHandle{nullptr, 1}))); + EXPECT_TRUE(transaction_.Send<LocalChannelHandle>(3)); +} + +TEST_F(ClientTransactionTest, SendNoState) { + EXPECT_CALL(*mock_channel(), AllocateTransactionState()) + .WillOnce(Return(nullptr)); + EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0)) + .WillOnce(Return(0)); + EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr)); + EXPECT_TRUE(transaction_.Send<void>(1)); +} + +TEST_F(ClientTransactionTest, SendBuffers) { + const void* const kSendBuffer = IntToConstPtr(123); + const size_t kSendSize = 12; + void* const kReceiveBuffer = IntToPtr(456); + const size_t kReceiveSize = 34; + + EXPECT_CALL(*mock_channel(), AllocateTransactionState()) + .WillOnce(Return(nullptr)); + EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr)); + + EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0)) + .WillOnce(Return(0)); + EXPECT_TRUE(transaction_.Send<void>(1, nullptr, 0, nullptr, 0)); + + EXPECT_CALL(*mock_channel(), + SendWithInt(nullptr, 2, Ne(nullptr), 1, nullptr, 0)) + .WillOnce(Return(0)); + EXPECT_TRUE(transaction_.Send<void>(2, kSendBuffer, kSendSize, nullptr, 0)); + + EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 3, nullptr, 0, nullptr, 0)) + .WillOnce(Return(0)); + EXPECT_TRUE(transaction_.Send<void>(3, kSendBuffer, 0, nullptr, 0)); + + EXPECT_CALL(*mock_channel(), + SendWithInt(nullptr, 4, nullptr, 0, Ne(nullptr), 1)) + .WillOnce(Return(0)); + EXPECT_TRUE( + transaction_.Send<void>(4, nullptr, 0, kReceiveBuffer, kReceiveSize)); + + EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 5, nullptr, 0, nullptr, 0)) + .WillOnce(Return(0)); + EXPECT_TRUE(transaction_.Send<void>(5, nullptr, 0, kReceiveBuffer, 0)); + + EXPECT_CALL(*mock_channel(), + SendWithInt(nullptr, 5, Ne(nullptr), 1, Ne(nullptr), 1)) + .WillOnce(Return(0)); + EXPECT_TRUE(transaction_.Send<void>(5, kSendBuffer, kSendSize, kReceiveBuffer, + kReceiveSize)); +} + +TEST_F(ClientTransactionTest, SendVector) { + iovec send[3] = {}; + iovec recv[4] = {}; + + EXPECT_CALL(*mock_channel(), AllocateTransactionState()) + .WillOnce(Return(nullptr)); + EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr)); + + EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0)) + .WillOnce(Return(0)); + EXPECT_TRUE(transaction_.SendVector<void>(1, nullptr, 0, nullptr, 0)); + + EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 2, send, 3, recv, 4)) + .WillOnce(Return(0)); + EXPECT_TRUE(transaction_.SendVector<void>(2, send, 3, recv, 4)); + + EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 3, send, 3, nullptr, 0)) + .WillOnce(Return(0)); + EXPECT_TRUE(transaction_.SendVector<void>(3, send, nullptr)); + + EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 4, nullptr, 0, recv, 4)) + .WillOnce(Return(0)); + EXPECT_TRUE(transaction_.SendVector<void>(4, nullptr, recv)); + + EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 5, send, 3, recv, 4)) + .WillOnce(Return(0)); + EXPECT_TRUE(transaction_.SendVector<void>(5, send, recv)); +} + +TEST_F(ClientTransactionTest, PushHandle) { + void* const kTransactionState = IntToPtr(123); + EXPECT_CALL(*mock_channel(), AllocateTransactionState()) + .WillOnce(Return(kTransactionState)); + EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); + + EXPECT_CALL(*mock_channel(), + PushFileHandle(kTransactionState, A<const LocalHandle&>())) + .WillOnce(Return(1)); + EXPECT_EQ(1, transaction_.PushFileHandle(LocalHandle{-1}).get()); + + EXPECT_CALL(*mock_channel(), + PushFileHandle(kTransactionState, A<const BorrowedHandle&>())) + .WillOnce(Return(2)); + EXPECT_EQ(2, transaction_.PushFileHandle(BorrowedHandle{-1}).get()); + + EXPECT_EQ(3, transaction_.PushFileHandle(RemoteHandle{3}).get()); + + EXPECT_CALL( + *mock_channel(), + PushChannelHandle(kTransactionState, A<const LocalChannelHandle&>())) + .WillOnce(Return(11)); + EXPECT_EQ( + 11, transaction_.PushChannelHandle(LocalChannelHandle{nullptr, 1}).get()); + + EXPECT_CALL( + *mock_channel(), + PushChannelHandle(kTransactionState, A<const BorrowedChannelHandle&>())) + .WillOnce(Return(12)); + EXPECT_EQ(12, transaction_.PushChannelHandle(BorrowedChannelHandle{2}).get()); + + EXPECT_EQ(13, transaction_.PushChannelHandle(RemoteChannelHandle{13}).get()); +} + +TEST_F(ClientTransactionTest, GetHandle) { + void* const kTransactionState = IntToPtr(123); + EXPECT_CALL(*mock_channel(), AllocateTransactionState()) + .WillOnce(Return(kTransactionState)); + EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); + + EXPECT_CALL(*mock_channel(), GetFileHandle(kTransactionState, 1, _)) + .WillOnce(Return(false)) + .WillOnce(Return(true)); + + LocalHandle file_handle; + EXPECT_FALSE(transaction_.GetFileHandle(1, &file_handle)); + EXPECT_TRUE(transaction_.GetFileHandle(1, &file_handle)); + + EXPECT_CALL(*mock_channel(), GetChannelHandle(kTransactionState, 2, _)) + .WillOnce(Return(false)) + .WillOnce(Return(true)); + + LocalChannelHandle channel_handle; + EXPECT_FALSE(transaction_.GetChannelHandle(2, &channel_handle)); + EXPECT_TRUE(transaction_.GetChannelHandle(2, &channel_handle)); +} diff --git a/libs/vr/libpdx/encoder_performance_test.cpp b/libs/vr/libpdx/encoder_performance_test.cpp new file mode 100644 index 0000000000..b7d94b3471 --- /dev/null +++ b/libs/vr/libpdx/encoder_performance_test.cpp @@ -0,0 +1,515 @@ +#include <errno.h> +#include <fcntl.h> +#include <time.h> +#include <unistd.h> + +#include <chrono> +#include <iomanip> +#include <iostream> +#include <vector> + +#include <pdx/rpc/argument_encoder.h> +#include <pdx/rpc/message_buffer.h> +#include <pdx/rpc/payload.h> +#include <pdx/utility.h> + +using namespace android::pdx::rpc; +using namespace android::pdx; +using std::placeholders::_1; +using std::placeholders::_2; +using std::placeholders::_3; +using std::placeholders::_4; +using std::placeholders::_5; +using std::placeholders::_6; + +namespace { + +constexpr size_t kMaxStaticBufferSize = 20480; + +// Provide numpunct facet that formats numbers with ',' as thousands separators. +class CommaNumPunct : public std::numpunct<char> { + protected: + char do_thousands_sep() const override { return ','; } + std::string do_grouping() const override { return "\03"; } +}; + +class TestPayload : public MessagePayload<SendBuffer>, + public MessageWriter, + public MessageReader, + public NoOpResourceMapper { + public: + // MessageWriter + void* GetNextWriteBufferSection(size_t size) override { + const size_t section_offset = Size(); + Extend(size); + return Data() + section_offset; + } + + OutputResourceMapper* GetOutputResourceMapper() override { return this; } + + // MessageReader + BufferSection GetNextReadBufferSection() override { + return {&*ConstCursor(), &*ConstEnd()}; + } + + void ConsumeReadBufferSectionData(const void* new_start) override { + std::advance(ConstCursor(), PointerDistance(new_start, &*ConstCursor())); + } + + InputResourceMapper* GetInputResourceMapper() override { return this; } +}; + +class StaticBuffer : public MessageWriter, + public MessageReader, + public NoOpResourceMapper { + public: + void Clear() { + read_ptr_ = buffer_; + write_ptr_ = 0; + } + void Rewind() { read_ptr_ = buffer_; } + + // MessageWriter + void* GetNextWriteBufferSection(size_t size) override { + void* ptr = buffer_ + write_ptr_; + write_ptr_ += size; + return ptr; + } + + OutputResourceMapper* GetOutputResourceMapper() override { return this; } + + // MessageReader + BufferSection GetNextReadBufferSection() override { + return {read_ptr_, std::end(buffer_)}; + } + + void ConsumeReadBufferSectionData(const void* new_start) override { + read_ptr_ = static_cast<const uint8_t*>(new_start); + } + + InputResourceMapper* GetInputResourceMapper() override { return this; } + + private: + uint8_t buffer_[kMaxStaticBufferSize]; + const uint8_t* read_ptr_{buffer_}; + size_t write_ptr_{0}; +}; + +// Simple callback function to clear/reset the input/output buffers for +// serialization. Using raw function pointer here instead of std::function to +// minimize the overhead of invocation in the tight test loop over millions of +// iterations. +using ResetFunc = void(void*); + +// Serialization test function signature, used by the TestRunner. +using SerializeTestSignature = std::chrono::nanoseconds(MessageWriter* writer, + size_t iterations, + ResetFunc* write_reset, + void* reset_data); + +// Deserialization test function signature, used by the TestRunner. +using DeserializeTestSignature = std::chrono::nanoseconds( + MessageReader* reader, MessageWriter* writer, size_t iterations, + ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data); + +// Generic serialization test runner method. Takes the |value| of type T and +// serializes it into the output buffer represented by |writer|. +template <typename T> +std::chrono::nanoseconds SerializeTestRunner(MessageWriter* writer, + size_t iterations, + ResetFunc* write_reset, + void* reset_data, const T& value) { + auto start = std::chrono::high_resolution_clock::now(); + for (size_t i = 0; i < iterations; i++) { + write_reset(reset_data); + Serialize(value, writer); + } + auto stop = std::chrono::high_resolution_clock::now(); + return stop - start; +} + +// Generic deserialization test runner method. Takes the |value| of type T and +// temporarily serializes it into the output buffer, then repeatedly +// deserializes the data back from that buffer. +template <typename T> +std::chrono::nanoseconds DeserializeTestRunner( + MessageReader* reader, MessageWriter* writer, size_t iterations, + ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data, + const T& value) { + write_reset(reset_data); + Serialize(value, writer); + T output_data; + auto start = std::chrono::high_resolution_clock::now(); + for (size_t i = 0; i < iterations; i++) { + read_reset(reset_data); + Deserialize(&output_data, reader); + } + auto stop = std::chrono::high_resolution_clock::now(); + if (output_data != value) + return start - stop; // Return negative value to indicate error. + return stop - start; +} + +// Special version of SerializeTestRunner that doesn't perform any serialization +// but does all the same setup steps and moves data of size |data_size| into +// the output buffer. Useful to determine the baseline to calculate time used +// just for serialization layer. +std::chrono::nanoseconds SerializeBaseTest(MessageWriter* writer, + size_t iterations, + ResetFunc* write_reset, + void* reset_data, size_t data_size) { + std::vector<uint8_t> dummy_data(data_size); + auto start = std::chrono::high_resolution_clock::now(); + for (size_t i = 0; i < iterations; i++) { + write_reset(reset_data); + memcpy(writer->GetNextWriteBufferSection(dummy_data.size()), + dummy_data.data(), dummy_data.size()); + } + auto stop = std::chrono::high_resolution_clock::now(); + return stop - start; +} + +// Special version of DeserializeTestRunner that doesn't perform any +// deserialization but invokes Rewind on the input buffer repeatedly. +// Useful to determine the baseline to calculate time used just for +// deserialization layer. +std::chrono::nanoseconds DeserializeBaseTest( + MessageReader* reader, MessageWriter* writer, size_t iterations, + ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data, + size_t data_size) { + std::vector<uint8_t> dummy_data(data_size); + write_reset(reset_data); + memcpy(writer->GetNextWriteBufferSection(dummy_data.size()), + dummy_data.data(), dummy_data.size()); + auto start = std::chrono::high_resolution_clock::now(); + for (size_t i = 0; i < iterations; i++) { + read_reset(reset_data); + auto section = reader->GetNextReadBufferSection(); + memcpy(dummy_data.data(), section.first, dummy_data.size()); + reader->ConsumeReadBufferSectionData( + AdvancePointer(section.first, dummy_data.size())); + } + auto stop = std::chrono::high_resolution_clock::now(); + return stop - start; +} + +// The main class that accumulates individual tests to be executed. +class TestRunner { + public: + struct BufferInfo { + BufferInfo(const std::string& buffer_name, MessageReader* reader, + MessageWriter* writer, ResetFunc* read_reset_func, + ResetFunc* write_reset_func, void* reset_data) + : name{buffer_name}, + reader{reader}, + writer{writer}, + read_reset_func{read_reset_func}, + write_reset_func{write_reset_func}, + reset_data{reset_data} {} + std::string name; + MessageReader* reader; + MessageWriter* writer; + ResetFunc* read_reset_func; + ResetFunc* write_reset_func; + void* reset_data; + }; + + void AddTestFunc(const std::string& name, + std::function<SerializeTestSignature> serialize_test, + std::function<DeserializeTestSignature> deserialize_test, + size_t data_size) { + tests_.emplace_back(name, std::move(serialize_test), + std::move(deserialize_test), data_size); + } + + template <typename T> + void AddSerializationTest(const std::string& name, T&& value) { + const size_t data_size = GetSerializedSize(value); + auto serialize_test = + std::bind(static_cast<std::chrono::nanoseconds (*)( + MessageWriter*, size_t, ResetFunc*, void*, const T&)>( + &SerializeTestRunner), + _1, _2, _3, _4, std::forward<T>(value)); + tests_.emplace_back(name, std::move(serialize_test), + std::function<DeserializeTestSignature>{}, data_size); + } + + template <typename T> + void AddDeserializationTest(const std::string& name, T&& value) { + const size_t data_size = GetSerializedSize(value); + auto deserialize_test = + std::bind(static_cast<std::chrono::nanoseconds (*)( + MessageReader*, MessageWriter*, size_t, ResetFunc*, + ResetFunc*, void*, const T&)>(&DeserializeTestRunner), + _1, _2, _3, _4, _5, _6, std::forward<T>(value)); + tests_.emplace_back(name, std::function<SerializeTestSignature>{}, + std::move(deserialize_test), data_size); + } + + template <typename T> + void AddTest(const std::string& name, T&& value) { + const size_t data_size = GetSerializedSize(value); + if (data_size > kMaxStaticBufferSize) { + std::cerr << "Test '" << name << "' requires " << data_size + << " bytes in the serialization buffer but only " + << kMaxStaticBufferSize << " are available." << std::endl; + exit(1); + } + auto serialize_test = + std::bind(static_cast<std::chrono::nanoseconds (*)( + MessageWriter*, size_t, ResetFunc*, void*, const T&)>( + &SerializeTestRunner), + _1, _2, _3, _4, value); + auto deserialize_test = + std::bind(static_cast<std::chrono::nanoseconds (*)( + MessageReader*, MessageWriter*, size_t, ResetFunc*, + ResetFunc*, void*, const T&)>(&DeserializeTestRunner), + _1, _2, _3, _4, _5, _6, std::forward<T>(value)); + tests_.emplace_back(name, std::move(serialize_test), + std::move(deserialize_test), data_size); + } + + std::string CenterString(std::string text, size_t column_width) { + if (text.size() < column_width) { + text = std::string((column_width - text.size()) / 2, ' ') + text; + } + return text; + } + + void RunTests(size_t iteration_count, + const std::vector<BufferInfo>& buffers) { + using float_seconds = std::chrono::duration<double>; + const std::string name_column_separator = " : "; + const std::string buffer_column_separator = " || "; + const std::string buffer_timing_column_separator = " | "; + const size_t data_size_column_width = 6; + const size_t time_column_width = 9; + const size_t qps_column_width = 18; + const size_t buffer_column_width = time_column_width + + buffer_timing_column_separator.size() + + qps_column_width; + + auto compare_name_length = [](const TestEntry& t1, const TestEntry& t2) { + return t1.name.size() < t2.name.size(); + }; + auto test_with_longest_name = + std::max_element(tests_.begin(), tests_.end(), compare_name_length); + size_t name_column_width = test_with_longest_name->name.size(); + + size_t total_width = + name_column_width + name_column_separator.size() + + data_size_column_width + buffer_column_separator.size() + + buffers.size() * (buffer_column_width + buffer_column_separator.size()); + + const std::string dbl_separator(total_width, '='); + const std::string separator(total_width, '-'); + + auto print_header = [&](const std::string& header) { + std::cout << dbl_separator << std::endl; + std::stringstream ss; + ss.imbue(std::locale(ss.getloc(), new CommaNumPunct)); + ss << header << " (" << iteration_count << " iterations)"; + std::cout << CenterString(ss.str(), total_width) << std::endl; + std::cout << dbl_separator << std::endl; + std::cout << std::setw(name_column_width) << "Test Name" << std::left + << name_column_separator << std::setw(data_size_column_width) + << CenterString("Size", data_size_column_width) + << buffer_column_separator; + for (const auto& buffer_info : buffers) { + std::cout << std::setw(buffer_column_width) + << CenterString(buffer_info.name, buffer_column_width) + << buffer_column_separator; + } + std::cout << std::endl; + std::cout << std::setw(name_column_width) << "" << name_column_separator + << std::setw(data_size_column_width) + << CenterString("bytes", data_size_column_width) + << buffer_column_separator << std::left; + for (size_t i = 0; i < buffers.size(); i++) { + std::cout << std::setw(time_column_width) + << CenterString("Time, s", time_column_width) + << buffer_timing_column_separator + << std::setw(qps_column_width) + << CenterString("QPS", qps_column_width) + << buffer_column_separator; + } + std::cout << std::right << std::endl; + std::cout << separator << std::endl; + }; + + print_header("Serialization benchmarks"); + for (const auto& test : tests_) { + if (test.serialize_test) { + std::cout << std::setw(name_column_width) << test.name << " : " + << std::setw(data_size_column_width) << test.data_size + << buffer_column_separator; + for (const auto& buffer_info : buffers) { + auto seconds = + std::chrono::duration_cast<float_seconds>(test.serialize_test( + buffer_info.writer, iteration_count, + buffer_info.write_reset_func, buffer_info.reset_data)); + double qps = iteration_count / seconds.count(); + std::cout << std::fixed << std::setprecision(3) + << std::setw(time_column_width) << seconds.count() + << buffer_timing_column_separator + << std::setw(qps_column_width) << qps + << buffer_column_separator; + } + std::cout << std::endl; + } + } + + print_header("Deserialization benchmarks"); + for (const auto& test : tests_) { + if (test.deserialize_test) { + std::cout << std::setw(name_column_width) << test.name << " : " + << std::setw(data_size_column_width) << test.data_size + << buffer_column_separator; + for (const auto& buffer_info : buffers) { + auto seconds = + std::chrono::duration_cast<float_seconds>(test.deserialize_test( + buffer_info.reader, buffer_info.writer, iteration_count, + buffer_info.read_reset_func, buffer_info.write_reset_func, + buffer_info.reset_data)); + double qps = iteration_count / seconds.count(); + std::cout << std::fixed << std::setprecision(3) + << std::setw(time_column_width) << seconds.count() + << buffer_timing_column_separator + << std::setw(qps_column_width) << qps + << buffer_column_separator; + } + std::cout << std::endl; + } + } + std::cout << dbl_separator << std::endl; + } + + private: + struct TestEntry { + TestEntry(const std::string& test_name, + std::function<SerializeTestSignature> serialize_test, + std::function<DeserializeTestSignature> deserialize_test, + size_t data_size) + : name{test_name}, + serialize_test{std::move(serialize_test)}, + deserialize_test{std::move(deserialize_test)}, + data_size{data_size} {} + std::string name; + std::function<SerializeTestSignature> serialize_test; + std::function<DeserializeTestSignature> deserialize_test; + size_t data_size; + }; + + std::vector<TestEntry> tests_; +}; + +std::string GenerateContainerName(const std::string& type, size_t count) { + std::stringstream ss; + ss << type << "(" << count << ")"; + return ss.str(); +} + +} // anonymous namespace + +int main(int /*argc*/, char** /*argv*/) { + const size_t iteration_count = 10000000; // 10M iterations. + TestRunner test_runner; + std::cout.imbue(std::locale(std::cout.getloc(), new CommaNumPunct)); + + // Baseline tests to figure out the overhead of buffer resizing and data + // transfers. + for (size_t len : {0, 1, 9, 66, 259}) { + auto serialize_base_test = + std::bind(&SerializeBaseTest, _1, _2, _3, _4, len); + auto deserialize_base_test = + std::bind(&DeserializeBaseTest, _1, _2, _3, _4, _5, _6, len); + test_runner.AddTestFunc("--Baseline--", std::move(serialize_base_test), + std::move(deserialize_base_test), len); + } + + // Individual serialization/deserialization tests. + test_runner.AddTest("bool", true); + test_runner.AddTest("int32_t", 12); + + for (size_t len : {0, 1, 8, 64, 256}) { + test_runner.AddTest(GenerateContainerName("string", len), + std::string(len, '*')); + } + // Serialization is too slow to handle such large strings, add this test for + // deserialization only. + test_runner.AddDeserializationTest(GenerateContainerName("string", 10240), + std::string(10240, '*')); + + for (size_t len : {0, 1, 8, 64, 256}) { + std::vector<int32_t> int_vector(len); + std::iota(int_vector.begin(), int_vector.end(), 0); + test_runner.AddTest(GenerateContainerName("vector<int32_t>", len), + std::move(int_vector)); + } + + std::vector<std::string> vector_of_strings = { + "012345678901234567890123456789", "012345678901234567890123456789", + "012345678901234567890123456789", "012345678901234567890123456789", + "012345678901234567890123456789", + }; + test_runner.AddTest( + GenerateContainerName("vector<string>", vector_of_strings.size()), + std::move(vector_of_strings)); + + test_runner.AddTest("tuple<int, bool, string, double>", + std::make_tuple(123, true, std::string{"foobar"}, 1.1)); + + for (size_t len : {0, 1, 8, 64}) { + std::map<int, std::string> test_map; + for (size_t i = 0; i < len; i++) + test_map.emplace(i, std::to_string(i)); + test_runner.AddTest(GenerateContainerName("map<int, string>", len), + std::move(test_map)); + } + + for (size_t len : {0, 1, 8, 64}) { + std::unordered_map<int, std::string> test_map; + for (size_t i = 0; i < len; i++) + test_map.emplace(i, std::to_string(i)); + test_runner.AddTest( + GenerateContainerName("unordered_map<int, string>", len), + std::move(test_map)); + } + + // BufferWrapper can't be used with deserialization tests right now because + // it requires external buffer to be filled in, which is not available. + std::vector<std::vector<uint8_t>> data_buffers; + for (size_t len : {0, 1, 8, 64, 256}) { + data_buffers.emplace_back(len); + test_runner.AddSerializationTest( + GenerateContainerName("BufferWrapper<uint8_t*>", len), + BufferWrapper<uint8_t*>(data_buffers.back().data(), + data_buffers.back().size())); + } + + // Various backing buffers to run the tests on. + std::vector<TestRunner::BufferInfo> buffers; + + Payload buffer; + buffers.emplace_back("Non-TLS Buffer", &buffer, &buffer, + [](void* ptr) { static_cast<Payload*>(ptr)->Rewind(); }, + [](void* ptr) { static_cast<Payload*>(ptr)->Clear(); }, + &buffer); + + TestPayload tls_buffer; + buffers.emplace_back( + "TLS Buffer", &tls_buffer, &tls_buffer, + [](void* ptr) { static_cast<TestPayload*>(ptr)->Rewind(); }, + [](void* ptr) { static_cast<TestPayload*>(ptr)->Clear(); }, &tls_buffer); + + StaticBuffer static_buffer; + buffers.emplace_back( + "Static Buffer", &static_buffer, &static_buffer, + [](void* ptr) { static_cast<StaticBuffer*>(ptr)->Rewind(); }, + [](void* ptr) { static_cast<StaticBuffer*>(ptr)->Clear(); }, + &static_buffer); + + // Finally, run all the tests. + test_runner.RunTests(iteration_count, buffers); + return 0; +} diff --git a/libs/vr/libpdx/mock_tests.cpp b/libs/vr/libpdx/mock_tests.cpp new file mode 100644 index 0000000000..76fd1541a5 --- /dev/null +++ b/libs/vr/libpdx/mock_tests.cpp @@ -0,0 +1,20 @@ +#include <gtest/gtest.h> +#include <pdx/mock_client_channel.h> +#include <pdx/mock_client_channel_factory.h> +#include <pdx/mock_message_reader.h> +#include <pdx/mock_message_writer.h> +#include <pdx/mock_service_dispatcher.h> +#include <pdx/mock_service_endpoint.h> + +TEST(MockTypes, Instantiation) { + // Make sure all our interfaces are mocked out properly and mock instances + // can be created. + android::pdx::MockClientChannel client_channel; + android::pdx::MockClientChannelFactory client_channel_factory; + android::pdx::MockInputResourceMapper input_resource_mapper; + android::pdx::MockMessageReader message_reader; + android::pdx::MockOutputResourceMapper output_resource_mapper; + android::pdx::MockMessageWriter message_writer; + android::pdx::MockServiceDispatcher service_dispatcher; + android::pdx::MockEndpoint endpoint; +} diff --git a/libs/vr/libpdx/private/pdx/channel_handle.h b/libs/vr/libpdx/private/pdx/channel_handle.h new file mode 100644 index 0000000000..1e62d250ec --- /dev/null +++ b/libs/vr/libpdx/private/pdx/channel_handle.h @@ -0,0 +1,123 @@ +#ifndef ANDROID_PDX_CHANNEL_HANDLE_H_ +#define ANDROID_PDX_CHANNEL_HANDLE_H_ + +#include <cstdint> +#include <type_traits> + +namespace android { +namespace pdx { + +enum class ChannelHandleMode { + Local, + Borrowed, + Remote, +}; + +class ChannelManagerInterface { + public: + virtual void CloseHandle(std::int32_t handle) = 0; + + protected: + // Nobody should be allowed to delete the instance of channel manager + // through this interface. + virtual ~ChannelManagerInterface() = default; +}; + +class ChannelHandleBase { + public: + ChannelHandleBase() = default; + ChannelHandleBase(const int32_t& value) : value_{value} {} + + ChannelHandleBase(const ChannelHandleBase&) = delete; + ChannelHandleBase& operator=(const ChannelHandleBase&) = delete; + + std::int32_t value() const { return value_; } + bool valid() const { return value_ >= 0; } + explicit operator bool() const { return valid(); } + + void Close() { value_ = kEmptyHandle; } + + protected: + // Must not be used by itself. Must be derived from. + ~ChannelHandleBase() = default; + enum : std::int32_t { kEmptyHandle = -1 }; + + std::int32_t value_{kEmptyHandle}; +}; + +template <ChannelHandleMode Mode> +class ChannelHandle : public ChannelHandleBase { + public: + ChannelHandle() = default; + using ChannelHandleBase::ChannelHandleBase; + ChannelHandle(ChannelHandle&& other) : ChannelHandleBase{other.value_} { + other.value_ = kEmptyHandle; + } + ~ChannelHandle() = default; + + ChannelHandle Duplicate() const { return ChannelHandle{value_}; } + + ChannelHandle& operator=(ChannelHandle&& other) { + value_ = other.value_; + other.value_ = kEmptyHandle; + return *this; + } +}; + +template <> +class ChannelHandle<ChannelHandleMode::Local> : public ChannelHandleBase { + public: + ChannelHandle() = default; + ChannelHandle(ChannelManagerInterface* manager, int32_t value) + : ChannelHandleBase{value}, manager_{manager} {} + + ChannelHandle(const ChannelHandle&) = delete; + ChannelHandle& operator=(const ChannelHandle&) = delete; + + ChannelHandle(ChannelHandle&& other) + : ChannelHandleBase{other.value_}, manager_{other.manager_} { + other.manager_ = nullptr; + other.value_ = kEmptyHandle; + } + + ChannelHandle& operator=(ChannelHandle&& other) { + value_ = other.value_; + manager_ = other.manager_; + other.value_ = kEmptyHandle; + other.manager_ = nullptr; + return *this; + } + + ~ChannelHandle() { + if (manager_) + manager_->CloseHandle(value_); + } + + ChannelHandle<ChannelHandleMode::Borrowed> Borrow() const { + return ChannelHandle<ChannelHandleMode::Borrowed>{value_}; + } + + void Close() { + if (manager_) + manager_->CloseHandle(value_); + manager_ = nullptr; + value_ = kEmptyHandle; + } + + private: + ChannelManagerInterface* manager_{nullptr}; +}; + +using LocalChannelHandle = ChannelHandle<ChannelHandleMode::Local>; +using BorrowedChannelHandle = ChannelHandle<ChannelHandleMode::Borrowed>; +using RemoteChannelHandle = ChannelHandle<ChannelHandleMode::Remote>; + +// ChannelReference is a 32 bit integer used in IPC serialization to be +// transferred across processes. You can convert this value to a local channel +// handle by calling Transaction.GetChannelHandle(). +using ChannelReference = int32_t; + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_CHANNEL_HANDLE_H_ diff --git a/libs/vr/libpdx/private/pdx/client.h b/libs/vr/libpdx/private/pdx/client.h new file mode 100644 index 0000000000..656de7e2ca --- /dev/null +++ b/libs/vr/libpdx/private/pdx/client.h @@ -0,0 +1,301 @@ +#ifndef ANDROID_PDX_CLIENT_H_ +#define ANDROID_PDX_CLIENT_H_ + +#include <errno.h> +#include <sys/types.h> + +#include <memory> +#include <string> +#include <type_traits> + +#include <pdx/channel_handle.h> +#include <pdx/client_channel.h> +#include <pdx/client_channel_factory.h> +#include <pdx/file_handle.h> +#include <pdx/message_reader.h> +#include <pdx/message_writer.h> +#include <pdx/rpc/remote_method_type.h> +#include <pdx/status.h> + +namespace android { +namespace pdx { + +class Transaction; + +/* + * Base class of client-side service API classes. + */ +class Client { + public: + static const int64_t kInfiniteTimeout = -1; + + virtual ~Client() = default; + + /* + * Returns true if the Client instance successfully initialized, false + * otherwise. Subclasses that can fail to initialize must override this and + * AND their initialization result with this base class method's result. + * + * This method is not intended to perform initialization, only to report + * the status of the initialization. + */ + virtual bool IsInitialized() const; + + /* + * Returns the error code describing the Client initialization failure, or 0 + * if there was no failure. + */ + int error() const; + + // Returns a reference to IPC channel handle. + LocalChannelHandle& GetChannelHandle(); + + protected: + friend Transaction; + explicit Client(std::unique_ptr<ClientChannel> channel); + explicit Client(std::unique_ptr<ClientChannelFactory> channel_factory, + int64_t timeout_ms = kInfiniteTimeout); + + /* + * Called by Client::Connect() after successfully connecting to the service + * endpoint. Subclasses may override this method to perform additional setup, + * including sending messages to complete the connection process. + * + * Subclasses may call Client::Close() within this method to terminate the + * connection; Client::Connect() returns the negated error passed to + * Client::Close() when this happens. + */ + virtual void OnConnect(); + + enum : size_t { MAX_IMPULSE_LENGTH = sizeof(uint64_t) * 4 }; + + Status<void> SendImpulse(int opcode); + Status<void> SendImpulse(int opcode, const void* buffer, size_t length); + + /* + * Remote method call API using pdx::rpc serialization. + * Include pdx/rpc/remote_method.h to use these methods. + */ + template <typename RemoteMethodType, typename... Args> + Status<typename RemoteMethodType::Return> InvokeRemoteMethod(Args&&... args); + + template <typename RemoteMethodType, typename ReturnType, typename... Args> + Status<void> InvokeRemoteMethodInPlace(ReturnType* return_value, + Args&&... args); + + /* + * Close the endpoint file descriptor and optionally indicate an error, which + * may be retrieved through error(). Subclasses may use this in their + * constructor to signal failure during initialization or at other times + * during operation. + */ + void Close(int error); + + /* + * Returns true if the client is connected to the service, false otherwise. + */ + bool IsConnected() const; + + /* + * Enables auto-reconnect with the given timeout. Use kInfiniteTimeout (-1) + * for no timeout. Auto-reconnect can only be enabled if the Client class + * was constructed with a ClientChannelFactory. + */ + void EnableAutoReconnect(int64_t reconnect_timeout_ms); + + /* + * Disables auto-reconnect. + */ + void DisableAutoReconnect(); + + /* + * Returns an fd that the client may use to check/wait for asynchronous + * notifications to the channel. It is implementation dependent how the + * transport backend handles this feature, however all implementations must + * support at least POLLIN/EPOLLIN/readable. + * + * For uses that require more than one type of event, use + * ClientChannel::GetEventMask() to distinguish between events. + */ + int event_fd() const; + + /* + * Returns the underlying ClientChannel object. + */ + ClientChannel* GetChannel() const { return channel_.get(); } + std::unique_ptr<ClientChannel>&& TakeChannel() { return std::move(channel_); } + + private: + Client(const Client&) = delete; + void operator=(const Client&) = delete; + + Status<void> CheckReconnect(); + bool NeedToDisconnectChannel(int error) const; + void CheckDisconnect(int error); + + template <typename T> + inline void CheckDisconnect(const Status<T>& status) { + if (!status) + CheckDisconnect(status.error()); + } + + std::unique_ptr<ClientChannel> channel_; + int error_{0}; + + // Reconnection state. + std::unique_ptr<ClientChannelFactory> channel_factory_; + int64_t reconnect_timeout_ms_{0}; + bool auto_reconnect_enabled_{false}; +}; + +/* + * Utility template base class for client-side service API classes. Handles + * initialization checks during allocation and automatically cleans up on + * failure. + * + * @tparam T Type of the class extending this one. + * @tparam C Client class to wrap. Defaults to the Client class. + */ +template <typename T, typename ParentClient = Client> +class ClientBase : public ParentClient { + public: + // Type of the client this class wraps. + using ClientType = ParentClient; + + static_assert(std::is_base_of<Client, ParentClient>::value, + "The provided parent client is not a Client subclass."); + + /* + * Allocates a new instance of the superclass and checks for successful + * initialization. + * + * The variadic arguments must expand to match one of type T's constructors + * and are passed through unchanged. If a timeout is desired, subclasses are + * responsible for passing this through to the appropriate ClientBase + * constructor. + * + * Returns a unique_ptr to the new instance on success, or an empty unique_ptr + * otherwise. + */ + template <typename... Args> + static inline std::unique_ptr<T> Create(Args&&... args) { + std::unique_ptr<T> client(new T(std::forward<Args>(args)...)); + if (client->IsInitialized()) + return client; + else + return nullptr; + } + + protected: + /* + * Type of the base class. Useful for referencing the base class type and + * constructor in subclasses. Subclasses with non-public constructors + * must declare BASE a friend. + */ + using BASE = ClientBase<T, ParentClient>; + + /* + * Type of the unique_ptr deleter. Useful for friend declarations. + */ + using deleter_type = typename std::unique_ptr<T>::deleter_type; + + using ParentClient::ParentClient; +}; + +class Transaction final : public OutputResourceMapper, + public InputResourceMapper { + public: + Transaction(Client& client); + ~Transaction(); + + template <typename T> + Status<T> Send(int opcode) { + return SendVector<T>(opcode, nullptr, 0, nullptr, 0); + } + + template <typename T> + Status<T> Send(int opcode, const void* send_buffer, size_t send_length, + void* receive_buffer, size_t receive_length) { + const bool send = (send_buffer && send_length); + const bool receive = (receive_buffer && receive_length); + const iovec send_vector = {const_cast<void*>(send_buffer), send_length}; + const iovec receive_vector = {receive_buffer, receive_length}; + return SendVector<T>(opcode, send ? &send_vector : nullptr, send ? 1 : 0, + receive ? &receive_vector : nullptr, receive ? 1 : 0); + } + + template <typename T> + Status<T> SendVector(int opcode, const iovec* send_vector, size_t send_count, + const iovec* receive_vector, size_t receive_count) { + Status<T> ret; + SendTransaction(opcode, &ret, send_vector, send_count, receive_vector, + receive_count); + return ret; + } + + template <typename T, size_t send_count, size_t receive_count> + Status<T> SendVector(int opcode, const iovec (&send_vector)[send_count], + const iovec (&receive_vector)[receive_count]) { + return SendVector<T>(opcode, send_vector, send_count, receive_vector, + receive_count); + } + + template <typename T, size_t send_count> + Status<T> SendVector(int opcode, const iovec (&send_vector)[send_count], + std::nullptr_t) { + return SendVector<T>(opcode, send_vector, send_count, nullptr, 0); + } + + template <typename T, size_t receive_count> + Status<T> SendVector(int opcode, std::nullptr_t, + const iovec (&receive_vector)[receive_count]) { + return SendVector<T>(opcode, nullptr, 0, receive_vector, receive_count); + } + + // OutputResourceMapper + Status<FileReference> PushFileHandle(const LocalHandle& handle) override; + Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override; + Status<FileReference> PushFileHandle(const RemoteHandle& handle) override; + Status<ChannelReference> PushChannelHandle( + const LocalChannelHandle& handle) override; + Status<ChannelReference> PushChannelHandle( + const BorrowedChannelHandle& handle) override; + Status<ChannelReference> PushChannelHandle( + const RemoteChannelHandle& handle) override; + + // InputResourceMapper + bool GetFileHandle(FileReference ref, LocalHandle* handle) override; + bool GetChannelHandle(ChannelReference ref, + LocalChannelHandle* handle) override; + + private: + bool EnsureStateAllocated(); + void SendTransaction(int opcode, Status<void>* ret, const iovec* send_vector, + size_t send_count, const iovec* receive_vector, + size_t receive_count); + void SendTransaction(int opcode, Status<int>* ret, const iovec* send_vector, + size_t send_count, const iovec* receive_vector, + size_t receive_count); + void SendTransaction(int opcode, Status<LocalHandle>* ret, + const iovec* send_vector, size_t send_count, + const iovec* receive_vector, size_t receive_count); + void SendTransaction(int opcode, Status<LocalChannelHandle>* ret, + const iovec* send_vector, size_t send_count, + const iovec* receive_vector, size_t receive_count); + void CheckDisconnect(int error); + + template <typename T> + inline void CheckDisconnect(const Status<T>& status) { + if (!status) + CheckDisconnect(status.error()); + } + + Client& client_; + void* state_{nullptr}; + bool state_allocated_{false}; +}; + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_CLIENT_H_ diff --git a/libs/vr/libpdx/private/pdx/client_channel.h b/libs/vr/libpdx/private/pdx/client_channel.h new file mode 100644 index 0000000000..dbfd626d6f --- /dev/null +++ b/libs/vr/libpdx/private/pdx/client_channel.h @@ -0,0 +1,58 @@ +#ifndef ANDROID_PDX_CLIENT_CHANNEL_H_ +#define ANDROID_PDX_CLIENT_CHANNEL_H_ + +#include <pdx/channel_handle.h> +#include <pdx/file_handle.h> +#include <pdx/status.h> + +struct iovec; + +namespace android { +namespace pdx { + +class ClientChannel { + public: + virtual ~ClientChannel() = default; + + // Returns a tag that uniquely identifies a specific underlying IPC transport. + virtual uint32_t GetIpcTag() const = 0; + + virtual int event_fd() const = 0; + virtual Status<int> GetEventMask(int events) = 0; + + virtual LocalChannelHandle& GetChannelHandle() = 0; + virtual void* AllocateTransactionState() = 0; + virtual void FreeTransactionState(void* state) = 0; + + virtual Status<void> SendImpulse(int opcode, const void* buffer, + size_t length) = 0; + + virtual Status<int> SendWithInt(void* transaction_state, int opcode, + const iovec* send_vector, size_t send_count, + const iovec* receive_vector, + size_t receive_count) = 0; + virtual Status<LocalHandle> SendWithFileHandle( + void* transaction_state, int opcode, const iovec* send_vector, + size_t send_count, const iovec* receive_vector, size_t receive_count) = 0; + virtual Status<LocalChannelHandle> SendWithChannelHandle( + void* transaction_state, int opcode, const iovec* send_vector, + size_t send_count, const iovec* receive_vector, size_t receive_count) = 0; + + virtual FileReference PushFileHandle(void* transaction_state, + const LocalHandle& handle) = 0; + virtual FileReference PushFileHandle(void* transaction_state, + const BorrowedHandle& handle) = 0; + virtual ChannelReference PushChannelHandle( + void* transaction_state, const LocalChannelHandle& handle) = 0; + virtual ChannelReference PushChannelHandle( + void* transaction_state, const BorrowedChannelHandle& handle) = 0; + virtual bool GetFileHandle(void* transaction_state, FileReference ref, + LocalHandle* handle) const = 0; + virtual bool GetChannelHandle(void* transaction_state, ChannelReference ref, + LocalChannelHandle* handle) const = 0; +}; + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_CLIENT_CHANNEL_H_ diff --git a/libs/vr/libpdx/private/pdx/client_channel_factory.h b/libs/vr/libpdx/private/pdx/client_channel_factory.h new file mode 100644 index 0000000000..a82ab70be3 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/client_channel_factory.h @@ -0,0 +1,21 @@ +#ifndef ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_ +#define ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_ + +#include <pdx/client_channel.h> +#include <pdx/status.h> + +namespace android { +namespace pdx { + +class ClientChannelFactory { + public: + virtual ~ClientChannelFactory() = default; + + virtual Status<std::unique_ptr<ClientChannel>> Connect( + int64_t timeout_ms) const = 0; +}; + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_ diff --git a/libs/vr/libpdx/private/pdx/file_handle.h b/libs/vr/libpdx/private/pdx/file_handle.h new file mode 100644 index 0000000000..b3c3ad7c8c --- /dev/null +++ b/libs/vr/libpdx/private/pdx/file_handle.h @@ -0,0 +1,141 @@ +#ifndef ANDROID_PDX_FILE_HANDLE_H_ +#define ANDROID_PDX_FILE_HANDLE_H_ + +#include <fcntl.h> +#include <unistd.h> + +#include <string> + +namespace android { +namespace pdx { + +enum class FileHandleMode { + Local, + Remote, + Borrowed, +}; + +// Manages ownership, sharing, and lifetime of file descriptors. +template <FileHandleMode Mode> +class FileHandle { + public: + static constexpr int kEmptyFileHandle = -1; + + // Constructs an empty FileHandle object. + FileHandle() : fd_(kEmptyFileHandle) {} + + // Constructs a FileHandle from an integer file descriptor and takes + // ownership. + explicit FileHandle(int fd) : fd_(fd) {} + + // Constructs a FileHandle by opening |path|. The arguments follow the + // semantics of open(). + FileHandle(const std::string& path, int flags, mode_t mode = 0) { + fd_ = open(path.c_str(), flags, mode); + } + + // Constructs a FileHandle by opening |path| relative to |dir_fd|, following + // the semantics of openat(). + FileHandle(const int directory_fd, const std::string& path, int flags, + mode_t mode = 0) { + fd_ = openat(directory_fd, path.c_str(), flags, mode); + } + + // Move constructor that assumes ownership of the file descriptor, leaving the + // other FileHandle object empty. + FileHandle(FileHandle&& other) { + fd_ = other.fd_; + other.fd_ = kEmptyFileHandle; + } + + // Returns a FileHandle as a duplicate handle of |fd|. Borrowed handles and + // handles in remote handle space are not duplicated. + static FileHandle AsDuplicate(const int fd) { + if (Mode == FileHandleMode::Local) + return FileHandle(dup(fd)); + else + return FileHandle(fd); + } + + // Destructor closes the file descriptor when non-empty. + ~FileHandle() { Close(); } + + // Move assignment operator that assumes ownership of the underlying file + // descriptor, leaving the other FileHandle object empty. + FileHandle& operator=(FileHandle&& other) { + if (this != &other) { + Reset(other.fd_); + other.fd_ = kEmptyFileHandle; + } + return *this; + } + + // Resets the underlying file handle to |fd|. + void Reset(int fd) { + Close(); + fd_ = fd; + } + + // Closes the underlying file descriptor when non-empty. + void Close() { + if (IsValid() && Mode == FileHandleMode::Local) + close(fd_); + fd_ = kEmptyFileHandle; + } + + // Return the internal fd, passing ownership to the caller. + int Release() { + int release_fd = fd_; + fd_ = kEmptyFileHandle; + return release_fd; + } + + // Duplicates the underlying file descriptor and returns a FileHandle that + // owns the new file descriptor. File descriptors are not duplicated when Mode + // is Remote or Borrowed. + FileHandle Duplicate() const { + return FileHandle(Mode == FileHandleMode::Local ? dup(fd_) : fd_); + } + + FileHandle<FileHandleMode::Borrowed> Borrow() const { + return FileHandle<FileHandleMode::Borrowed>(Get()); + } + + // Gets the underlying file descriptor value. + int Get() const { return fd_; } + bool IsValid() const { return fd_ >= 0; } + explicit operator bool() const { return IsValid(); } + + private: + int fd_; + + FileHandle(const FileHandle&) = delete; + void operator=(const FileHandle&) = delete; +}; + +// Alias for a file handle in the local process' handle space. +using LocalHandle = FileHandle<FileHandleMode::Local>; + +// Alias for a file handle in another process' handle space. Handles returned +// from pushing a file object or channel must be stored in this type of handle +// class, which doesn't close the underlying file descriptor. The underlying +// file descriptor in this wrapper should not be passed to close() because doing +// so would close an unrelated file descriptor in the local handle space. +using RemoteHandle = FileHandle<FileHandleMode::Remote>; + +// Alias for borrowed handles in the local process' handle space. A borrowed +// file handle is not close() because this wrapper does not own the underlying +// file descriptor. Care must be take to ensure that a borrowed file handle +// remains valid during use. +using BorrowedHandle = FileHandle<FileHandleMode::Borrowed>; + +// FileReference is a 16 bit integer used in IPC serialization to be +// transferred across processes. You can convert this value to a local file +// handle by calling Transaction.GetFileHandle() on client side and +// Message.GetFileHandle() on service side. +using FileReference = int16_t; + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_FILE_HANDLE_H_ diff --git a/libs/vr/libpdx/private/pdx/message_reader.h b/libs/vr/libpdx/private/pdx/message_reader.h new file mode 100644 index 0000000000..bc280cffd7 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/message_reader.h @@ -0,0 +1,38 @@ +#ifndef ANDROID_PDX_MESSAGE_READER_H_ +#define ANDROID_PDX_MESSAGE_READER_H_ + +#include <memory> + +#include <pdx/channel_handle.h> +#include <pdx/file_handle.h> + +namespace android { +namespace pdx { + +class InputResourceMapper { + public: + virtual bool GetFileHandle(FileReference ref, LocalHandle* handle) = 0; + virtual bool GetChannelHandle(ChannelReference ref, + LocalChannelHandle* handle) = 0; + + protected: + virtual ~InputResourceMapper() = default; +}; + +class MessageReader { + public: + // Pointers to start/end of the region in the read buffer. + using BufferSection = std::pair<const void*, const void*>; + + virtual BufferSection GetNextReadBufferSection() = 0; + virtual void ConsumeReadBufferSectionData(const void* new_start) = 0; + virtual InputResourceMapper* GetInputResourceMapper() = 0; + + protected: + virtual ~MessageReader() = default; +}; + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_MESSAGE_READER_H_ diff --git a/libs/vr/libpdx/private/pdx/message_writer.h b/libs/vr/libpdx/private/pdx/message_writer.h new file mode 100644 index 0000000000..4a101d6806 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/message_writer.h @@ -0,0 +1,40 @@ +#ifndef ANDROID_PDX_MESSAGE_WRITER_H_ +#define ANDROID_PDX_MESSAGE_WRITER_H_ + +#include <pdx/channel_handle.h> +#include <pdx/file_handle.h> +#include <pdx/status.h> + +namespace android { +namespace pdx { + +class OutputResourceMapper { + public: + virtual Status<FileReference> PushFileHandle(const LocalHandle& handle) = 0; + virtual Status<FileReference> PushFileHandle( + const BorrowedHandle& handle) = 0; + virtual Status<FileReference> PushFileHandle(const RemoteHandle& handle) = 0; + virtual Status<ChannelReference> PushChannelHandle( + const LocalChannelHandle& handle) = 0; + virtual Status<ChannelReference> PushChannelHandle( + const BorrowedChannelHandle& handle) = 0; + virtual Status<ChannelReference> PushChannelHandle( + const RemoteChannelHandle& handle) = 0; + + protected: + virtual ~OutputResourceMapper() = default; +}; + +class MessageWriter { + public: + virtual void* GetNextWriteBufferSection(size_t size) = 0; + virtual OutputResourceMapper* GetOutputResourceMapper() = 0; + + protected: + virtual ~MessageWriter() = default; +}; + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_MESSAGE_WRITER_H_ diff --git a/libs/vr/libpdx/private/pdx/mock_client_channel.h b/libs/vr/libpdx/private/pdx/mock_client_channel.h new file mode 100644 index 0000000000..561c939daf --- /dev/null +++ b/libs/vr/libpdx/private/pdx/mock_client_channel.h @@ -0,0 +1,56 @@ +#ifndef ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_ +#define ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_ + +#include <gmock/gmock.h> +#include <pdx/client_channel.h> + +namespace android { +namespace pdx { + +class MockClientChannel : public ClientChannel { + public: + MOCK_CONST_METHOD0(GetIpcTag, uint32_t()); + MOCK_CONST_METHOD0(event_fd, int()); + MOCK_METHOD1(GetEventMask, Status<int>(int)); + MOCK_METHOD0(GetChannelHandle, LocalChannelHandle&()); + MOCK_METHOD0(AllocateTransactionState, void*()); + MOCK_METHOD1(FreeTransactionState, void(void* state)); + MOCK_METHOD3(SendImpulse, + Status<void>(int opcode, const void* buffer, size_t length)); + MOCK_METHOD6(SendWithInt, + Status<int>(void* transaction_state, int opcode, + const iovec* send_vector, size_t send_count, + const iovec* receive_vector, size_t receive_count)); + MOCK_METHOD6(SendWithFileHandle, + Status<LocalHandle>(void* transaction_state, int opcode, + const iovec* send_vector, size_t send_count, + const iovec* receive_vector, + size_t receive_count)); + MOCK_METHOD6(SendWithChannelHandle, + Status<LocalChannelHandle>(void* transaction_state, int opcode, + const iovec* send_vector, + size_t send_count, + const iovec* receive_vector, + size_t receive_count)); + MOCK_METHOD2(PushFileHandle, FileReference(void* transaction_state, + const LocalHandle& handle)); + MOCK_METHOD2(PushFileHandle, FileReference(void* transaction_state, + const BorrowedHandle& handle)); + MOCK_METHOD2(PushChannelHandle, + ChannelReference(void* transaction_state, + const LocalChannelHandle& handle)); + MOCK_METHOD2(PushChannelHandle, + ChannelReference(void* transaction_state, + const BorrowedChannelHandle& handle)); + MOCK_CONST_METHOD3(GetFileHandle, + bool(void* transaction_state, FileReference ref, + LocalHandle* handle)); + MOCK_CONST_METHOD3(GetChannelHandle, + bool(void* transaction_state, ChannelReference ref, + LocalChannelHandle* handle)); +}; + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_ diff --git a/libs/vr/libpdx/private/pdx/mock_client_channel_factory.h b/libs/vr/libpdx/private/pdx/mock_client_channel_factory.h new file mode 100644 index 0000000000..0190f5e458 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/mock_client_channel_factory.h @@ -0,0 +1,19 @@ +#ifndef ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_ +#define ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_ + +#include <gmock/gmock.h> +#include <pdx/client_channel_factory.h> + +namespace android { +namespace pdx { + +class MockClientChannelFactory : public ClientChannelFactory { + public: + MOCK_CONST_METHOD1( + Connect, Status<std::unique_ptr<ClientChannel>>(int64_t timeout_ms)); +}; + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_ diff --git a/libs/vr/libpdx/private/pdx/mock_message_reader.h b/libs/vr/libpdx/private/pdx/mock_message_reader.h new file mode 100644 index 0000000000..85e96ef993 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/mock_message_reader.h @@ -0,0 +1,27 @@ +#ifndef ANDROID_PDX_MOCK_MESSAGE_READER_H_ +#define ANDROID_PDX_MOCK_MESSAGE_READER_H_ + +#include <gmock/gmock.h> +#include <pdx/message_reader.h> + +namespace android { +namespace pdx { + +class MockInputResourceMapper : public InputResourceMapper { + public: + MOCK_METHOD2(GetFileHandle, bool(FileReference ref, LocalHandle* handle)); + MOCK_METHOD2(GetChannelHandle, + bool(ChannelReference ref, LocalChannelHandle* handle)); +}; + +class MockMessageReader : public MessageReader { + public: + MOCK_METHOD0(GetNextReadBufferSection, BufferSection()); + MOCK_METHOD1(ConsumeReadBufferSectionData, void(const void* new_start)); + MOCK_METHOD0(GetInputResourceMapper, InputResourceMapper*()); +}; + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_MOCK_MESSAGE_READER_H_ diff --git a/libs/vr/libpdx/private/pdx/mock_message_writer.h b/libs/vr/libpdx/private/pdx/mock_message_writer.h new file mode 100644 index 0000000000..e06e5bbc2b --- /dev/null +++ b/libs/vr/libpdx/private/pdx/mock_message_writer.h @@ -0,0 +1,35 @@ +#ifndef ANDROID_PDX_MOCK_MESSAGE_WRITER_H_ +#define ANDROID_PDX_MOCK_MESSAGE_WRITER_H_ + +#include <gmock/gmock.h> +#include <pdx/message_writer.h> + +namespace android { +namespace pdx { + +class MockOutputResourceMapper : public OutputResourceMapper { + public: + MOCK_METHOD1(PushFileHandle, + Status<FileReference>(const LocalHandle& handle)); + MOCK_METHOD1(PushFileHandle, + Status<FileReference>(const BorrowedHandle& handle)); + MOCK_METHOD1(PushFileHandle, + Status<FileReference>(const RemoteHandle& handle)); + MOCK_METHOD1(PushChannelHandle, + Status<ChannelReference>(const LocalChannelHandle& handle)); + MOCK_METHOD1(PushChannelHandle, + Status<ChannelReference>(const BorrowedChannelHandle& handle)); + MOCK_METHOD1(PushChannelHandle, + Status<ChannelReference>(const RemoteChannelHandle& handle)); +}; + +class MockMessageWriter : public MessageWriter { + public: + MOCK_METHOD1(GetNextWriteBufferSection, void*(size_t size)); + MOCK_METHOD0(GetOutputResourceMapper, OutputResourceMapper*()); +}; + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_MOCK_MESSAGE_WRITER_H_ diff --git a/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h b/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h new file mode 100644 index 0000000000..9b51d30592 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h @@ -0,0 +1,24 @@ +#ifndef ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_ +#define ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_ + +#include <gmock/gmock.h> +#include <pdx/service_dispatcher.h> + +namespace android { +namespace pdx { + +class MockServiceDispatcher : public ServiceDispatcher { + public: + MOCK_METHOD1(AddService, int(const std::shared_ptr<Service>& service)); + MOCK_METHOD1(RemoveService, int(const std::shared_ptr<Service>& service)); + MOCK_METHOD0(ReceiveAndDispatch, int()); + MOCK_METHOD1(ReceiveAndDispatch, int(int timeout)); + MOCK_METHOD0(EnterDispatchLoop, int()); + MOCK_METHOD1(SetCanceled, void(bool cancel)); + MOCK_CONST_METHOD0(IsCanceled, bool()); +}; + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_ diff --git a/libs/vr/libpdx/private/pdx/mock_service_endpoint.h b/libs/vr/libpdx/private/pdx/mock_service_endpoint.h new file mode 100644 index 0000000000..e741d4a46b --- /dev/null +++ b/libs/vr/libpdx/private/pdx/mock_service_endpoint.h @@ -0,0 +1,74 @@ +#ifndef ANDROID_PDX_MOCK_ENDPOINT_H_ +#define ANDROID_PDX_MOCK_ENDPOINT_H_ + +#include <gmock/gmock.h> +#include <pdx/service_endpoint.h> + +namespace android { +namespace pdx { + +class MockEndpoint : public Endpoint { + public: + MOCK_CONST_METHOD0(GetIpcTag, uint32_t()); + MOCK_METHOD1(SetService, Status<void>(Service* service)); + MOCK_METHOD2(SetChannel, Status<void>(int channel_id, Channel* channel)); + MOCK_METHOD1(CloseChannel, Status<void>(int channel_id)); + MOCK_METHOD3(ModifyChannelEvents, + Status<void>(int channel_id, int clear_mask, int set_mask)); + MOCK_METHOD4(PushChannel, + Status<RemoteChannelHandle>(Message* message, int flags, + Channel* channel, int* channel_id)); + MOCK_METHOD3(CheckChannel, + Status<int>(const Message* message, ChannelReference ref, + Channel** channel)); + MOCK_METHOD1(MessageReceive, Status<void>(Message* message)); + MOCK_METHOD2(MessageReply, Status<void>(Message* message, int return_code)); + MOCK_METHOD2(MessageReplyFd, + Status<void>(Message* message, unsigned int push_fd)); + MOCK_METHOD2(MessageReplyChannelHandle, + Status<void>(Message* message, + const LocalChannelHandle& handle)); + MOCK_METHOD2(MessageReplyChannelHandle, + Status<void>(Message* message, + const BorrowedChannelHandle& handle)); + MOCK_METHOD2(MessageReplyChannelHandle, + Status<void>(Message* message, + const RemoteChannelHandle& handle)); + MOCK_METHOD3(ReadMessageData, + Status<size_t>(Message* message, const iovec* vector, + size_t vector_length)); + MOCK_METHOD3(WriteMessageData, + Status<size_t>(Message* message, const iovec* vector, + size_t vector_length)); + MOCK_METHOD2(PushFileHandle, + Status<FileReference>(Message* message, + const LocalHandle& handle)); + MOCK_METHOD2(PushFileHandle, + Status<FileReference>(Message* message, + const BorrowedHandle& handle)); + MOCK_METHOD2(PushFileHandle, + Status<FileReference>(Message* message, + const RemoteHandle& handle)); + MOCK_METHOD2(PushChannelHandle, + Status<ChannelReference>(Message* message, + const LocalChannelHandle& handle)); + MOCK_METHOD2(PushChannelHandle, + Status<ChannelReference>(Message* message, + const BorrowedChannelHandle& handle)); + MOCK_METHOD2(PushChannelHandle, + Status<ChannelReference>(Message* message, + const RemoteChannelHandle& handle)); + MOCK_CONST_METHOD2(GetFileHandle, + LocalHandle(Message* message, FileReference ref)); + MOCK_CONST_METHOD2(GetChannelHandle, + LocalChannelHandle(Message* message, + ChannelReference ref)); + MOCK_METHOD0(AllocateMessageState, void*()); + MOCK_METHOD1(FreeMessageState, void(void* state)); + MOCK_METHOD0(Cancel, Status<void>()); +}; + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_MOCK_ENDPOINT_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/argument_encoder.h b/libs/vr/libpdx/private/pdx/rpc/argument_encoder.h new file mode 100644 index 0000000000..e0062845a5 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/argument_encoder.h @@ -0,0 +1,184 @@ +#ifndef ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_ +#define ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_ + +#include <cstdint> +#include <tuple> +#include <type_traits> + +#include <pdx/rpc/serialization.h> +#include <pdx/service.h> + +namespace android { +namespace pdx { +namespace rpc { + +// Provides automatic serialization of argument lists and return +// values by analyzing the supplied function signature types. +// Examples: +// ArgumentEncoder<int(int, float)> encoder(writer); +// encoder.EncodeArguments(1, 1.0); + +template <typename T> +class ArgumentEncoder; + +// Specialization of ArgumentEncoder for void return types. +template <typename... Args> +class ArgumentEncoder<void(Args...)> { + public: + explicit ArgumentEncoder(MessageWriter* writer) : writer_{writer} {} + + // Serializes the arguments as a tuple. + void EncodeArguments(Args... args) { + Serialize(std::forward_as_tuple(args...), writer_); + } + + private: + MessageWriter* writer_; +}; + +// Specialization of ArgumentEncoder for non-void return types. +template <typename Return, typename... Args> +class ArgumentEncoder<Return(Args...)> { + public: + // Simplified types with reference and cv removed. + using ReturnType = typename std::decay<Return>::type; + + explicit ArgumentEncoder(MessageWriter* writer) : writer_{writer} {} + + // Serializes the arguments as a tuple. + void EncodeArguments(Args... args) { + Serialize(std::forward_as_tuple(args...), writer_); + } + + // Serializes the return value for rvalue references. + void EncodeReturn(const ReturnType& return_value) { + Serialize(return_value, writer_); + } + + private: + MessageWriter* writer_; +}; + +// Utility to build an ArgumentEncoder from a function pointer and a message +// writer. +template <typename Return, typename... Args> +inline ArgumentEncoder<Return(Args...)> MakeArgumentEncoder( + Return (*)(Args...), MessageWriter* writer) { + return ArgumentEncoder<Return(Args...)>(writer); +} + +// Utility to build an ArgumentEncoder from a method pointer and a message +// writer. +template <typename Class, typename Return, typename... Args> +inline ArgumentEncoder<Return(Args...)> MakeArgumentEncoder( + Return (Class::*)(Args...), MessageWriter* writer) { + return ArgumentEncoder<Return(Args...)>(writer); +} + +// Utility to build an ArgumentEncoder from a const method pointer and a +// message writer. +template <typename Class, typename Return, typename... Args> +inline ArgumentEncoder<Return(Args...)> MakeArgumentEncoder( + Return (Class::*)(Args...) const, MessageWriter* writer) { + return ArgumentEncoder<Return(Args...)>(writer); +} + +// Utility to build an ArgumentEncoder from a function type and a message +// writer. +template <typename Signature> +inline ArgumentEncoder<Signature> MakeArgumentEncoder(MessageWriter* writer) { + return ArgumentEncoder<Signature>(writer); +} + +////////////////////////////////////////////////////////////////////////////// +// Provides automatic deserialization of argument lists and return +// values by analyzing the supplied function signature types. +// Examples: +// auto decoder = MakeArgumentDecoder<std::string(void)>(reader); +// ErrorType error = decoder.DecodeReturn(&return_value); + +template <typename T> +class ArgumentDecoder; + +// Specialization of ArgumentDecoder for void return types. +template <typename... Args> +class ArgumentDecoder<void(Args...)> { + public: + // Simplified types with reference and cv removed. + using ArgsTupleType = std::tuple<typename std::decay<Args>::type...>; + + explicit ArgumentDecoder(MessageReader* reader) : reader_{reader} {} + + // Deserializes arguments into a tuple. + ArgsTupleType DecodeArguments(ErrorType* error) { + ArgsTupleType value; + *error = Deserialize(&value, reader_); + return value; + } + + private: + MessageReader* reader_; +}; + +// Specialization of ArgumentDecoder for non-void return types. +template <typename Return, typename... Args> +class ArgumentDecoder<Return(Args...)> { + public: + // Simplified types with reference and cv removed. + using ArgsTupleType = std::tuple<typename std::decay<Args>::type...>; + using ReturnType = typename std::decay<Return>::type; + + explicit ArgumentDecoder(MessageReader* reader) : reader_{reader} {} + + // Deserializes arguments into a tuple. + ArgsTupleType DecodeArguments(ErrorType* error) { + ArgsTupleType value; + *error = Deserialize(&value, reader_); + return value; + } + + // Deserializes the return value. + ErrorType DecodeReturn(ReturnType* value) { + return Deserialize(value, reader_); + } + + private: + MessageReader* reader_; +}; + +// Utility to build an ArgumentDecoder from a function pointer and a message +// reader. +template <typename Return, typename... Args> +inline ArgumentDecoder<Return(Args...)> MakeArgumentDecoder( + Return (*)(Args...), MessageReader* reader) { + return ArgumentDecoder<Return(Args...)>(reader); +} + +// Utility to build an ArgumentDecoder from a method pointer and a message +// reader. +template <typename Class, typename Return, typename... Args> +inline ArgumentDecoder<Return(Args...)> MakeArgumentDecoder( + Return (Class::*)(Args...), MessageReader* reader) { + return ArgumentDecoder<Return(Args...)>(reader); +} + +// Utility to build an ArgumentDecoder from a const method pointer and a +// message reader. +template <typename Class, typename Return, typename... Args> +inline ArgumentDecoder<Return(Args...)> MakeArgumentDecoder( + Return (Class::*)(Args...) const, MessageReader* reader) { + return ArgumentDecoder<Return(Args...)>(reader); +} + +// Utility to build an ArgumentDecoder from a function type and a message +// reader. +template <typename Signature> +inline ArgumentDecoder<Signature> MakeArgumentDecoder(MessageReader* reader) { + return ArgumentDecoder<Signature>(reader); +} + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/array_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/array_wrapper.h new file mode 100644 index 0000000000..93d87f3cab --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/array_wrapper.h @@ -0,0 +1,111 @@ +#ifndef ANDROID_PDX_RPC_ARRAY_WRAPPER_H_ +#define ANDROID_PDX_RPC_ARRAY_WRAPPER_H_ + +#include <cstddef> +#include <memory> +#include <type_traits> +#include <vector> + +namespace android { +namespace pdx { +namespace rpc { + +// Wrapper class for C array buffers, providing an interface suitable for +// SerializeObject and DeserializeObject. This class serializes to the same +// format as std::vector, and may be substituted for std::vector during +// serialization and deserialization. This substitution makes handling of C +// arrays more efficient by avoiding unnecessary copies when remote method +// signatures specify std::vector arguments or return values. +template <typename T> +class ArrayWrapper { + public: + // Define types in the style of STL containers to support STL operators. + typedef T value_type; + typedef std::size_t size_type; + typedef T& reference; + typedef const T& const_reference; + typedef T* pointer; + typedef const T* const_pointer; + + ArrayWrapper() : buffer_(nullptr), capacity_(0), end_(0) {} + + ArrayWrapper(pointer buffer, size_type capacity, size_type size) + : buffer_(&buffer[0]), + capacity_(capacity), + end_(capacity < size ? capacity : size) {} + + ArrayWrapper(pointer buffer, size_type size) + : ArrayWrapper(buffer, size, size) {} + + ArrayWrapper(const ArrayWrapper& other) { *this = other; } + + ArrayWrapper(ArrayWrapper&& other) { *this = std::move(other); } + + ArrayWrapper& operator=(const ArrayWrapper& other) { + if (&other == this) { + return *this; + } else { + buffer_ = other.buffer_; + capacity_ = other.capacity_; + end_ = other.end_; + } + + return *this; + } + + ArrayWrapper& operator=(ArrayWrapper&& other) { + if (&other == this) { + return *this; + } else { + buffer_ = other.buffer_; + capacity_ = other.capacity_; + end_ = other.end_; + other.buffer_ = nullptr; + other.capacity_ = 0; + other.end_ = 0; + } + + return *this; + } + + pointer data() { return buffer_; } + const_pointer data() const { return buffer_; } + + pointer begin() { return &buffer_[0]; } + pointer end() { return &buffer_[end_]; } + const_pointer begin() const { return &buffer_[0]; } + const_pointer end() const { return &buffer_[end_]; } + + size_type size() const { return end_; } + size_type max_size() const { return capacity_; } + size_type capacity() const { return capacity_; } + + // Moves the end marker to |size|, clamping the end marker to the max capacity + // of the underlying array. This method does not change the size of the + // underlying array. + void resize(size_type size) { + if (size <= capacity_) + end_ = size; + else + end_ = capacity_; + } + + reference operator[](size_type pos) { return buffer_[pos]; } + const_reference operator[](size_type pos) const { return buffer_[pos]; } + + private: + pointer buffer_; + size_type capacity_; + size_type end_; +}; + +template <typename T, typename SizeType = std::size_t> +ArrayWrapper<T> WrapArray(T* buffer, SizeType size) { + return ArrayWrapper<T>(buffer, size); +} + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_ARRAY_WRAPPER_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h new file mode 100644 index 0000000000..aa86531061 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h @@ -0,0 +1,177 @@ +#ifndef ANDROID_PDX_RPC_BUFFER_WRAPPER_H_ +#define ANDROID_PDX_RPC_BUFFER_WRAPPER_H_ + +#include <cstddef> +#include <memory> +#include <type_traits> +#include <vector> + +namespace android { +namespace pdx { +namespace rpc { + +// Wrapper class for buffers, providing an interface suitable for +// SerializeObject and DeserializeObject. This class supports serialization of +// buffers as raw bytes. +template <typename T> +class BufferWrapper; + +template <typename T> +class BufferWrapper<T*> { + public: + // Define types in the style of STL containers to support STL operators. + typedef T value_type; + typedef std::size_t size_type; + typedef T& reference; + typedef const T& const_reference; + typedef T* pointer; + typedef const T* const_pointer; + + BufferWrapper() : buffer_(nullptr), capacity_(0), end_(0) {} + + BufferWrapper(pointer buffer, size_type capacity, size_type size) + : buffer_(&buffer[0]), + capacity_(capacity), + end_(capacity < size ? capacity : size) {} + + BufferWrapper(pointer buffer, size_type size) + : BufferWrapper(buffer, size, size) {} + + BufferWrapper(const BufferWrapper& other) { *this = other; } + + BufferWrapper(BufferWrapper&& other) { *this = std::move(other); } + + BufferWrapper& operator=(const BufferWrapper& other) { + if (&other == this) { + return *this; + } else { + buffer_ = other.buffer_; + capacity_ = other.capacity_; + end_ = other.end_; + } + + return *this; + } + + BufferWrapper& operator=(BufferWrapper&& other) { + if (&other == this) { + return *this; + } else { + buffer_ = other.buffer_; + capacity_ = other.capacity_; + end_ = other.end_; + other.buffer_ = nullptr; + other.capacity_ = 0; + other.end_ = 0; + } + + return *this; + } + + pointer data() { return buffer_; } + const_pointer data() const { return buffer_; } + + pointer begin() { return &buffer_[0]; } + pointer end() { return &buffer_[end_]; } + const_pointer begin() const { return &buffer_[0]; } + const_pointer end() const { return &buffer_[end_]; } + + size_type size() const { return end_; } + size_type max_size() const { return capacity_; } + size_type capacity() const { return capacity_; } + + void resize(size_type size) { + if (size <= capacity_) + end_ = size; + else + end_ = capacity_; + } + + reference operator[](size_type pos) { return buffer_[pos]; } + const_reference operator[](size_type pos) const { return buffer_[pos]; } + + private: + pointer buffer_; + size_type capacity_; + size_type end_; +}; + +template <typename T, typename Allocator> +class BufferWrapper<std::vector<T, Allocator>> { + public: + using BufferType = typename std::vector<T, Allocator>; + using value_type = typename BufferType::value_type; + using size_type = typename BufferType::size_type; + using reference = typename BufferType::reference; + using const_reference = typename BufferType::const_reference; + using pointer = typename BufferType::pointer; + using const_pointer = typename BufferType::const_pointer; + using iterator = typename BufferType::iterator; + using const_iterator = typename BufferType::const_iterator; + + BufferWrapper() {} + BufferWrapper(const BufferType& buffer) : buffer_(buffer) {} + BufferWrapper(const BufferType& buffer, const Allocator& allocator) + : buffer_(buffer, allocator) {} + BufferWrapper(BufferType&& buffer) : buffer_(std::move(buffer)) {} + BufferWrapper(BufferType&& buffer, const Allocator& allocator) + : buffer_(std::move(buffer), allocator) {} + BufferWrapper(const BufferWrapper&) = default; + BufferWrapper(BufferWrapper&&) = default; + BufferWrapper& operator=(const BufferWrapper&) = default; + BufferWrapper& operator=(BufferWrapper&&) = default; + + pointer data() { return buffer_.data(); } + const_pointer data() const { return buffer_.data(); } + + iterator begin() { return buffer_.begin(); } + iterator end() { return buffer_.end(); } + const_iterator begin() const { return buffer_.begin(); } + const_iterator end() const { return buffer_.end(); } + + size_type size() const { return buffer_.size(); } + size_type max_size() const { return buffer_.capacity(); } + size_type capacity() const { return buffer_.capacity(); } + + void resize(size_type size) { buffer_.resize(size); } + void reserve(size_type size) { buffer_.reserve(size); } + + reference operator[](size_type pos) { return buffer_[pos]; } + const_reference operator[](size_type pos) const { return buffer_[pos]; } + + BufferType& buffer() { return buffer_; } + const BufferType& buffer() const { return buffer_; } + + private: + BufferType buffer_; +}; + +template <typename T, typename SizeType = std::size_t> +BufferWrapper<T*> WrapBuffer(T* buffer, SizeType size) { + return BufferWrapper<T*>(buffer, size); +} + +template <typename SizeType = std::size_t> +BufferWrapper<std::uint8_t*> WrapBuffer(void* buffer, SizeType size) { + return BufferWrapper<std::uint8_t*>(static_cast<std::uint8_t*>(buffer), size); +} + +template <typename SizeType = std::size_t> +BufferWrapper<const std::uint8_t*> WrapBuffer(const void* buffer, + SizeType size) { + return BufferWrapper<const std::uint8_t*>( + static_cast<const std::uint8_t*>(buffer), size); +} + +template <typename T, typename Allocator = std::allocator<T>> +BufferWrapper<std::vector<T, Allocator>> WrapBuffer( + std::vector<T, Allocator>&& buffer) { + return BufferWrapper<std::vector<T, Allocator>>( + std::forward<std::vector<T, Allocator>>(buffer)); +} + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_BUFFER_WRAPPER_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h b/libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h new file mode 100644 index 0000000000..5ce34f8f9c --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h @@ -0,0 +1,43 @@ +#ifndef ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_ +#define ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_ + +#include <type_traits> + +namespace android { +namespace pdx { +namespace rpc { + +// Copies const, void, and reference qualifiers from type T to type U, such that +// the new type U' carries the same cv-reference qualifiers as T, with the same +// underlying type as U. +template <typename T, typename U> +class CopyCVReference { + private: + using R = typename std::remove_reference<T>::type; + using U1 = + typename std::conditional<std::is_const<R>::value, + typename std::add_const<U>::type, U>::type; + using U2 = + typename std::conditional<std::is_volatile<R>::value, + typename std::add_volatile<U1>::type, U1>::type; + using U3 = + typename std::conditional<std::is_lvalue_reference<T>::value, + typename std::add_lvalue_reference<U2>::type, + U2>::type; + using U4 = + typename std::conditional<std::is_rvalue_reference<T>::value, + typename std::add_rvalue_reference<U3>::type, + U3>::type; + + public: + using Type = U4; +}; + +template <typename T, typename U> +using CopyCVReferenceType = typename CopyCVReference<T, U>::Type; + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h b/libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h new file mode 100644 index 0000000000..b6e298084e --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h @@ -0,0 +1,47 @@ +#ifndef ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_ +#define ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_ + +#include <memory> + +namespace android { +namespace pdx { +namespace rpc { + +// Allocator adaptor that interposes construct() calls to convert value +// initialization into default initialization. All standard containers +// value-initialize their elements when constructed with a single size_type +// argument or when grown by a call to resize. This allocator avoids potentially +// costly value-initialization in these situations for value types that are +// default constructible. As a consequence, elements of non-class types are left +// uninitialized; this is desirable when using std::vector as a resizable +// buffer, for example. +template <typename T, typename Allocator = std::allocator<T>> +class DefaultInitializationAllocator : public Allocator { + typedef std::allocator_traits<Allocator> AllocatorTraits; + + public: + template <typename U> + struct rebind { + using other = DefaultInitializationAllocator< + U, typename AllocatorTraits::template rebind_alloc<U>>; + }; + + using Allocator::Allocator; + + template <typename U> + void construct(U* pointer) noexcept( + std::is_nothrow_default_constructible<U>::value) { + ::new (static_cast<void*>(pointer)) U; + } + template <typename U, typename... Args> + void construct(U* pointer, Args&&... args) { + AllocatorTraits::construct(static_cast<Allocator&>(*this), pointer, + std::forward<Args>(args)...); + } +}; + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/encoding.h b/libs/vr/libpdx/private/pdx/rpc/encoding.h new file mode 100644 index 0000000000..f51d807f5b --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/encoding.h @@ -0,0 +1,616 @@ +#ifndef ANDROID_PDX_RPC_ENCODING_H_ +#define ANDROID_PDX_RPC_ENCODING_H_ + +#include <array> +#include <cstdint> +#include <cstring> +#include <map> +#include <numeric> +#include <string> +#include <tuple> +#include <unordered_map> +#include <vector> + +#include <pdx/channel_handle.h> +#include <pdx/file_handle.h> + +#include "array_wrapper.h" +#include "buffer_wrapper.h" +#include "string_wrapper.h" +#include "variant.h" + +namespace android { +namespace pdx { +namespace rpc { + +// This library uses a subset, or profile, of MessagePack (http://msgpack.org) +// to encode supported data types during serialization and to verify the +// expected data types during deserialization. One notable deviation from the +// MessagePack specification is that little-endian byte order is used for +// multi-byte numeric types to avoid unnecessary conversion on nearly all +// relevant architectures. +// +// Some data types, integers for example, support multiple encoding strategies. +// This library attempts to optimize for space based on the value of such types. +// However, during decode all valid encodings for a given type are accepted. + +// Prefix byte for type encodings. This is the complete list of prefix bytes +// from the MessagePack specification, even though not all types are used by +// this library. +enum EncodingPrefix { + ENCODING_TYPE_POSITIVE_FIXINT = 0x00, + ENCODING_TYPE_POSITIVE_FIXINT_MIN = 0x00, + ENCODING_TYPE_POSITIVE_FIXINT_MAX = 0x7f, + ENCODING_TYPE_POSITIVE_FIXINT_MASK = 0x7f, + ENCODING_TYPE_FIXMAP = 0x80, + ENCODING_TYPE_FIXMAP_MIN = 0x80, + ENCODING_TYPE_FIXMAP_MAX = 0x8f, + ENCODING_TYPE_FIXMAP_MASK = 0x0f, + ENCODING_TYPE_FIXARRAY = 0x90, + ENCODING_TYPE_FIXARRAY_MIN = 0x90, + ENCODING_TYPE_FIXARRAY_MAX = 0x9f, + ENCODING_TYPE_FIXARRAY_MASK = 0x0f, + ENCODING_TYPE_FIXSTR = 0xa0, + ENCODING_TYPE_FIXSTR_MIN = 0xa0, + ENCODING_TYPE_FIXSTR_MAX = 0xbf, + ENCODING_TYPE_FIXSTR_MASK = 0x1f, + ENCODING_TYPE_NIL = 0xc0, + ENCODING_TYPE_RESERVED = 0xc1, + ENCODING_TYPE_FALSE = 0xc2, + ENCODING_TYPE_TRUE = 0xc3, + ENCODING_TYPE_BIN8 = 0xc4, + ENCODING_TYPE_BIN16 = 0xc5, + ENCODING_TYPE_BIN32 = 0xc6, + ENCODING_TYPE_EXT8 = 0xc7, + ENCODING_TYPE_EXT16 = 0xc8, + ENCODING_TYPE_EXT32 = 0xc9, + ENCODING_TYPE_FLOAT32 = 0xca, + ENCODING_TYPE_FLOAT64 = 0xcb, + ENCODING_TYPE_UINT8 = 0xcc, + ENCODING_TYPE_UINT16 = 0xcd, + ENCODING_TYPE_UINT32 = 0xce, + ENCODING_TYPE_UINT64 = 0xcf, + ENCODING_TYPE_INT8 = 0xd0, + ENCODING_TYPE_INT16 = 0xd1, + ENCODING_TYPE_INT32 = 0xd2, + ENCODING_TYPE_INT64 = 0xd3, + ENCODING_TYPE_FIXEXT1 = 0xd4, + ENCODING_TYPE_FIXEXT2 = 0xd5, + ENCODING_TYPE_FIXEXT4 = 0xd6, + ENCODING_TYPE_FIXEXT8 = 0xd7, + ENCODING_TYPE_FIXEXT16 = 0xd8, + ENCODING_TYPE_STR8 = 0xd9, + ENCODING_TYPE_STR16 = 0xda, + ENCODING_TYPE_STR32 = 0xdb, + ENCODING_TYPE_ARRAY16 = 0xdc, + ENCODING_TYPE_ARRAY32 = 0xdd, + ENCODING_TYPE_MAP16 = 0xde, + ENCODING_TYPE_MAP32 = 0xdf, + ENCODING_TYPE_NEGATIVE_FIXINT = 0xe0, + ENCODING_TYPE_NEGATIVE_FIXINT_MIN = 0xe0, + ENCODING_TYPE_NEGATIVE_FIXINT_MAX = 0xff, +}; + +// Base encoding classes grouping multi-strategy encodings. +enum EncodingClass { + ENCODING_CLASS_BOOL, + ENCODING_CLASS_NIL, + ENCODING_CLASS_INT, + ENCODING_CLASS_UINT, + ENCODING_CLASS_FLOAT, + ENCODING_CLASS_ARRAY, + ENCODING_CLASS_MAP, + ENCODING_CLASS_STRING, + ENCODING_CLASS_BINARY, + ENCODING_CLASS_EXTENSION, +}; + +// Encoding prefixes are unsigned bytes. +typedef std::uint8_t EncodingType; + +// Extension encoding types defined by this library. +enum EncodingExtType : int8_t { + ENCODING_EXT_TYPE_FILE_DESCRIPTOR, + ENCODING_EXT_TYPE_CHANNEL_HANDLE, +}; + +// Encoding predicates. Determines whether the given encoding is of a specific +// type. +inline constexpr bool IsFixintEncoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: + case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX: + return true; + default: + return false; + } +} + +inline constexpr bool IsUnsignedFixintEncoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: + return true; + default: + return false; + } +} + +inline constexpr bool IsInt8Encoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: + case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX: + case ENCODING_TYPE_INT8: + return true; + default: + return false; + } +} + +inline constexpr bool IsUInt8Encoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: + case ENCODING_TYPE_UINT8: + return true; + default: + return false; + } +} + +inline constexpr bool IsInt16Encoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: + case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX: + case ENCODING_TYPE_INT8: + case ENCODING_TYPE_INT16: + return true; + default: + return false; + } +} + +inline constexpr bool IsUInt16Encoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: + case ENCODING_TYPE_UINT8: + case ENCODING_TYPE_UINT16: + return true; + default: + return false; + } +} + +inline constexpr bool IsInt32Encoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: + case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX: + case ENCODING_TYPE_INT8: + case ENCODING_TYPE_INT16: + case ENCODING_TYPE_INT32: + return true; + default: + return false; + } +} + +inline constexpr bool IsUInt32Encoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: + case ENCODING_TYPE_UINT8: + case ENCODING_TYPE_UINT16: + case ENCODING_TYPE_UINT32: + return true; + default: + return false; + } +} + +inline constexpr bool IsInt64Encoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: + case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX: + case ENCODING_TYPE_INT8: + case ENCODING_TYPE_INT16: + case ENCODING_TYPE_INT32: + case ENCODING_TYPE_INT64: + return true; + default: + return false; + } +} + +inline constexpr bool IsUInt64Encoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: + case ENCODING_TYPE_UINT8: + case ENCODING_TYPE_UINT16: + case ENCODING_TYPE_UINT32: + case ENCODING_TYPE_UINT64: + return true; + default: + return false; + } +} + +inline constexpr bool IsFixmapEncoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_FIXMAP_MIN ... ENCODING_TYPE_FIXMAP_MAX: + return true; + default: + return false; + } +} + +inline constexpr bool IsFixarrayEncoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_FIXARRAY_MIN ... ENCODING_TYPE_FIXARRAY_MAX: + return true; + default: + return false; + } +} + +inline constexpr bool IsFixstrEncoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_FIXSTR_MIN ... ENCODING_TYPE_FIXSTR_MAX: + return true; + default: + return false; + } +} + +inline constexpr bool IsFixextEncoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_FIXEXT1: + case ENCODING_TYPE_FIXEXT2: + case ENCODING_TYPE_FIXEXT4: + case ENCODING_TYPE_FIXEXT8: + case ENCODING_TYPE_FIXEXT16: + return true; + default: + return false; + } +} + +inline constexpr bool IsFloat32Encoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_FLOAT32: + return true; + default: + return false; + } +} + +inline constexpr bool IsFloat64Encoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_FLOAT32: + case ENCODING_TYPE_FLOAT64: + return true; + default: + return false; + } +} + +inline constexpr bool IsBoolEncoding(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_FALSE: + case ENCODING_TYPE_TRUE: + return true; + default: + return false; + } +} + +inline constexpr std::size_t GetFixstrSize(EncodingType encoding) { + return encoding & ENCODING_TYPE_FIXSTR_MASK; +} + +inline constexpr std::size_t GetFixarraySize(EncodingType encoding) { + return encoding & ENCODING_TYPE_FIXARRAY_MASK; +} + +inline constexpr std::size_t GetFixmapSize(EncodingType encoding) { + return encoding & ENCODING_TYPE_FIXMAP_MASK; +} + +inline constexpr std::size_t GetFixextSize(EncodingType encoding) { + switch (encoding) { + case ENCODING_TYPE_FIXEXT1: + return 1; + case ENCODING_TYPE_FIXEXT2: + return 2; + case ENCODING_TYPE_FIXEXT4: + return 4; + case ENCODING_TYPE_FIXEXT8: + return 8; + case ENCODING_TYPE_FIXEXT16: + return 16; + default: + return 0; // Invalid fixext size. + } +} + +// Gets the size of the encoding in bytes, not including external payload data. +inline constexpr std::size_t GetEncodingSize(EncodingType encoding) { + switch (encoding) { + // Encoding is fully contained within the type value. + case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: + case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX: + case ENCODING_TYPE_FIXMAP_MIN ... ENCODING_TYPE_FIXMAP_MAX: + case ENCODING_TYPE_FIXARRAY_MIN ... ENCODING_TYPE_FIXARRAY_MAX: + case ENCODING_TYPE_FIXSTR_MIN ... ENCODING_TYPE_FIXSTR_MAX: + case ENCODING_TYPE_NIL: + case ENCODING_TYPE_RESERVED: + case ENCODING_TYPE_FALSE: + case ENCODING_TYPE_TRUE: + return 1; + + // Encoding type followed by one-byte size or immediate value. + case ENCODING_TYPE_BIN8: + case ENCODING_TYPE_EXT8: + case ENCODING_TYPE_UINT8: + case ENCODING_TYPE_INT8: + case ENCODING_TYPE_STR8: + // Encoding type followed by one-byte extension type. + case ENCODING_TYPE_FIXEXT1: + case ENCODING_TYPE_FIXEXT2: + case ENCODING_TYPE_FIXEXT4: + case ENCODING_TYPE_FIXEXT8: + case ENCODING_TYPE_FIXEXT16: + return 2; + + // Encoding type followed by two-byte size or immediate value. + case ENCODING_TYPE_BIN16: + case ENCODING_TYPE_EXT16: + case ENCODING_TYPE_UINT16: + case ENCODING_TYPE_INT16: + case ENCODING_TYPE_STR16: + case ENCODING_TYPE_ARRAY16: + case ENCODING_TYPE_MAP16: + return 3; + + // Encoding type followed by four-byte size or immediate value. + case ENCODING_TYPE_BIN32: + case ENCODING_TYPE_EXT32: + case ENCODING_TYPE_FLOAT32: + case ENCODING_TYPE_UINT32: + case ENCODING_TYPE_INT32: + case ENCODING_TYPE_STR32: + case ENCODING_TYPE_ARRAY32: + case ENCODING_TYPE_MAP32: + return 5; + + // Encoding type followed by eight-byte immediate value. + case ENCODING_TYPE_FLOAT64: + case ENCODING_TYPE_UINT64: + case ENCODING_TYPE_INT64: + return 9; + + default: + return 0; + } +} + +// Encoding for standard types. Each supported data type has an associated +// encoding or set of encodings. These functions determine the MessagePack +// encoding based on the data type, value, and size of their arguments. + +inline constexpr EncodingType EncodeArrayType(std::size_t size) { + if (size < (1U << 4)) + return ENCODING_TYPE_FIXARRAY | (size & ENCODING_TYPE_FIXARRAY_MASK); + else if (size < (1U << 16)) + return ENCODING_TYPE_ARRAY16; + else + return ENCODING_TYPE_ARRAY32; +} + +inline constexpr EncodingType EncodeMapType(std::size_t size) { + if (size < (1U << 4)) + return ENCODING_TYPE_FIXMAP | (size & ENCODING_TYPE_FIXMAP_MASK); + else if (size < (1U << 16)) + return ENCODING_TYPE_MAP16; + else + return ENCODING_TYPE_MAP32; +} + +inline constexpr EncodingType EncodeStringType(std::size_t size) { + if (size < (1U << 5)) + return ENCODING_TYPE_FIXSTR | (size & ENCODING_TYPE_FIXSTR_MASK); + else if (size < (1U << 8)) + return ENCODING_TYPE_STR8; + else if (size < (1U << 16)) + return ENCODING_TYPE_STR16; + else + return ENCODING_TYPE_STR32; +} + +inline constexpr EncodingType EncodeBinType(std::size_t size) { + if (size < (1U << 8)) + return ENCODING_TYPE_BIN8; + else if (size < (1U << 16)) + return ENCODING_TYPE_BIN16; + else + return ENCODING_TYPE_BIN32; +} + +inline EncodingType EncodeType(const EmptyVariant& /*empty*/) { + return ENCODING_TYPE_NIL; +} + +// Variant is encoded as a single-element map, with the type index as the key. +template <typename... Types> +inline EncodingType EncodeType(const Variant<Types...>& /*variant*/) { + return EncodeMapType(1); +} + +template <typename T> +inline constexpr EncodingType EncodeType(const StringWrapper<T>& value) { + return EncodeStringType(value.length()); +} + +inline constexpr EncodingType EncodeType(const std::string& value) { + return EncodeStringType(value.length()); +} + +template <typename T, std::size_t Size> +inline constexpr EncodingType EncodeType(const std::array<T, Size>& /*value*/) { + return EncodeArrayType(Size); +} + +template <typename T> +inline constexpr EncodingType EncodeType(const ArrayWrapper<T>& value) { + return EncodeArrayType(value.size()); +} + +template <typename T, typename Allocator> +inline constexpr EncodingType EncodeType( + const std::vector<T, Allocator>& value) { + return EncodeArrayType(value.size()); +} + +template <typename Key, typename T, typename Compare, typename Allocator> +inline constexpr EncodingType EncodeType( + const std::map<Key, T, Compare, Allocator>& value) { + return EncodeMapType(value.size()); +} + +template <typename Key, typename T, typename Hash, typename KeyEqual, + typename Allocator> +inline constexpr EncodingType EncodeType( + const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& value) { + return EncodeMapType(value.size()); +} + +template <typename T> +inline constexpr EncodingType EncodeType(const BufferWrapper<T>& value) { + // BIN size is in bytes. + return EncodeBinType(value.size() * + sizeof(typename BufferWrapper<T>::value_type)); +} + +template <typename T, typename U> +inline constexpr EncodingType EncodeType(const std::pair<T, U>& /*value*/) { + return EncodeArrayType(2); +} + +template <typename... T> +inline constexpr EncodingType EncodeType(const std::tuple<T...>& /*value*/) { + return EncodeArrayType(sizeof...(T)); +} + +// FileHandle is encoded as a FIXEXT2 with a type code for "FileDescriptor" +// and a signed 16-bit index into the pushed fd array. Empty file descriptor +// have an array index of -1. +template <FileHandleMode Mode> +inline constexpr EncodingType EncodeType(const FileHandle<Mode>& /*fd*/) { + return ENCODING_TYPE_FIXEXT2; +} + +// ChannelHandle is encoded as a FIXEXT4 with a type of +// ENCODING_EXT_TYPE_CHANNEL_HANDLE and a signed 32-bit value representing +// a client channel in a remote process. Empty handle has a value of -1. +template <ChannelHandleMode Mode> +inline constexpr EncodingType EncodeType( + const ChannelHandle<Mode>& /*handle*/) { + return ENCODING_TYPE_FIXEXT4; +} + +inline constexpr EncodingType EncodeType(const bool& value) { + return value ? ENCODING_TYPE_TRUE : ENCODING_TYPE_FALSE; +} + +// Type 'char' is a little bit special in that it is distinct from 'signed char' +// and 'unsigned char'. Treating it as an unsigned 8-bit value is safe for +// encoding purposes and nicely handles 7-bit ASCII encodings as FIXINT. +inline constexpr EncodingType EncodeType(const char& value) { + if (value < static_cast<char>(1 << 7)) + return value; + else + return ENCODING_TYPE_UINT8; +} + +inline constexpr EncodingType EncodeType(const uint8_t& value) { + if (value < (1U << 7)) + return value; + else + return ENCODING_TYPE_UINT8; +} +inline constexpr EncodingType EncodeType(const int8_t& value) { + if (value >= -32) + return value; + else + return ENCODING_TYPE_INT8; +} +inline constexpr EncodingType EncodeType(const uint16_t& value) { + if (value < (1U << 7)) + return static_cast<EncodingType>(value); + else if (value < (1U << 8)) + return ENCODING_TYPE_UINT8; + else + return ENCODING_TYPE_UINT16; +} +inline constexpr EncodingType EncodeType(const int16_t& value) { + if (value >= -32 && value <= 127) + return static_cast<EncodingType>(value); + else if (value >= -128 && value <= 127) + return ENCODING_TYPE_INT8; + else + return ENCODING_TYPE_INT16; +} +inline constexpr EncodingType EncodeType(const uint32_t& value) { + if (value < (1U << 7)) + return static_cast<EncodingType>(value); + else if (value < (1U << 8)) + return ENCODING_TYPE_UINT8; + else if (value < (1U << 16)) + return ENCODING_TYPE_UINT16; + else + return ENCODING_TYPE_UINT32; +} +inline constexpr EncodingType EncodeType(const int32_t& value) { + if (value >= -32 && value <= 127) + return static_cast<EncodingType>(value); + else if (value >= -128 && value <= 127) + return ENCODING_TYPE_INT8; + else if (value >= -32768 && value <= 32767) + return ENCODING_TYPE_INT16; + else + return ENCODING_TYPE_INT32; +} +inline constexpr EncodingType EncodeType(const uint64_t& value) { + if (value < (1ULL << 7)) + return static_cast<EncodingType>(value); + else if (value < (1ULL << 8)) + return ENCODING_TYPE_UINT8; + else if (value < (1ULL << 16)) + return ENCODING_TYPE_UINT16; + else if (value < (1ULL << 32)) + return ENCODING_TYPE_UINT32; + else + return ENCODING_TYPE_UINT64; +} +inline constexpr EncodingType EncodeType(const int64_t& value) { + if (value >= -32 && value <= 127) + return static_cast<EncodingType>(value); + else if (value >= -128 && value <= 127) // Effectively [-128, -32). + return ENCODING_TYPE_INT8; + else if (value >= -32768 && value <= 32767) + return ENCODING_TYPE_INT16; + else if (value >= -2147483648 && value <= 2147483647) + return ENCODING_TYPE_INT32; + else + return ENCODING_TYPE_INT64; +} + +inline constexpr EncodingType EncodeType(const float& /*value*/) { + return ENCODING_TYPE_FLOAT32; +} + +inline constexpr EncodingType EncodeType(const double& /*value*/) { + return ENCODING_TYPE_FLOAT64; +} + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_ENCODING_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/enumeration.h b/libs/vr/libpdx/private/pdx/rpc/enumeration.h new file mode 100644 index 0000000000..7a35d31b22 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/enumeration.h @@ -0,0 +1,65 @@ +#ifndef ANDROID_PDX_RPC_ENUMERATION_H_ +#define ANDROID_PDX_RPC_ENUMERATION_H_ + +#include <pdx/rpc/sequence.h> + +namespace android { +namespace pdx { +namespace rpc { + +// Utility for manipulating lists of types. Provides operations to lookup an +// element by type or index. + +namespace detail { + +// Helper type that captures type and index for each element of a type +// enumeration. +template <std::size_t I, typename T> +struct IndexedElement { + using Type = T; + static constexpr std::size_t Index = I; +}; + +// Helper type that captures an IndexSequence and corresponding list of types. +template <typename Is, typename... Ts> +struct ElementIndexer; + +// Partial specialization that generates an instantiation of IndexElement<I, T> +// for each element of a type enumeration using inheritance. Once a type +// enumeration is instantiated this way the compiler is able to deduce either I +// or T from the other using the method below. +template <std::size_t... Is, typename... Ts> +struct ElementIndexer<IndexSequence<Is...>, Ts...> : IndexedElement<Is, Ts>... { +}; + +// Helper function that causes the compiler to deduce an IndexedElement<I, T> +// given T. +template <typename T, std::size_t I> +static IndexedElement<I, T> SelectElementByType(IndexedElement<I, T>); + +// Helper function that causes the compiler to deduce an IndexedElement<I, T> +// given I. +template <std::size_t I, typename T> +static IndexedElement<I, T> SelectElementByIndex(IndexedElement<I, T>); + +} // namespace detail + +// Deduces the IndexedElement<I, T> given T and a type sequence Ts. This may be +// used to determine the index of T within Ts at compile time. +template <typename T, typename... Ts> +using ElementForType = decltype(detail::SelectElementByType<T>( + detail::ElementIndexer<typename IndexSequenceFor<Ts...>::type, Ts...>{})); + +// Deduces the IndexedElement<I, T> given I and a type sequence Ts. This may be +// used to determine the type of the element at index I within Ts at compile +// time. Tuple operations may also be used to accomplish the same task, however +// this implementation is provided here for symmetry. +template <std::size_t I, typename... Ts> +using ElementForIndex = decltype(detail::SelectElementByIndex<I>( + detail::ElementIndexer<typename IndexSequenceFor<Ts...>::type, Ts...>{})); + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_ENUMERATION_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/find_replace.h b/libs/vr/libpdx/private/pdx/rpc/find_replace.h new file mode 100644 index 0000000000..b4b086bd05 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/find_replace.h @@ -0,0 +1,45 @@ +#ifndef ANDROID_PDX_RPC_FIND_REPLACE_H_ +#define ANDROID_PDX_RPC_FIND_REPLACE_H_ + +#include <type_traits> + +#include <pdx/rpc/copy_cv_reference.h> + +namespace android { +namespace pdx { +namespace rpc { + +// Utility class to capture types to find and replace. +template <typename Find, typename Replace> +struct FindReplace; + +template <typename T, typename U> +using IsSameBaseType = typename std::is_same<typename std::decay<T>::type, + typename std::decay<U>::type>; + +// Replaces the type Subject with type Replace if type Subject is the same type +// as type Find, excluding cv-reference qualifiers in the match. +template <typename Find, typename Replace, typename Subject> +using ReplaceType = + typename std::conditional<IsSameBaseType<Find, Subject>::value, + CopyCVReferenceType<Subject, Replace>, + Subject>::type; + +// Determines whether the type Find (excluding cv-reference qualifiers) is in +// the given parameter pack. +template <typename Find, typename... Types> +struct ContainsType : std::true_type {}; + +template <typename Find, typename First, typename... Rest> +struct ContainsType<Find, First, Rest...> + : std::conditional<IsSameBaseType<Find, First>::value, std::true_type, + ContainsType<Find, Rest...>>::type {}; + +template <typename Find> +struct ContainsType<Find> : std::false_type {}; + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_FIND_REPLACE_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/function_traits.h b/libs/vr/libpdx/private/pdx/rpc/function_traits.h new file mode 100644 index 0000000000..7641b0a7c1 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/function_traits.h @@ -0,0 +1,61 @@ +#ifndef ANDROID_PDX_RPC_FUNCTION_TRAITS_H_ +#define ANDROID_PDX_RPC_FUNCTION_TRAITS_H_ + +#include <type_traits> + +#include <pdx/rpc/type_operators.h> + +namespace android { +namespace pdx { +namespace rpc { + +// Utility type to capture return and argument types of a function signature. +// Examples: +// typedef SignatureType<int(int)> SignatureType; +// using SignatureType = SignatureType<int(int)>; +template <typename T> +using SignatureType = T; + +// Utility class to extract return and argument types from function types. +// Provides nested types for return value, arguments, and full signature. Also +// provides accessor types for individual arguments, argument-arity, and type +// substitution. +template <typename T> +struct FunctionTraits; + +template <typename Return_, typename... Args_> +struct FunctionTraits<Return_(Args_...)> { + using Return = Return_; + using Args = std::tuple<Args_...>; + using Signature = SignatureType<Return_(Args_...)>; + + enum : std::size_t { Arity = sizeof...(Args_) }; + + template <std::size_t Index> + using Arg = typename std::tuple_element<Index, Args>::type; + + template <typename... Params> + using RewriteArgs = + SignatureType<Return_(ConditionalRewrite<Args_, Params>...)>; + + template <typename ReturnType, typename... Params> + using RewriteSignature = + SignatureType<ConditionalRewrite<Return_, ReturnType>( + ConditionalRewrite<Args_, Params>...)>; + + template <template <typename> class Wrapper, typename ReturnType, + typename... Params> + using RewriteSignatureWrapReturn = + SignatureType<Wrapper<ConditionalRewrite<Return_, ReturnType>>( + ConditionalRewrite<Args_, Params>...)>; + + template <typename ReturnType> + using RewriteReturn = + SignatureType<ConditionalRewrite<Return_, ReturnType>(Args_...)>; +}; + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_FUNCTION_TRAITS_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/macros.h b/libs/vr/libpdx/private/pdx/rpc/macros.h new file mode 100644 index 0000000000..aeae9d3e5e --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/macros.h @@ -0,0 +1,148 @@ +#ifndef ANDROID_PDX_RPC_MACROS_H_ +#define ANDROID_PDX_RPC_MACROS_H_ + +// Macros to apply other macros over all elements in a list. +// +// For example, for a macro A(x) and B(x, y): +// - FOR_EACH(A, 1, 2, 3) -> A(1) A(2) A(3). +// - FOR_EACH_BINARY(B, z, 1, 2, 3) -> B(z, 1) B(z, 2) B(z, 3) +// - FOR_EACH_LIST(A, 1, 2, 3) -> A(1), B(2), C(3) +// - FOR_EACH_BINARY_LIST(B, z, 1, 2, 3) -> B(z, 1), B(z, 2), B(z, 3) +// +// Empty lists are supported and will produce no output. + +// Recursive expansion macros. +#define _PDX_EXPAND0(...) __VA_ARGS__ +#define _PDX_EXPAND1(...) _PDX_EXPAND0(_PDX_EXPAND0(_PDX_EXPAND0(__VA_ARGS__))) +#define _PDX_EXPAND2(...) _PDX_EXPAND1(_PDX_EXPAND1(_PDX_EXPAND1(__VA_ARGS__))) +#define _PDX_EXPAND3(...) _PDX_EXPAND2(_PDX_EXPAND2(_PDX_EXPAND2(__VA_ARGS__))) +#define _PDX_EXPAND4(...) _PDX_EXPAND3(_PDX_EXPAND3(_PDX_EXPAND3(__VA_ARGS__))) +#define _PDX_EXPAND(...) _PDX_EXPAND4(_PDX_EXPAND4(_PDX_EXPAND4(__VA_ARGS__))) + +// Required to workaround a bug in the VC++ preprocessor. +#define _PDX_INDIRECT_EXPAND(macro, args) macro args + +// Defines a step separation for macro expansion. +#define _PDX_SEPARATOR + +// Clears any remaining contents wrapped in parentheses. +#define _PDX_CLEAR(...) + +// Introduces a first dummy argument and _PDX_CLEAR as second argument. +#define _PDX_CLEAR_IF_LAST() _, _PDX_CLEAR + +// Returns the first argument of a list. +#define _PDX_FIRST_ARG(first, ...) first + +// Returns the second argument of a list. +#define _PDX_SECOND_ARG(_, second, ...) second + +// Expands the arguments and introduces a separator. +#define _PDX_EXPAND_NEXT_FUNC(_, next_func, ...) \ + _PDX_INDIRECT_EXPAND(_PDX_SECOND_ARG, (_, next_func)) \ + _PDX_SEPARATOR + +// Returns next_func if the next element is not (), or _PDX_CLEAR +// otherwise. +// +// _PDX_CLEAR_IF_LAST inserts an extra first dummy argument if peek is (). +#define _PDX_NEXT_FUNC(next_element, next_func) \ + _PDX_EXPAND_NEXT_FUNC(_PDX_CLEAR_IF_LAST next_element, next_func) + +// Macros for the unary version of PDX_FOR_EACH. + +// Applies the unary macro. Duplicated for macro recursive expansion. +#define _PDX_APPLY_1(macro, head, next, ...) \ + macro(head) _PDX_NEXT_FUNC(next, _PDX_APPLY_2)(macro, next, __VA_ARGS__) + +// Applies the unary macro. Duplicated for macro recursive expansion. +#define _PDX_APPLY_2(macro, head, next, ...) \ + macro(head) _PDX_NEXT_FUNC(next, _PDX_APPLY_1)(macro, next, __VA_ARGS__) + +// Stops expansion if __VA_ARGS__ is empty, calling _PDX_APPLY_1 +// otherwise. +#define _PDX_HANDLE_EMPTY_ARGS(macro, ...) \ + _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_1) \ + (macro, __VA_ARGS__, ()) + +// Applies a unary macro over all the elements in a list. +#define PDX_FOR_EACH(macro, ...) \ + _PDX_EXPAND(_PDX_HANDLE_EMPTY_ARGS(macro, __VA_ARGS__)) + +// Applies the unary macro at the end of a list. Duplicated for macro recursive +// expansion. +#define _PDX_APPLY_LIST_1(macro, head, next, ...) \ + , macro(head) \ + _PDX_NEXT_FUNC(next, _PDX_APPLY_LIST_2)(macro, next, __VA_ARGS__) + +// Applies the unary macro at the end of a list. Duplicated for macro recursive +// expansion. +#define _PDX_APPLY_LIST_2(macro, head, next, ...) \ + , macro(head) \ + _PDX_NEXT_FUNC(next, _PDX_APPLY_LIST_1)(macro, next, __VA_ARGS__) + +// Applies the unary macro at the start of a list. +#define _PDX_APPLY_LIST_0(macro, head, next, ...) \ + macro(head) _PDX_NEXT_FUNC(next, _PDX_APPLY_LIST_1)(macro, next, __VA_ARGS__) + +// Stops expansion if __VA_ARGS__ is empty, calling _PDX_APPLY_LIST_0 +// otherwise. +#define _PDX_HANDLE_EMPTY_LIST(macro, ...) \ + _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_LIST_0) \ + (macro, __VA_ARGS__, ()) + +// Applies a unary macro over all the elements in a list. +#define PDX_FOR_EACH_LIST(macro, ...) \ + _PDX_EXPAND(_PDX_HANDLE_EMPTY_LIST(macro, __VA_ARGS__)) + +// Macros for the binary version of PDX_FOR_EACH. + +// Applies the binary macro. Duplicated for macro recursive expansion. +#define _PDX_APPLY_BINARY_1(macro, arg, head, next, ...) \ + macro(arg, head) \ + _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_2)(macro, arg, next, __VA_ARGS__) + +// Applies the binary macro. Duplicated for macro recursive expansion. +#define _PDX_APPLY_BINARY_2(macro, arg, head, next, ...) \ + macro(arg, head) \ + _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_1)(macro, arg, next, __VA_ARGS__) + +// Version of _PDX_HANDLE_EMPTY_ARGS that takes 1 fixed argument for a +// binary macro. +#define _PDX_HANDLE_EMPTY_ARGS_BINARY(macro, arg, ...) \ + _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_BINARY_1) \ + (macro, arg, __VA_ARGS__, ()) + +// Applies a binary macro over all the elements in a list and a given argument. +#define PDX_FOR_EACH_BINARY(macro, arg, ...) \ + _PDX_EXPAND(_PDX_HANDLE_EMPTY_ARGS_BINARY(macro, arg, __VA_ARGS__)) + +// Applies the binary macro at the end of a list. Duplicated for macro recursive +// expansion. +#define _PDX_APPLY_BINARY_LIST_1(macro, arg, head, next, ...) \ + , macro(arg, head) _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_LIST_2)( \ + macro, arg, next, __VA_ARGS__) + +// Applies the binary macro at the end of a list. Duplicated for macro recursive +// expansion. +#define _PDX_APPLY_BINARY_LIST_2(macro, arg, head, next, ...) \ + , macro(arg, head) _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_LIST_1)( \ + macro, arg, next, __VA_ARGS__) + +// Applies the binary macro at the start of a list. Duplicated for macro +// recursive expansion. +#define _PDX_APPLY_BINARY_LIST_0(macro, arg, head, next, ...) \ + macro(arg, head) _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_LIST_1)( \ + macro, arg, next, __VA_ARGS__) + +// Version of _PDX_HANDLE_EMPTY_LIST that takes 1 fixed argument for a +// binary macro. +#define _PDX_HANDLE_EMPTY_LIST_BINARY(macro, arg, ...) \ + _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_BINARY_LIST_0) \ + (macro, arg, __VA_ARGS__, ()) + +// Applies a binary macro over all the elements in a list and a given argument. +#define PDX_FOR_EACH_BINARY_LIST(macro, arg, ...) \ + _PDX_EXPAND(_PDX_HANDLE_EMPTY_LIST_BINARY(macro, arg, __VA_ARGS__)) + +#endif // ANDROID_PDX_RPC_MACROS_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/message_buffer.h b/libs/vr/libpdx/private/pdx/rpc/message_buffer.h new file mode 100644 index 0000000000..ba4e86e0de --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/message_buffer.h @@ -0,0 +1,22 @@ +#ifndef ANDROID_PDX_RPC_MESSAGE_BUFFER_H_ +#define ANDROID_PDX_RPC_MESSAGE_BUFFER_H_ + +#include <pdx/rpc/thread_local_buffer.h> + +namespace android { +namespace pdx { +namespace rpc { + +// Utility type for thread-local buffers, providing suitable defaults for most +// situations. Independent thread-local buffers may be created by using +// different types for Slot -- ThreadLocalSlot, ThreadLocalTypedSlot and +// ThreadLocalIndexedSlot provide utilities for building these types. +template <typename Slot, std::size_t Capacity = 4096, typename T = std::uint8_t, + typename Allocator = DefaultInitializationAllocator<T>> +using MessageBuffer = ThreadLocalBuffer<T, Allocator, Capacity, Slot>; + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_MESSAGE_BUFFER_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/payload.h b/libs/vr/libpdx/private/pdx/rpc/payload.h new file mode 100644 index 0000000000..a48a64ce15 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/payload.h @@ -0,0 +1,157 @@ +#ifndef ANDROID_PDX_RPC_PAYLOAD_H_ +#define ANDROID_PDX_RPC_PAYLOAD_H_ + +#include <iterator> + +#include <pdx/client.h> +#include <pdx/rpc/message_buffer.h> +#include <pdx/service.h> + +namespace android { +namespace pdx { +namespace rpc { + +// Implements the payload interface, required by Serialize/Deserialize, on top +// of a thread-local MessageBuffer. +template <typename Slot> +class MessagePayload { + public: + using BufferType = typename MessageBuffer<Slot>::BufferType; + using ValueType = typename MessageBuffer<Slot>::ValueType; + + // Constructs a MessagePayload with an empty TLS buffer. + MessagePayload() + : buffer_(MessageBuffer<Slot>::GetEmptyBuffer()), + cursor_(buffer_.begin()), + const_cursor_(buffer_.cbegin()) {} + + // Returns a reference to the cursor iterator to be used during serialization + // into the underlying MessageBuffer. + typename BufferType::iterator& Cursor() { return cursor_; } + + // Returns a reference to the const cursor iterator at the beginning of the + // underlying MessageBuffer. + typename BufferType::const_iterator& ConstCursor() { return const_cursor_; } + + // Returns a const iterator marking the end of the underlying MessageBuffer. + typename BufferType::const_iterator ConstEnd() { return buffer_.cend(); } + + // Resizes the underlying MessageBuffer and sets the cursor to the beginning. + void Resize(std::size_t size) { + buffer_.resize(size); + cursor_ = buffer_.begin(); + const_cursor_ = buffer_.cbegin(); + } + + // Resets the read cursor so that data can be read from the buffer again. + void Rewind() { const_cursor_ = buffer_.cbegin(); } + + // Adds |size| bytes to the size of the underlying MessageBuffer and positions + // the cursor at the beginning of the extended region. + void Extend(std::size_t size) { + const std::size_t offset = buffer_.size(); + buffer_.resize(offset + size); + cursor_ = buffer_.begin() + offset; + const_cursor_ = buffer_.cbegin() + offset; + } + + // Clears the underlying MessageBuffer and sets the cursor to the beginning. + void Clear() { + buffer_.clear(); + cursor_ = buffer_.begin(); + const_cursor_ = buffer_.cbegin(); + } + + ValueType* Data() { return buffer_.data(); } + const ValueType* Data() const { return buffer_.data(); } + std::size_t Size() const { return buffer_.size(); } + std::size_t Capacity() const { return buffer_.capacity(); } + + private: + BufferType& buffer_; + typename BufferType::iterator cursor_; + typename BufferType::const_iterator const_cursor_; + + MessagePayload(const MessagePayload<Slot>&) = delete; + void operator=(const MessagePayload<Slot>&) = delete; +}; + +// Implements the payload interface for service-side RPCs. Handles translating +// between remote and local handle spaces automatically. +template <typename Slot> +class ServicePayload : public MessagePayload<Slot>, + public MessageWriter, + public MessageReader { + public: + ServicePayload(Message& message) : message_(message) {} + + // MessageWriter + void* GetNextWriteBufferSection(size_t size) override { + this->Extend(size); + return &*this->Cursor(); + } + + OutputResourceMapper* GetOutputResourceMapper() override { return &message_; } + + // MessageReader + BufferSection GetNextReadBufferSection() override { + return {&*this->ConstCursor(), &*this->ConstEnd()}; + } + + void ConsumeReadBufferSectionData(const void* new_start) override { + std::advance(this->ConstCursor(), + PointerDistance(new_start, &*this->ConstCursor())); + } + + InputResourceMapper* GetInputResourceMapper() override { return &message_; } + + private: + Message& message_; +}; + +// Implements the payload interface for client-side RPCs. Handles gathering file +// handles to be sent over IPC automatically. +template <typename Slot> +class ClientPayload : public MessagePayload<Slot>, + public MessageWriter, + public MessageReader { + public: + using ContainerType = + MessageBuffer<ThreadLocalTypeSlot<ClientPayload<Slot>>, 1024u, int>; + using BufferType = typename ContainerType::BufferType; + + ClientPayload(Transaction& transaction) : transaction_{transaction} {} + + // MessageWriter + void* GetNextWriteBufferSection(size_t size) override { + this->Extend(size); + return &*this->Cursor(); + } + + OutputResourceMapper* GetOutputResourceMapper() override { + return &transaction_; + } + + // MessageReader + BufferSection GetNextReadBufferSection() override { + return {&*this->ConstCursor(), &*this->ConstEnd()}; + } + + void ConsumeReadBufferSectionData(const void* new_start) override { + std::advance(this->ConstCursor(), + PointerDistance(new_start, &*this->ConstCursor())); + } + + InputResourceMapper* GetInputResourceMapper() override { + return &transaction_; + } + + private: + Transaction& transaction_; +}; + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_PAYLOAD_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h new file mode 100644 index 0000000000..d496719a58 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h @@ -0,0 +1,38 @@ +#ifndef ANDROID_PDX_RPC_POINTER_WRAPPER_H_ +#define ANDROID_PDX_RPC_POINTER_WRAPPER_H_ + +namespace android { +namespace pdx { +namespace rpc { + +// Wrapper class for pointers to any serializable type. This class is used by +// serialization/deserialization to handle pointers to objects that are to be +// serialized or deserialized. +template <typename T> +class PointerWrapper { + public: + using BaseType = T; + + PointerWrapper(T* pointer) : pointer_(pointer) {} + PointerWrapper(const PointerWrapper&) = default; + PointerWrapper(PointerWrapper&&) = default; + PointerWrapper& operator=(const PointerWrapper&) = default; + PointerWrapper& operator=(PointerWrapper&&) = default; + + T& Dereference() { return *pointer_; } + const T& Dereference() const { return *pointer_; } + + private: + T* pointer_; +}; + +template <typename T> +PointerWrapper<T> WrapPointer(T* pointer) { + return PointerWrapper<T>(pointer); +} + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_POINTER_WRAPPER_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/remote_method.h b/libs/vr/libpdx/private/pdx/rpc/remote_method.h new file mode 100644 index 0000000000..505c63b1bf --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/remote_method.h @@ -0,0 +1,473 @@ +#ifndef ANDROID_PDX_RPC_REMOTE_METHOD_H_ +#define ANDROID_PDX_RPC_REMOTE_METHOD_H_ + +#include <tuple> +#include <type_traits> + +#include <pdx/client.h> +#include <pdx/rpc/argument_encoder.h> +#include <pdx/rpc/message_buffer.h> +#include <pdx/rpc/payload.h> +#include <pdx/rpc/remote_method_type.h> +#include <pdx/service.h> +#include <pdx/status.h> + +namespace android { +namespace pdx { +namespace rpc { + +#ifdef __clang__ +// Stand-in type to avoid Clang compiler bug. Clang currently has a bug where +// performing parameter pack expansion for arguments with empty packs causes a +// compiler crash. Provide a substitute void type and specializations/overloads +// of CheckArgumentTypes and DispatchRemoteMethod to work around this problem. +struct Void {}; + +// Evaluates to true if the method type is <any>(Void), false otherwise. +template <typename RemoteMethodType> +using IsVoidMethod = typename std::integral_constant< + bool, RemoteMethodType::Traits::Arity == 1 && + std::is_same<typename RemoteMethodType::Traits::template Arg<0>, + Void>::value>; + +// Utility to determine if a method is of type <any>(Void). +template <typename RemoteMethodType> +using EnableIfVoidMethod = + typename std::enable_if<IsVoidMethod<RemoteMethodType>::value>::type; + +// Utility to determine if a method is not of type <any>(Void). +template <typename RemoteMethodType> +using EnableIfNotVoidMethod = + typename std::enable_if<!IsVoidMethod<RemoteMethodType>::value>::type; + +#else +// GCC works fine with void argument types, always enable the regular +// implementation of DispatchRemoteMethod. +using Void = void; +template <typename RemoteMethodType> +using EnableIfVoidMethod = void; +template <typename RemoteMethodType> +using EnableIfNotVoidMethod = void; +#endif + +// Helper type trait to specialize InvokeRemoteMethods for return types that +// can be obtained directly from Transaction::Send<T>() without deserializing +// reply payload. +template <typename T> +struct IsDirectReturn : std::false_type {}; + +template <> +struct IsDirectReturn<void> : std::true_type {}; + +template <> +struct IsDirectReturn<int> : std::true_type {}; + +template <> +struct IsDirectReturn<LocalHandle> : std::true_type {}; + +template <> +struct IsDirectReturn<LocalChannelHandle> : std::true_type {}; + +template <typename Return, typename Type = void> +using EnableIfDirectReturn = + typename std::enable_if<IsDirectReturn<Return>::value, Type>::type; + +template <typename Return, typename Type = void> +using EnableIfNotDirectReturn = + typename std::enable_if<!IsDirectReturn<Return>::value, Type>::type; + +// Utility class to invoke a method with arguments packed in a tuple. +template <typename Class, typename T> +class UnpackArguments; + +// Utility class to invoke a method with arguments packed in a tuple. +template <typename Class, typename Return, typename... Args> +class UnpackArguments<Class, Return(Args...)> { + public: + using ArgsTupleType = std::tuple<typename std::decay<Args>::type...>; + using MethodType = Return (Class::*)(Message&, Args...); + + UnpackArguments(Class& instance, MethodType method, Message& message, + ArgsTupleType& parameters) + : instance_(instance), + method_(method), + message_(message), + parameters_(parameters) {} + + // Invokes method_ on intance_ with the packed arguments from parameters_. + Return Invoke() { + constexpr auto Arity = sizeof...(Args); + return static_cast<Return>(InvokeHelper(MakeIndexSequence<Arity>{})); + } + + private: + Class& instance_; + MethodType method_; + Message& message_; + ArgsTupleType& parameters_; + + template <std::size_t... Index> + Return InvokeHelper(IndexSequence<Index...>) { + return static_cast<Return>((instance_.*method_)( + message_, + std::get<Index>(std::forward<ArgsTupleType>(parameters_))...)); + } + + UnpackArguments(const UnpackArguments&) = delete; + void operator=(const UnpackArguments&) = delete; +}; + +// Returns an error code from a remote method to the client. May be called +// either during dispatch of the remote method handler or at a later time if the +// message is moved for delayed response. +inline void RemoteMethodError(Message& message, int error_code) { + const auto status = message.ReplyError(error_code); + ALOGE_IF(!status, "RemoteMethodError: Failed to reply to message: %s", + status.GetErrorMessage().c_str()); +} + +// Returns a value from a remote method to the client. The usual method to +// return a value during dispatch of a remote method is to simply use a return +// statement in the handler. If the message is moved however, these methods may +// be used to return a value at a later time, outside of initial dispatch. + +// Overload for direct return types. +template <typename RemoteMethodType, typename Return> +EnableIfDirectReturn<typename RemoteMethodType::Return> RemoteMethodReturn( + Message& message, const Return& return_value) { + const auto status = message.Reply(return_value); + ALOGE_IF(!status, "RemoteMethodReturn: Failed to reply to message: %s", + status.GetErrorMessage().c_str()); +} + +// Overload for non-direct return types. +template <typename RemoteMethodType, typename Return> +EnableIfNotDirectReturn<typename RemoteMethodType::Return> RemoteMethodReturn( + Message& message, const Return& return_value) { + using Signature = typename RemoteMethodType::template RewriteReturn<Return>; + rpc::ServicePayload<ReplyBuffer> payload(message); + MakeArgumentEncoder<Signature>(&payload).EncodeReturn(return_value); + + auto ret = message.WriteAll(payload.Data(), payload.Size()); + auto status = message.Reply(ret); + ALOGE_IF(!status, "RemoteMethodReturn: Failed to reply to message: %s", + status.GetErrorMessage().c_str()); +} + +// Overload for Status<void> return types. +template <typename RemoteMethodType> +void RemoteMethodReturn(Message& message, const Status<void>& return_value) { + if (return_value) + RemoteMethodReturn<RemoteMethodType>(message, 0); + else + RemoteMethodError(message, return_value.error()); +} + +// Overload for Status<T> return types. This overload forwards the underlying +// value or error within the Status<T>. +template <typename RemoteMethodType, typename Return> +void RemoteMethodReturn(Message& message, const Status<Return>& return_value) { + if (return_value) + RemoteMethodReturn<RemoteMethodType, Return>(message, return_value.get()); + else + RemoteMethodError(message, return_value.error()); +} + +// Dispatches a method by deserializing arguments from the given Message, with +// compile-time interface check. Overload for void return types. +template <typename RemoteMethodType, typename Class, typename... Args, + typename = EnableIfNotVoidMethod<RemoteMethodType>> +void DispatchRemoteMethod(Class& instance, + void (Class::*method)(Message&, Args...), + Message& message, + std::size_t max_capacity = InitialBufferCapacity) { + using Signature = typename RemoteMethodType::template RewriteArgs<Args...>; + rpc::ServicePayload<ReceiveBuffer> payload(message); + payload.Resize(max_capacity); + + Status<size_t> read_status = message.Read(payload.Data(), payload.Size()); + if (!read_status) { + RemoteMethodError(message, read_status.error()); + return; + } + + payload.Resize(read_status.get()); + + ErrorType error; + auto decoder = MakeArgumentDecoder<Signature>(&payload); + auto arguments = decoder.DecodeArguments(&error); + if (error) { + RemoteMethodError(message, EIO); + return; + } + + UnpackArguments<Class, Signature>(instance, method, message, arguments) + .Invoke(); + // Return to the caller unless the message was moved. + if (message) + RemoteMethodReturn<RemoteMethodType>(message, 0); +} + +// Dispatches a method by deserializing arguments from the given Message, with +// compile-time interface signature check. Overload for generic return types. +template <typename RemoteMethodType, typename Class, typename Return, + typename... Args, typename = EnableIfNotVoidMethod<RemoteMethodType>> +void DispatchRemoteMethod(Class& instance, + Return (Class::*method)(Message&, Args...), + Message& message, + std::size_t max_capacity = InitialBufferCapacity) { + using Signature = + typename RemoteMethodType::template RewriteSignature<Return, Args...>; + rpc::ServicePayload<ReceiveBuffer> payload(message); + payload.Resize(max_capacity); + + Status<size_t> read_status = message.Read(payload.Data(), payload.Size()); + if (!read_status) { + RemoteMethodError(message, read_status.error()); + return; + } + + payload.Resize(read_status.get()); + + ErrorType error; + auto decoder = MakeArgumentDecoder<Signature>(&payload); + auto arguments = decoder.DecodeArguments(&error); + if (error) { + RemoteMethodError(message, EIO); + return; + } + + auto return_value = + UnpackArguments<Class, Signature>(instance, method, message, arguments) + .Invoke(); + // Return the value to the caller unless the message was moved. + if (message) + RemoteMethodReturn<RemoteMethodType>(message, return_value); +} + +// Dispatches a method by deserializing arguments from the given Message, with +// compile-time interface signature check. Overload for Status<T> return types. +template <typename RemoteMethodType, typename Class, typename Return, + typename... Args, typename = EnableIfNotVoidMethod<RemoteMethodType>> +void DispatchRemoteMethod(Class& instance, + Status<Return> (Class::*method)(Message&, Args...), + Message& message, + std::size_t max_capacity = InitialBufferCapacity) { + using Signature = + typename RemoteMethodType::template RewriteSignature<Return, Args...>; + using InvokeSignature = + typename RemoteMethodType::template RewriteSignatureWrapReturn< + Status, Return, Args...>; + rpc::ServicePayload<ReceiveBuffer> payload(message); + payload.Resize(max_capacity); + + Status<size_t> read_status = message.Read(payload.Data(), payload.Size()); + if (!read_status) { + RemoteMethodError(message, read_status.error()); + return; + } + + payload.Resize(read_status.get()); + + ErrorType error; + auto decoder = MakeArgumentDecoder<Signature>(&payload); + auto arguments = decoder.DecodeArguments(&error); + if (error) { + RemoteMethodError(message, EIO); + return; + } + + auto return_value = UnpackArguments<Class, InvokeSignature>( + instance, method, message, arguments) + .Invoke(); + // Return the value to the caller unless the message was moved. + if (message) + RemoteMethodReturn<RemoteMethodType>(message, return_value); +} + +#ifdef __clang__ +// Overloads to handle Void argument type without exploding clang. + +// Overload for void return type. +template <typename RemoteMethodType, typename Class, + typename = EnableIfVoidMethod<RemoteMethodType>> +void DispatchRemoteMethod(Class& instance, void (Class::*method)(Message&), + Message& message) { + (instance.*method)(message); + // Return to the caller unless the message was moved. + if (message) + RemoteMethodReturn<RemoteMethodType>(message, 0); +} + +// Overload for generic return type. +template <typename RemoteMethodType, typename Class, typename Return, + typename = EnableIfVoidMethod<RemoteMethodType>> +void DispatchRemoteMethod(Class& instance, Return (Class::*method)(Message&), + Message& message) { + auto return_value = (instance.*method)(message); + // Return the value to the caller unless the message was moved. + if (message) + RemoteMethodReturn<RemoteMethodType>(message, return_value); +} + +// Overload for Status<T> return type. +template <typename RemoteMethodType, typename Class, typename Return, + typename = EnableIfVoidMethod<RemoteMethodType>> +void DispatchRemoteMethod(Class& instance, + Status<Return> (Class::*method)(Message&), + Message& message) { + auto return_value = (instance.*method)(message); + // Return the value to the caller unless the message was moved. + if (message) + RemoteMethodReturn<RemoteMethodType>(message, return_value); +} +#endif + +} // namespace rpc + +// Definitions for template methods declared in pdx/client.h. + +template <int Opcode, typename T> +struct CheckArgumentTypes; + +template <int Opcode, typename Return, typename... Args> +struct CheckArgumentTypes<Opcode, Return(Args...)> { + template <typename R> + static typename rpc::EnableIfDirectReturn<R, Status<R>> Invoke(Client& client, + Args... args) { + Transaction trans{client}; + rpc::ClientPayload<rpc::SendBuffer> payload{trans}; + rpc::MakeArgumentEncoder<Return(Args...)>(&payload).EncodeArguments( + std::forward<Args>(args)...); + return trans.Send<R>(Opcode, payload.Data(), payload.Size(), nullptr, 0); + } + + template <typename R> + static typename rpc::EnableIfNotDirectReturn<R, Status<R>> Invoke( + Client& client, Args... args) { + Transaction trans{client}; + + rpc::ClientPayload<rpc::SendBuffer> send_payload{trans}; + rpc::MakeArgumentEncoder<Return(Args...)>(&send_payload) + .EncodeArguments(std::forward<Args>(args)...); + + rpc::ClientPayload<rpc::ReplyBuffer> reply_payload{trans}; + reply_payload.Resize(reply_payload.Capacity()); + + Status<R> result; + auto status = + trans.Send<void>(Opcode, send_payload.Data(), send_payload.Size(), + reply_payload.Data(), reply_payload.Size()); + if (!status) { + result.SetError(status.error()); + } else { + R return_value; + rpc::ErrorType error = + rpc::MakeArgumentDecoder<Return(Args...)>(&reply_payload) + .DecodeReturn(&return_value); + + switch (error.error_code()) { + case rpc::ErrorCode::NO_ERROR: + result.SetValue(std::move(return_value)); + break; + + // This error is returned when ArrayWrapper/StringWrapper is too + // small to receive the payload. + case rpc::ErrorCode::INSUFFICIENT_DESTINATION_SIZE: + result.SetError(ENOBUFS); + break; + + default: + result.SetError(EIO); + break; + } + } + return result; + } + + template <typename R> + static typename rpc::EnableIfDirectReturn<R, Status<void>> InvokeInPlace( + Client& client, R* return_value, Args... args) { + Transaction trans{client}; + + rpc::ClientPayload<rpc::SendBuffer> send_payload{trans}; + rpc::MakeArgumentEncoder<Return(Args...)>(&send_payload) + .EncodeArguments(std::forward<Args>(args)...); + + Status<void> result; + auto status = trans.Send<R>(Opcode, send_payload.Data(), + send_payload.Size(), nullptr, 0); + if (status) { + *return_value = status.take(); + result.SetValue(); + } else { + result.SetError(status.error()); + } + return result; + } + + template <typename R> + static typename rpc::EnableIfNotDirectReturn<R, Status<void>> InvokeInPlace( + Client& client, R* return_value, Args... args) { + Transaction trans{client}; + + rpc::ClientPayload<rpc::SendBuffer> send_payload{trans}; + rpc::MakeArgumentEncoder<Return(Args...)>(&send_payload) + .EncodeArguments(std::forward<Args>(args)...); + + rpc::ClientPayload<rpc::ReplyBuffer> reply_payload{trans}; + reply_payload.Resize(reply_payload.Capacity()); + + auto result = + trans.Send<void>(Opcode, send_payload.Data(), send_payload.Size(), + reply_payload.Data(), reply_payload.Size()); + if (result) { + rpc::ErrorType error = + rpc::MakeArgumentDecoder<Return(Args...)>(&reply_payload) + .DecodeReturn(return_value); + + switch (error.error_code()) { + case rpc::ErrorCode::NO_ERROR: + result.SetValue(); + break; + + // This error is returned when ArrayWrapper/StringWrapper is too + // small to receive the payload. + case rpc::ErrorCode::INSUFFICIENT_DESTINATION_SIZE: + result.SetError(ENOBUFS); + break; + + default: + result.SetError(EIO); + break; + } + } + return result; + } +}; + +// Invokes the remote method with opcode and signature described by +// |RemoteMethodType|. +template <typename RemoteMethodType, typename... Args> +Status<typename RemoteMethodType::Return> Client::InvokeRemoteMethod( + Args&&... args) { + return CheckArgumentTypes< + RemoteMethodType::Opcode, + typename RemoteMethodType::template RewriteArgs<Args...>>:: + template Invoke<typename RemoteMethodType::Return>( + *this, std::forward<Args>(args)...); +} + +template <typename RemoteMethodType, typename Return, typename... Args> +Status<void> Client::InvokeRemoteMethodInPlace(Return* return_value, + Args&&... args) { + return CheckArgumentTypes< + RemoteMethodType::Opcode, + typename RemoteMethodType::template RewriteSignature<Return, Args...>>:: + template InvokeInPlace(*this, return_value, std::forward<Args>(args)...); +} + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_REMOTE_METHOD_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h b/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h new file mode 100644 index 0000000000..cf9a189e60 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h @@ -0,0 +1,73 @@ +#ifndef ANDROID_PDX_RPC_REMOTE_METHOD_TYPE_H_ +#define ANDROID_PDX_RPC_REMOTE_METHOD_TYPE_H_ + +#include <cstddef> +#include <tuple> +#include <type_traits> + +#include <pdx/rpc/enumeration.h> +#include <pdx/rpc/function_traits.h> + +namespace android { +namespace pdx { +namespace rpc { + +// Utility class binding a remote method opcode to its function signature. +// Describes the interface between RPC clients and services for a single method. +template <int Opcode_, typename Signature_> +struct RemoteMethodType { + typedef FunctionTraits<Signature_> Traits; + + enum : int { Opcode = Opcode_ }; + + typedef typename Traits::Signature Signature; + typedef typename Traits::Return Return; + typedef typename Traits::Args Args; + + template <typename... Params> + using RewriteArgs = typename Traits::template RewriteArgs<Params...>; + + template <typename ReturnType, typename... Params> + using RewriteSignature = + typename Traits::template RewriteSignature<ReturnType, Params...>; + + template <template <typename> class Wrapper, typename ReturnType, + typename... Params> + using RewriteSignatureWrapReturn = + typename Traits::template RewriteSignatureWrapReturn<Wrapper, ReturnType, + Params...>; + + template <typename ReturnType> + using RewriteReturn = typename Traits::template RewriteReturn<ReturnType>; +}; + +// Utility class representing a set of related RemoteMethodTypes. Describes the +// interface between RPC clients and services as a set of methods. +template <typename... MethodTypes> +struct RemoteAPI { + typedef std::tuple<MethodTypes...> Methods; + enum : std::size_t { Length = sizeof...(MethodTypes) }; + + template <std::size_t Index> + using Method = typename std::tuple_element<Index, Methods>::type; + + template <typename MethodType> + static constexpr std::size_t MethodIndex() { + return ElementForType<MethodType, MethodTypes...>::Index; + } +}; + +// Macro to simplify defining remote method signatures. Remote method signatures +// are specified by defining a RemoteMethodType for each remote method. +#define PDX_REMOTE_METHOD(name, opcode, ... /*signature*/) \ + using name = ::android::pdx::rpc::RemoteMethodType<opcode, __VA_ARGS__> + +// Macro to simplify defining a set of remote method signatures. +#define PDX_REMOTE_API(name, ... /*methods*/) \ + using name = ::android::pdx::rpc::RemoteAPI<__VA_ARGS__> + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_REMOTE_METHOD_TYPE_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/sequence.h b/libs/vr/libpdx/private/pdx/rpc/sequence.h new file mode 100644 index 0000000000..5fd898a846 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/sequence.h @@ -0,0 +1,56 @@ +#ifndef ANDROID_PDX_RPC_SEQUENCE_H_ +#define ANDROID_PDX_RPC_SEQUENCE_H_ + +#include <cstdint> + +namespace android { +namespace pdx { +namespace rpc { + +// Provides a C++11 implementation of C++14 index_sequence and +// make_index_sequence for compatibility with common compilers. This +// implementation may be conditionally replaced with compiler-provided versions +// when C++14 support is available. + +// Utility to capture a sequence of unsigned indices. +template <std::size_t... I> +struct IndexSequence { + using type = IndexSequence; + using value_type = std::size_t; + static constexpr std::size_t size() { return sizeof...(I); } +}; + +namespace detail { + +// Helper class to merge and renumber sequence parts in log N instantiations. +template <typename S1, typename S2> +struct MergeSequencesAndRenumber; + +template <std::size_t... I1, std::size_t... I2> +struct MergeSequencesAndRenumber<IndexSequence<I1...>, IndexSequence<I2...>> + : IndexSequence<I1..., (sizeof...(I1) + I2)...> {}; + +} // namespace detail + +// Utility to build an IndexSequence with N indices. +template <std::size_t N> +struct MakeIndexSequence : detail::MergeSequencesAndRenumber< + typename MakeIndexSequence<N / 2>::type, + typename MakeIndexSequence<N - N / 2>::type> {}; + +// Identity sequences. +template <> +struct MakeIndexSequence<0> : IndexSequence<> {}; +template <> +struct MakeIndexSequence<1> : IndexSequence<0> {}; + +// Utility to build an IndexSequence with indices for each element of a +// parameter pack. +template <typename... T> +using IndexSequenceFor = MakeIndexSequence<sizeof...(T)>; + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_SEQUENCE_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/serializable.h b/libs/vr/libpdx/private/pdx/rpc/serializable.h new file mode 100644 index 0000000000..04a4352268 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/serializable.h @@ -0,0 +1,150 @@ +#ifndef ANDROID_PDX_RPC_SERIALIZABLE_H_ +#define ANDROID_PDX_RPC_SERIALIZABLE_H_ + +#include <cstddef> +#include <string> +#include <tuple> + +#include <pdx/message_reader.h> +#include <pdx/message_writer.h> + +#include "macros.h" +#include "serialization.h" + +namespace android { +namespace pdx { +namespace rpc { + +// This file provides utilities to define serializable types for communication +// between clients and services. Supporting efficient, typed communication +// protocols is the primary goal, NOT providing a general-purpose solution for +// all your C++ serialization needs. Features that are not aligned to the goals +// are not supported, such as static/const member serialization and serializable +// types with virtual methods (requiring a virtual destructor). + +// Captures the type and value of a pointer to member. Pointer to members are +// essentially compile-time constant offsets that can be stored in the type +// system without adding to the size of the structures they describe. This +// library uses this property to implement a limited form of reflection for +// serialization/deserialization functions. +template <typename T, T> +struct MemberPointer; + +template <typename Type, typename Class, Type Class::*Pointer> +struct MemberPointer<Type Class::*, Pointer> { + // Type of the member pointer this type represents. + using PointerType = Type Class::*; + + // Resolves a pointer to member with the given instance, yielding a + // reference to the member in that instance. + static Type& Resolve(Class& instance) { return (instance.*Pointer); } + static const Type& Resolve(const Class& instance) { + return (instance.*Pointer); + } +}; + +// Describes a set of members to be serialized/deserialized by this library. The +// parameter pack MemberPointers takes a list of MemberPointer types that +// describe each member to participate in serialization/deserialization. +template <typename T, typename... MemberPointers> +struct SerializableMembersType { + using Type = T; + + // The number of member pointers described by this type. + enum : std::size_t { MemberCount = sizeof...(MemberPointers) }; + + // The member pointers described by this type. + using Members = std::tuple<MemberPointers...>; + + // Accessor for individual member pointer types. + template <std::size_t Index> + using At = typename std::tuple_element<Index, Members>::type; +}; + +// Classes must do the following to correctly define a serializable type: +// 1. Define a type called "SerializableMembers" as a template instantiation +// of SerializableMembersType, describing the members of the class to +// participate in serialization (presumably all of them). Use the macro +// PDX_SERIALIZABLE_MEMBERS(...) below to aid the correct type +// definition. This type should be private to prevent leaking member +// access information. +// 2. Make SerializableTraits and HasSerilizableMembers types a friend of +// the class. The macro PDX_SERIALIZABLE_MEMEBRS(...) takes care of +// this automatically. +// 3. Define a public default constructor, if necessary. Deserialization +// requires instances to be default-constructible. +// +// Example usage: +// class MySerializableType : public AnotherBaseType { +// public: +// MySerializableType(); +// ... +// private: +// int a; +// string b; +// PDX_SERIALIZABLE_MEMBERS(MySerializableType, a, b); +// }; +// +// Note that const and static member serialization is not supported. + +template <typename T> +class SerializableTraits { + public: + // Gets the serialized size of type T. + static std::size_t GetSerializedSize(const T& value) { + return GetEncodingSize(EncodeArrayType(SerializableMembers::MemberCount)) + + GetMembersSize<SerializableMembers>(value); + } + + // Serializes type T. + static void SerializeObject(const T& value, MessageWriter* writer, + void*& buffer) { + SerializeArrayEncoding(EncodeArrayType(SerializableMembers::MemberCount), + SerializableMembers::MemberCount, buffer); + SerializeMembers<SerializableMembers>(value, writer, buffer); + } + + // Deserializes type T. + static ErrorType DeserializeObject(T* value, MessageReader* reader, + const void*& start, const void* end) { + EncodingType encoding; + std::size_t size; + + if (const auto error = + DeserializeArrayType(&encoding, &size, reader, start, end)) { + return error; + } else if (size != SerializableMembers::MemberCount) { + return ErrorCode::UNEXPECTED_TYPE_SIZE; + } else { + return DeserializeMembers<SerializableMembers>(value, reader, start, end); + } + } + + private: + using SerializableMembers = typename T::SerializableMembers; +}; + +// Utility macro to define a MemberPointer type for a member name. +#define PDX_MEMBER_POINTER(type, member) \ + ::android::pdx::rpc::MemberPointer<decltype(&type::member), &type::member> + +// Defines a list of MemberPointer types given a list of member names. +#define PDX_MEMBERS(type, ... /*members*/) \ + PDX_FOR_EACH_BINARY_LIST(PDX_MEMBER_POINTER, type, __VA_ARGS__) + +// Defines the serializable members of a type given a list of member names and +// befriends SerializableTraits and HasSerializableMembers for the class. This +// macro handles requirements #1 and #2 above. +#define PDX_SERIALIZABLE_MEMBERS(type, ... /*members*/) \ + template <typename T> \ + friend class ::android::pdx::rpc::SerializableTraits; \ + template <typename, typename> \ + friend struct ::android::pdx::rpc::HasSerializableMembers; \ + using SerializableMembers = ::android::pdx::rpc::SerializableMembersType< \ + type, PDX_MEMBERS(type, __VA_ARGS__)> + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_SERIALIZABLE_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/serialization.h b/libs/vr/libpdx/private/pdx/rpc/serialization.h new file mode 100644 index 0000000000..f12aef1329 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/serialization.h @@ -0,0 +1,1998 @@ +#ifndef ANDROID_PDX_RPC_SERIALIZATION_H_ +#define ANDROID_PDX_RPC_SERIALIZATION_H_ + +#include <cstdint> +#include <cstring> +#include <iterator> +#include <map> +#include <numeric> +#include <sstream> +#include <string> +#include <tuple> +#include <type_traits> +#include <unordered_map> +#include <utility> +#include <vector> + +#include <pdx/channel_handle.h> +#include <pdx/file_handle.h> +#include <pdx/message_reader.h> +#include <pdx/message_writer.h> +#include <pdx/trace.h> +#include <pdx/utility.h> + +#include "array_wrapper.h" +#include "default_initialization_allocator.h" +#include "encoding.h" +#include "pointer_wrapper.h" +#include "string_wrapper.h" +#include "variant.h" + +namespace android { +namespace pdx { +namespace rpc { + +// Automatic serialization/deserialization library based on MessagePack +// (http://msgpack.org). This library provides top level Serialize() and +// Deserialize() functions to encode/decode a variety of data types. +// +// The following data types are supported: +// * Standard signed integer types: int8_t, int16_t, int32_t, and int64_t. +// * Regular signed integer types equivalent to the standard types: +// signed char, short, int, long, and long long. +// * Standard unsigned integer types: uint8_t, uint16_t, uint32_t, and +// uint64_t. +// * Regular unsigned integer types equivalent to the standard types: +// unsigned char, unsigned short, unsigned int, unsigned long, +// and unsigned long long. +// * char without signed/unsigned qualifiers. +// * bool. +// * std::vector with value type of any supported type, including nesting. +// * std::string. +// * std::tuple with elements of any supported type, including nesting. +// * std::pair with elements of any supported type, including nesting. +// * std::map with keys and values of any supported type, including nesting. +// * std::unordered_map with keys and values of any supported type, including +// nesting. +// * std::array with values of any supported type, including nesting. +// * ArrayWrapper of any supported basic type. +// * BufferWrapper of any POD type. +// * StringWrapper of any supported char type. +// * User types with correctly defined SerializableMembers member type. +// +// Planned support for: +// * std::basic_string with all supported char types. + +// Counting template for managing template recursion. +template <std::size_t N> +struct Index {}; + +// Forward declaration of traits type to access types with a SerializedMembers +// member type. +template <typename T> +class SerializableTraits; + +template <typename T, typename... MemberPointers> +struct SerializableMembersType; + +// Utility to deduce the template type from a derived type. +template <template <typename...> class TT, typename... Ts> +std::true_type DeduceTemplateType(const TT<Ts...>*); +template <template <typename...> class TT> +std::false_type DeduceTemplateType(...); + +// Utility determining whether template type TT<...> is a base of type T. +template <template <typename...> class TT, typename T> +using IsTemplateBaseOf = decltype(DeduceTemplateType<TT>(std::declval<T*>())); + +// Utility type for SFINAE in HasHasSerializableMembers. +template <typename... Ts> +using TrySerializableMembersType = void; + +// Determines whether type T has a member type named SerializableMembers of +// template type SerializableMembersType. +template <typename, typename = void> +struct HasSerializableMembers : std::false_type {}; +template <typename T> +struct HasSerializableMembers< + T, TrySerializableMembersType<typename T::SerializableMembers>> + : std::integral_constant< + bool, IsTemplateBaseOf<SerializableMembersType, + typename T::SerializableMembers>::value> {}; + +// Utility to simplify overload enable expressions for types with correctly +// defined SerializableMembers. +template <typename T> +using EnableIfHasSerializableMembers = + typename std::enable_if<HasSerializableMembers<T>::value>::type; + +// Utility to simplify overload enable expressions for enum types. +template <typename T, typename ReturnType = void> +using EnableIfEnum = + typename std::enable_if<std::is_enum<T>::value, ReturnType>::type; + +/////////////////////////////////////////////////////////////////////////////// +// Error Reporting // +/////////////////////////////////////////////////////////////////////////////// + +// Error codes returned by the deserialization code. +enum class ErrorCode { + NO_ERROR = 0, + UNEXPECTED_ENCODING, + UNEXPECTED_TYPE_SIZE, + INSUFFICIENT_BUFFER, + INSUFFICIENT_DESTINATION_SIZE, + GET_FILE_DESCRIPTOR_FAILED, + GET_CHANNEL_HANDLE_FAILED, + INVALID_VARIANT_ELEMENT, +}; + +// Type for errors returned by the deserialization code. +class ErrorType { + public: + ErrorType() : error_code_(ErrorCode::NO_ERROR) {} + + // ErrorType constructor for generic error codes. Explicitly not explicit, + // implicit conversion from ErrorCode to ErrorType is desirable behavior. + // NOLINTNEXTLINE(runtime/explicit) + ErrorType(ErrorCode error_code) : error_code_(error_code) {} + + // ErrorType constructor for encoding type errors. + ErrorType(ErrorCode error_code, EncodingClass encoding_class, + EncodingType encoding_type) + : error_code_(error_code) { + unexpected_encoding_.encoding_class = encoding_class; + unexpected_encoding_.encoding_type = encoding_type; + } + + // Evaluates to true if the ErrorType represents an error. + explicit operator bool() const { return error_code_ != ErrorCode::NO_ERROR; } + + operator ErrorCode() const { return error_code_; } + ErrorCode error_code() const { return error_code_; } + + // Accessors for extra info about unexpected encoding errors. + EncodingClass encoding_class() const { + return unexpected_encoding_.encoding_class; + } + EncodingType encoding_type() const { + return unexpected_encoding_.encoding_type; + } + + operator std::string() const { + std::ostringstream stream; + + switch (error_code_) { + case ErrorCode::NO_ERROR: + return "NO_ERROR"; + case ErrorCode::UNEXPECTED_ENCODING: + stream << "UNEXPECTED_ENCODING: " << static_cast<int>(encoding_class()) + << ", " << static_cast<int>(encoding_type()); + return stream.str(); + case ErrorCode::UNEXPECTED_TYPE_SIZE: + return "UNEXPECTED_TYPE_SIZE"; + case ErrorCode::INSUFFICIENT_BUFFER: + return "INSUFFICIENT_BUFFER"; + case ErrorCode::INSUFFICIENT_DESTINATION_SIZE: + return "INSUFFICIENT_DESTINATION_SIZE"; + default: + return "[Unknown Error]"; + } + } + + private: + ErrorCode error_code_; + + // Union of extra information for different error code types. + union { + // UNEXPECTED_ENCODING. + struct { + EncodingClass encoding_class; + EncodingType encoding_type; + } unexpected_encoding_; + }; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Object Size // +/////////////////////////////////////////////////////////////////////////////// + +inline constexpr std::size_t GetSerializedSize(const bool& b) { + return GetEncodingSize(EncodeType(b)); +} + +// Overloads of GetSerializedSize() for standard integer types. +inline constexpr std::size_t GetSerializedSize(const char& c) { + return GetEncodingSize(EncodeType(c)); +} +inline constexpr std::size_t GetSerializedSize(const std::uint8_t& i) { + return GetEncodingSize(EncodeType(i)); +} +inline constexpr std::size_t GetSerializedSize(const std::int8_t& i) { + return GetEncodingSize(EncodeType(i)); +} +inline constexpr std::size_t GetSerializedSize(const std::uint16_t& i) { + return GetEncodingSize(EncodeType(i)); +} +inline constexpr std::size_t GetSerializedSize(const std::int16_t& i) { + return GetEncodingSize(EncodeType(i)); +} +inline constexpr std::size_t GetSerializedSize(const std::uint32_t& i) { + return GetEncodingSize(EncodeType(i)); +} +inline constexpr std::size_t GetSerializedSize(const std::int32_t& i) { + return GetEncodingSize(EncodeType(i)); +} +inline constexpr std::size_t GetSerializedSize(const std::uint64_t& i) { + return GetEncodingSize(EncodeType(i)); +} +inline constexpr std::size_t GetSerializedSize(const std::int64_t& i) { + return GetEncodingSize(EncodeType(i)); +} + +inline constexpr std::size_t GetSerializedSize(const float& f) { + return GetEncodingSize(EncodeType(f)); +} +inline constexpr std::size_t GetSerializedSize(const double& d) { + return GetEncodingSize(EncodeType(d)); +} + +// Overload for enum types. +template <typename T> +inline EnableIfEnum<T, std::size_t> GetSerializedSize(T v) { + return GetSerializedSize(static_cast<std::underlying_type_t<T>>(v)); +} + +// Forward declaration for nested definitions. +inline std::size_t GetSerializedSize(const EmptyVariant&); +template <typename... Types> +inline std::size_t GetSerializedSize(const Variant<Types...>&); +template <typename T, typename Enabled = EnableIfHasSerializableMembers<T>> +inline constexpr std::size_t GetSerializedSize(const T&); +template <typename T> +inline constexpr std::size_t GetSerializedSize(const PointerWrapper<T>&); +inline constexpr std::size_t GetSerializedSize(const std::string&); +template <typename T> +inline constexpr std::size_t GetSerializedSize(const StringWrapper<T>&); +template <typename T> +inline constexpr std::size_t GetSerializedSize(const BufferWrapper<T>&); +template <FileHandleMode Mode> +inline constexpr std::size_t GetSerializedSize(const FileHandle<Mode>&); +template <ChannelHandleMode Mode> +inline constexpr std::size_t GetSerializedSize(const ChannelHandle<Mode>&); +template <typename T, typename Allocator> +inline std::size_t GetSerializedSize(const std::vector<T, Allocator>& v); +template <typename Key, typename T, typename Compare, typename Allocator> +inline std::size_t GetSerializedSize( + const std::map<Key, T, Compare, Allocator>& m); +template <typename Key, typename T, typename Hash, typename KeyEqual, + typename Allocator> +inline std::size_t GetSerializedSize( + const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>&); +template <typename T> +inline std::size_t GetSerializedSize(const ArrayWrapper<T>&); +template <typename T, std::size_t Size> +inline std::size_t GetSerializedSize(const std::array<T, Size>& v); +template <typename T, typename U> +inline std::size_t GetSerializedSize(const std::pair<T, U>& p); +template <typename... T> +inline std::size_t GetSerializedSize(const std::tuple<T...>& tuple); + +// Overload for empty variant type. +inline std::size_t GetSerializedSize(const EmptyVariant& empty) { + return GetEncodingSize(EncodeType(empty)); +} + +// Overload for Variant types. +template <typename... Types> +inline std::size_t GetSerializedSize(const Variant<Types...>& variant) { + return GetEncodingSize(EncodeType(variant)) + + GetSerializedSize(variant.index()) + + variant.Visit( + [](const auto& value) { return GetSerializedSize(value); }); +} + +// Overload for structs/classes with SerializableMembers defined. +template <typename T, typename Enabled> +inline constexpr std::size_t GetSerializedSize(const T& value) { + return SerializableTraits<T>::GetSerializedSize(value); +} + +// Overload for PointerWrapper. +template <typename T> +inline constexpr std::size_t GetSerializedSize(const PointerWrapper<T>& p) { + return GetSerializedSize(p.Dereference()); +} + +// Overload for std::string. +inline constexpr std::size_t GetSerializedSize(const std::string& s) { + return GetEncodingSize(EncodeType(s)) + + s.length() * sizeof(std::string::value_type); +} + +// Overload for StringWrapper. +template <typename T> +inline constexpr std::size_t GetSerializedSize(const StringWrapper<T>& s) { + return GetEncodingSize(EncodeType(s)) + + s.length() * sizeof(typename StringWrapper<T>::value_type); +} + +// Overload for BufferWrapper types. +template <typename T> +inline constexpr std::size_t GetSerializedSize(const BufferWrapper<T>& b) { + return GetEncodingSize(EncodeType(b)) + + b.size() * sizeof(typename BufferWrapper<T>::value_type); +} + +// Overload for FileHandle. FileHandle is encoded as a FIXEXT2, with a type code +// of "FileHandle" and a signed 16-bit offset into the pushed fd array. Empty +// FileHandles are encoded with an array index of -1. +template <FileHandleMode Mode> +inline constexpr std::size_t GetSerializedSize(const FileHandle<Mode>& fd) { + return GetEncodingSize(EncodeType(fd)) + sizeof(std::int16_t); +} + +// Overload for ChannelHandle. ChannelHandle is encoded as a FIXEXT4, with a +// type code of "ChannelHandle" and a signed 32-bit offset into the pushed +// channel array. Empty ChannelHandles are encoded with an array index of -1. +template <ChannelHandleMode Mode> +inline constexpr std::size_t GetSerializedSize( + const ChannelHandle<Mode>& channel_handle) { + return GetEncodingSize(EncodeType(channel_handle)) + sizeof(std::int32_t); +} + +// Overload for standard vector types. +template <typename T, typename Allocator> +inline std::size_t GetSerializedSize(const std::vector<T, Allocator>& v) { + return std::accumulate(v.begin(), v.end(), GetEncodingSize(EncodeType(v)), + [](const std::size_t& sum, const T& object) { + return sum + GetSerializedSize(object); + }); +} + +// Overload for standard map types. +template <typename Key, typename T, typename Compare, typename Allocator> +inline std::size_t GetSerializedSize( + const std::map<Key, T, Compare, Allocator>& v) { + return std::accumulate( + v.begin(), v.end(), GetEncodingSize(EncodeType(v)), + [](const std::size_t& sum, const std::pair<Key, T>& object) { + return sum + GetSerializedSize(object.first) + + GetSerializedSize(object.second); + }); +} + +// Overload for standard unordered_map types. +template <typename Key, typename T, typename Hash, typename KeyEqual, + typename Allocator> +inline std::size_t GetSerializedSize( + const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& v) { + return std::accumulate( + v.begin(), v.end(), GetEncodingSize(EncodeType(v)), + [](const std::size_t& sum, const std::pair<Key, T>& object) { + return sum + GetSerializedSize(object.first) + + GetSerializedSize(object.second); + }); +} + +// Overload for ArrayWrapper types. +template <typename T> +inline std::size_t GetSerializedSize(const ArrayWrapper<T>& v) { + return std::accumulate(v.begin(), v.end(), GetEncodingSize(EncodeType(v)), + [](const std::size_t& sum, const T& object) { + return sum + GetSerializedSize(object); + }); +} + +// Overload for std::array types. +template <typename T, std::size_t Size> +inline std::size_t GetSerializedSize(const std::array<T, Size>& v) { + return std::accumulate(v.begin(), v.end(), GetEncodingSize(EncodeType(v)), + [](const std::size_t& sum, const T& object) { + return sum + GetSerializedSize(object); + }); +} + +// Overload for std::pair. +template <typename T, typename U> +inline std::size_t GetSerializedSize(const std::pair<T, U>& p) { + return GetEncodingSize(EncodeType(p)) + GetSerializedSize(p.first) + + GetSerializedSize(p.second); +} + +// Stops template recursion when the last tuple element is reached. +template <typename... T> +inline std::size_t GetTupleSize(const std::tuple<T...>&, Index<0>) { + return 0; +} + +// Gets the size of each element in a tuple recursively. +template <typename... T, std::size_t index> +inline std::size_t GetTupleSize(const std::tuple<T...>& tuple, Index<index>) { + return GetTupleSize(tuple, Index<index - 1>()) + + GetSerializedSize(std::get<index - 1>(tuple)); +} + +// Overload for tuple types. Gets the size of the tuple, recursing +// through the elements. +template <typename... T> +inline std::size_t GetSerializedSize(const std::tuple<T...>& tuple) { + return GetEncodingSize(EncodeType(tuple)) + + GetTupleSize(tuple, Index<sizeof...(T)>()); +} + +// Stops template recursion when the last member of a Serializable +// type is reached. +template <typename Members, typename T> +inline std::size_t GetMemberSize(const T&, Index<0>) { + return 0; +} + +// Gets the size of each member of a Serializable type recursively. +template <typename Members, typename T, std::size_t index> +inline std::size_t GetMemberSize(const T& object, Index<index>) { + return GetMemberSize<Members>(object, Index<index - 1>()) + + GetSerializedSize(Members::template At<index - 1>::Resolve(object)); +} + +// Gets the size of a type using the given SerializableMembersType +// type. +template <typename Members, typename T> +inline std::size_t GetMembersSize(const T& object) { + return GetMemberSize<Members>(object, Index<Members::MemberCount>()); +} + +/////////////////////////////////////////////////////////////////////////////// +// Object Serialization // +/////////////////////////////////////////////////////////////////////////////// + +// +// SerializeRaw() converts a primitive array or type into a raw byte string. +// These functions are named differently from SerializeObject() expressly to +// avoid catch-all specialization of that template, which can be difficult to +// detect otherwise. +// + +inline void WriteRawData(void*& dest, const void* src, size_t size) { + memcpy(dest, src, size); + dest = static_cast<uint8_t*>(dest) + size; +} + +// Serializes a primitive array into a raw byte string. +template <typename T, + typename = typename std::enable_if<std::is_pod<T>::value>::type> +inline void SerializeRaw(const T& value, void*& buffer) { + WriteRawData(buffer, &value, sizeof(value)); +} + +inline void SerializeEncoding(EncodingType encoding, void*& buffer) { + SerializeRaw(encoding, buffer); +} + +inline void SerializeType(const bool& value, void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeEncoding(encoding, buffer); +} + +// Serializes the type code, extended type code, and size for +// extension types. +inline void SerializeExtEncoding(EncodingType encoding, + EncodingExtType ext_type, std::size_t size, + void*& buffer) { + SerializeEncoding(encoding, buffer); + if (encoding == ENCODING_TYPE_EXT8) { + std::uint8_t length = size; + SerializeRaw(length, buffer); + } else if (encoding == ENCODING_TYPE_EXT16) { + std::uint16_t length = size; + SerializeRaw(length, buffer); + } else if (encoding == ENCODING_TYPE_EXT32) { + std::uint32_t length = size; + SerializeRaw(length, buffer); + } else /* if (IsFixextEncoding(encoding) */ { + // Encoding byte contains the fixext length, nothing else to do. + } + SerializeRaw(ext_type, buffer); +} + +// Serializes the type code for file descriptor types. +template <FileHandleMode Mode> +inline void SerializeType(const FileHandle<Mode>& value, void*& buffer) { + SerializeExtEncoding(EncodeType(value), ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 2, + buffer); +} + +// Serializes the type code for channel handle types. +template <ChannelHandleMode Mode> +inline void SerializeType(const ChannelHandle<Mode>& handle, void*& buffer) { + SerializeExtEncoding(EncodeType(handle), ENCODING_EXT_TYPE_CHANNEL_HANDLE, 4, + buffer); +} + +// Serializes type code for variant types. +template <typename... Types> +inline void SerializeType(const Variant<Types...>& value, void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeEncoding(encoding, buffer); +} + +// Serializes the type code for string types. +template <typename StringType> +inline void SerializeStringType(const StringType& value, void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeEncoding(encoding, buffer); + if (encoding == ENCODING_TYPE_STR8) { + std::uint8_t length = value.length(); + SerializeRaw(length, buffer); + } else if (encoding == ENCODING_TYPE_STR16) { + std::uint16_t length = value.length(); + SerializeRaw(length, buffer); + } else if (encoding == ENCODING_TYPE_STR32) { + std::uint32_t length = value.length(); + SerializeRaw(length, buffer); + } else /* if (IsFixstrEncoding(encoding) */ { + // Encoding byte contains the fixstr length, nothing else to do. + } +} + +// Serializes the type code for std::string and StringWrapper. These types are +// interchangeable and must serialize to the same format. +inline void SerializeType(const std::string& value, void*& buffer) { + SerializeStringType(value, buffer); +} +template <typename T> +inline void SerializeType(const StringWrapper<T>& value, void*& buffer) { + SerializeStringType(value, buffer); +} + +// Serializes the type code for bin types. +inline void SerializeBinEncoding(EncodingType encoding, std::size_t size, + void*& buffer) { + SerializeEncoding(encoding, buffer); + if (encoding == ENCODING_TYPE_BIN8) { + std::uint8_t length = size; + SerializeRaw(length, buffer); + } else if (encoding == ENCODING_TYPE_BIN16) { + std::uint16_t length = size; + SerializeRaw(length, buffer); + } else if (encoding == ENCODING_TYPE_BIN32) { + std::uint32_t length = size; + SerializeRaw(length, buffer); + } else { + // Invalid encoding for BIN type. + } +} + +// Serializes the type code for BufferWrapper types. +template <typename T> +inline void SerializeType(const BufferWrapper<T>& value, void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeBinEncoding( + encoding, value.size() * sizeof(typename BufferWrapper<T>::value_type), + buffer); +} + +// Serializes the array encoding type and length. +inline void SerializeArrayEncoding(EncodingType encoding, std::size_t size, + void*& buffer) { + SerializeEncoding(encoding, buffer); + if (encoding == ENCODING_TYPE_ARRAY16) { + std::uint16_t length = size; + SerializeRaw(length, buffer); + } else if (encoding == ENCODING_TYPE_ARRAY32) { + std::uint32_t length = size; + SerializeRaw(length, buffer); + } else /* if (IsFixarrayEncoding(encoding) */ { + // Encoding byte contains the fixarray length, nothing else to do. + } +} + +// Serializes the map encoding type and length. +inline void SerializeMapEncoding(EncodingType encoding, std::size_t size, + void*& buffer) { + SerializeEncoding(encoding, buffer); + if (encoding == ENCODING_TYPE_MAP16) { + std::uint16_t length = size; + SerializeRaw(length, buffer); + } else if (encoding == ENCODING_TYPE_MAP32) { + std::uint32_t length = size; + SerializeRaw(length, buffer); + } else /* if (IsFixmapEncoding(encoding) */ { + // Encoding byte contains the fixmap length, nothing else to do. + } +} + +// Serializes the type code for array types. +template <typename ArrayType> +inline void SerializeArrayType(const ArrayType& value, std::size_t size, + void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeArrayEncoding(encoding, size, buffer); +} + +// Serializes the type code for map types. +template <typename MapType> +inline void SerializeMapType(const MapType& value, std::size_t size, + void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeMapEncoding(encoding, size, buffer); +} + +// Serializes the type code for std::vector and ArrayWrapper. These types are +// interchangeable and must serialize to the same format. +template <typename T, typename Allocator> +inline void SerializeType(const std::vector<T, Allocator>& value, + void*& buffer) { + SerializeArrayType(value, value.size(), buffer); +} +template <typename T> +inline void SerializeType(const ArrayWrapper<T>& value, void*& buffer) { + SerializeArrayType(value, value.size(), buffer); +} + +// Serializes the type code for std::array. This type serializes to the same +// format as std::vector and ArrayWrapper and is interchangeable in certain +// situations. +template <typename T, std::size_t Size> +inline void SerializeType(const std::array<T, Size>& value, void*& buffer) { + SerializeArrayType(value, Size, buffer); +} + +// Serializes the type code for std::map types. +template <typename Key, typename T, typename Compare, typename Allocator> +inline void SerializeType(const std::map<Key, T, Compare, Allocator>& value, + void*& buffer) { + SerializeMapType(value, value.size(), buffer); +} + +// Serializes the type code for std::unordered_map types. +template <typename Key, typename T, typename Hash, typename KeyEqual, + typename Allocator> +inline void SerializeType( + const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& value, + void*& buffer) { + SerializeMapType(value, value.size(), buffer); +} + +// Serializes the type code for std::pair types. +template <typename T, typename U> +inline void SerializeType(const std::pair<T, U>& value, void*& buffer) { + SerializeArrayType(value, 2, buffer); +} + +// Serializes the type code for std::tuple types. +template <typename... T> +inline void SerializeType(const std::tuple<T...>& value, void*& buffer) { + SerializeArrayType(value, sizeof...(T), buffer); +} + +// Specialization of SerializeObject for boolean type. +inline void SerializeObject(const bool& value, MessageWriter* /*writer*/, + void*& buffer) { + SerializeType(value, buffer); + // Encoding contains the boolean value, nothing else to do. +} + +// Overloads of SerializeObject for float and double types. +inline void SerializeObject(const float& value, MessageWriter* /*writer*/, + void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeEncoding(encoding, buffer); + SerializeRaw(value, buffer); +} + +inline void SerializeObject(const double& value, MessageWriter* /*writer*/, + void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeEncoding(encoding, buffer); + SerializeRaw(value, buffer); +} + +// Overloads of SerializeObject() for standard integer types. +inline void SerializeObject(const char& value, MessageWriter* /*writer*/, + void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeEncoding(encoding, buffer); + if (encoding == ENCODING_TYPE_UINT8) { + SerializeRaw(value, buffer); + } else /* if (IsUnsignedFixintEncoding(encoding) */ { + // Encoding byte contains the value, nothing else to do. + } +} + +inline void SerializeObject(const int8_t& value, MessageWriter* /*writer*/, + void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeEncoding(encoding, buffer); + if (encoding == ENCODING_TYPE_INT8) { + SerializeRaw(value, buffer); + } else /* if (IsFixintEncoding(encoding) */ { + // Encoding byte contains the value, nothing else to do. + } +} + +inline void SerializeObject(const uint8_t& value, MessageWriter* /*writer*/, + void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeEncoding(encoding, buffer); + if (encoding == ENCODING_TYPE_UINT8) { + SerializeRaw(value, buffer); + } else /* if (IsUnsignedFixintEncoding(encoding) */ { + // Encoding byte contains the value, nothing else to do. + } +} + +inline void SerializeObject(const int16_t& value, MessageWriter* /*writer*/, + void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeEncoding(encoding, buffer); + if (encoding == ENCODING_TYPE_INT8) { + const int8_t byte = value; + SerializeRaw(byte, buffer); + } else if (encoding == ENCODING_TYPE_INT16) { + SerializeRaw(value, buffer); + } else /* if (IsFixintEncoding(encoding) */ { + // Encoding byte contains the value, nothing else to do. + } +} + +inline void SerializeObject(const uint16_t& value, MessageWriter* /*writer*/, + void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeEncoding(encoding, buffer); + if (encoding == ENCODING_TYPE_UINT8) { + const uint8_t byte = value; + SerializeRaw(byte, buffer); + } else if (encoding == ENCODING_TYPE_UINT16) { + SerializeRaw(value, buffer); + } else /* if (IsUnsignedFixintEncoding(encoding) */ { + // Encoding byte contains the value, nothing else to do. + } +} + +inline void SerializeObject(const int32_t& value, MessageWriter* /*writer*/, + void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeEncoding(encoding, buffer); + if (encoding == ENCODING_TYPE_INT8) { + const int8_t byte = value; + SerializeRaw(byte, buffer); + } else if (encoding == ENCODING_TYPE_INT16) { + const int16_t half = value; + SerializeRaw(half, buffer); + } else if (encoding == ENCODING_TYPE_INT32) { + SerializeRaw(value, buffer); + } else /* if (IsFixintEncoding(encoding) */ { + // Encoding byte contains the value, nothing else to do. + } +} + +inline void SerializeObject(const uint32_t& value, MessageWriter* /*writer*/, + void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeEncoding(encoding, buffer); + if (encoding == ENCODING_TYPE_UINT8) { + const uint8_t byte = value; + SerializeRaw(byte, buffer); + } else if (encoding == ENCODING_TYPE_UINT16) { + const uint16_t half = value; + SerializeRaw(half, buffer); + } else if (encoding == ENCODING_TYPE_UINT32) { + SerializeRaw(value, buffer); + } else /* if (IsUnsignedFixintEncoding(encoding) */ { + // Encoding byte contains the value, nothing else to do. + } +} + +inline void SerializeObject(const int64_t& value, MessageWriter* /*writer*/, + void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeEncoding(encoding, buffer); + if (encoding == ENCODING_TYPE_INT8) { + const int8_t byte = value; + SerializeRaw(byte, buffer); + } else if (encoding == ENCODING_TYPE_INT16) { + const int16_t half = value; + SerializeRaw(half, buffer); + } else if (encoding == ENCODING_TYPE_INT32) { + const int32_t word = value; + SerializeRaw(word, buffer); + } else if (encoding == ENCODING_TYPE_INT64) { + SerializeRaw(value, buffer); + } else /* if (IsFixintEncoding(encoding) */ { + // Encoding byte contains the value, nothing else to do. + } +} + +inline void SerializeObject(const uint64_t& value, MessageWriter* /*writer*/, + void*& buffer) { + const EncodingType encoding = EncodeType(value); + SerializeEncoding(encoding, buffer); + if (encoding == ENCODING_TYPE_UINT8) { + const uint8_t byte = value; + SerializeRaw(byte, buffer); + } else if (encoding == ENCODING_TYPE_UINT16) { + const uint16_t half = value; + SerializeRaw(half, buffer); + } else if (encoding == ENCODING_TYPE_UINT32) { + const uint32_t word = value; + SerializeRaw(word, buffer); + } else if (encoding == ENCODING_TYPE_UINT64) { + SerializeRaw(value, buffer); + } else /* if (IsUnsignedFixintEncoding(encoding) */ { + // Encoding byte contains the value, nothing else to do. + } +} + +// Serialize enum types. +template <typename T> +inline EnableIfEnum<T> SerializeObject(const T& value, MessageWriter* writer, + void*& buffer) { + SerializeObject(static_cast<std::underlying_type_t<T>>(value), writer, + buffer); +} + +// Forward declaration for nested definitions. +inline void SerializeObject(const EmptyVariant&, MessageWriter*, void*&); +template <typename... Types> +inline void SerializeObject(const Variant<Types...>&, MessageWriter*, void*&); +template <typename T, typename Enabled = EnableIfHasSerializableMembers<T>> +inline void SerializeObject(const T&, MessageWriter*, void*&); +template <typename T> +inline void SerializeObject(const PointerWrapper<T>&, MessageWriter*, void*&); +template <FileHandleMode Mode> +inline void SerializeObject(const FileHandle<Mode>&, MessageWriter*, void*&); +template <ChannelHandleMode Mode> +inline void SerializeObject(const ChannelHandle<Mode>&, MessageWriter*, void*&); +template <typename T, typename Allocator> +inline void SerializeObject(const BufferWrapper<std::vector<T, Allocator>>&, MessageWriter*, void*&); +template <typename T> +inline void SerializeObject(const BufferWrapper<T*>&, MessageWriter*, void*&); +inline void SerializeObject(const std::string&, MessageWriter*, void*&); +template <typename T> +inline void SerializeObject(const StringWrapper<T>&, MessageWriter*, void*&); +template <typename T, typename Allocator> +inline void SerializeObject(const std::vector<T, Allocator>&, MessageWriter*, void*&); +template <typename T> +inline void SerializeObject(const ArrayWrapper<T>&, MessageWriter*, void*&); +template <typename T, std::size_t Size> +inline void SerializeObject(const std::array<T, Size>&, MessageWriter*, void*&); +template <typename Key, typename T, typename Compare, typename Allocator> +inline void SerializeObject(const std::map<Key, T, Compare, Allocator>&, MessageWriter*, void*&); +template <typename Key, typename T, typename Hash, typename KeyEqual, + typename Allocator> +inline void SerializeObject( + const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>&, MessageWriter*, void*&); +template <typename T, typename U> +inline void SerializeObject(const std::pair<T, U>&, MessageWriter*, void*&); +template <typename... T> +inline void SerializeObject(const std::tuple<T...>&, MessageWriter*, void*&); + +// Overload for empty variant type. +inline void SerializeObject(const EmptyVariant& empty, + MessageWriter* /*writer*/, void*& buffer) { + const EncodingType encoding = EncodeType(empty); + SerializeEncoding(encoding, buffer); +} + +// Overload for Variant types. +template <typename... Types> +inline void SerializeObject(const Variant<Types...>& variant, + MessageWriter* writer, void*& buffer) { + SerializeType(variant, buffer); + SerializeObject(variant.index(), writer, buffer); + return variant.Visit([writer, &buffer](const auto& value) { + return SerializeObject(value, writer, buffer); + }); +} + +// Overload for serializable structure/class types. +template <typename T, typename Enabled> +inline void SerializeObject(const T& value, MessageWriter* writer, + void*& buffer) { + SerializableTraits<T>::SerializeObject(value, writer, buffer); +} + +// Serializes the payload of a PointerWrapper. +template <typename T> +inline void SerializeObject(const PointerWrapper<T>& pointer, + MessageWriter* writer, void*& buffer) { + SerializeObject(pointer.Dereference(), writer, buffer); +} + +// Serializes the payload of file descriptor types. +template <FileHandleMode Mode> +inline void SerializeObject(const FileHandle<Mode>& fd, MessageWriter* writer, + void*& buffer) { + SerializeType(fd, buffer); + const Status<FileReference> status = + writer->GetOutputResourceMapper()->PushFileHandle(fd); + FileReference value = status ? status.get() : -status.error(); + SerializeRaw(value, buffer); +} + +// Serializes the payload of channel handle types. +template <ChannelHandleMode Mode> +inline void SerializeObject(const ChannelHandle<Mode>& handle, + MessageWriter* writer, void*& buffer) { + SerializeType(handle, buffer); + const Status<ChannelReference> status = + writer->GetOutputResourceMapper()->PushChannelHandle(handle); + ChannelReference value = status ? status.get() : -status.error(); + SerializeRaw(value, buffer); +} + +// Serializes the payload of BufferWrapper types. +template <typename T, typename Allocator> +inline void SerializeObject(const BufferWrapper<std::vector<T, Allocator>>& b, + MessageWriter* /*writer*/, void*& buffer) { + const auto value_type_size = + sizeof(typename BufferWrapper<std::vector<T, Allocator>>::value_type); + SerializeType(b, buffer); + WriteRawData(buffer, b.data(), b.size() * value_type_size); +} +template <typename T> +inline void SerializeObject(const BufferWrapper<T*>& b, + MessageWriter* /*writer*/, void*& buffer) { + const auto value_type_size = sizeof(typename BufferWrapper<T*>::value_type); + SerializeType(b, buffer); + WriteRawData(buffer, b.data(), b.size() * value_type_size); +} + +// Serializes the payload of string types. +template <typename StringType> +inline void SerializeString(const StringType& s, void*& buffer) { + const auto value_type_size = sizeof(typename StringType::value_type); + SerializeType(s, buffer); + WriteRawData(buffer, s.data(), s.length() * value_type_size); +} + +// Overload of SerializeObject() for std::string and StringWrapper. These types +// are interchangeable and must serialize to the same format. +inline void SerializeObject(const std::string& s, MessageWriter* /*writer*/, + void*& buffer) { + SerializeString(s, buffer); +} +template <typename T> +inline void SerializeObject(const StringWrapper<T>& s, + MessageWriter* /*writer*/, void*& buffer) { + SerializeString(s, buffer); +} + +// Serializes the payload of array types. +template <typename ArrayType> +inline void SerializeArray(const ArrayType& v, MessageWriter* writer, + void*& buffer) { + SerializeType(v, buffer); + for (const auto& element : v) + SerializeObject(element, writer, buffer); +} + +// Serializes the payload for map types. +template <typename MapType> +inline void SerializeMap(const MapType& v, MessageWriter* writer, + void*& buffer) { + SerializeType(v, buffer); + for (const auto& element : v) { + SerializeObject(element.first, writer, buffer); + SerializeObject(element.second, writer, buffer); + } +} + +// Overload of SerializeObject() for std::vector and ArrayWrapper types. These +// types are interchangeable and must serialize to the same format. +template <typename T, typename Allocator> +inline void SerializeObject(const std::vector<T, Allocator>& v, + MessageWriter* writer, void*& buffer) { + SerializeArray(v, writer, buffer); +} +template <typename T> +inline void SerializeObject(const ArrayWrapper<T>& v, MessageWriter* writer, + void*& buffer) { + SerializeArray(v, writer, buffer); +} + +// Overload of SerializeObject() for std::array types. These types serialize to +// the same format at std::vector and ArrayWrapper and are interchangeable in +// certain situations. +template <typename T, std::size_t Size> +inline void SerializeObject(const std::array<T, Size>& v, MessageWriter* writer, + void*& buffer) { + SerializeArray(v, writer, buffer); +} + +// Overload of SerializeObject() for std::map types. +template <typename Key, typename T, typename Compare, typename Allocator> +inline void SerializeObject(const std::map<Key, T, Compare, Allocator>& v, + MessageWriter* writer, void*& buffer) { + SerializeMap(v, writer, buffer); +} + +// Overload of SerializeObject() for std::unordered_map types. +template <typename Key, typename T, typename Hash, typename KeyEqual, + typename Allocator> +inline void SerializeObject( + const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& v, + MessageWriter* writer, void*& buffer) { + SerializeMap(v, writer, buffer); +} + +// Overload of SerializeObject() for std:pair types. +template <typename T, typename U> +inline void SerializeObject(const std::pair<T, U>& pair, MessageWriter* writer, + void*& buffer) { + SerializeType(pair, buffer); + SerializeObject(pair.first, writer, buffer); + SerializeObject(pair.second, writer, buffer); +} + +// Stops template recursion when the last tuple element is reached. +template <typename... T> +inline void SerializeTuple(const std::tuple<T...>&, MessageWriter*, void*&, + Index<0>) {} + +// Serializes each element of a tuple recursively. +template <typename... T, std::size_t index> +inline void SerializeTuple(const std::tuple<T...>& tuple, MessageWriter* writer, + void*& buffer, Index<index>) { + SerializeTuple(tuple, writer, buffer, Index<index - 1>()); + SerializeObject(std::get<index - 1>(tuple), writer, buffer); +} + +// Overload of SerializeObject() for tuple types. +template <typename... T> +inline void SerializeObject(const std::tuple<T...>& tuple, + MessageWriter* writer, void*& buffer) { + SerializeType(tuple, buffer); + SerializeTuple(tuple, writer, buffer, Index<sizeof...(T)>()); +} + +// Stops template recursion when the last member pointer is reached. +template <typename Members, typename T> +inline void SerializeMember(const T&, MessageWriter*, void*&, Index<0>) {} + +// Serializes each member pointer recursively. +template <typename Members, typename T, std::size_t index> +inline void SerializeMember(const T& object, MessageWriter* writer, + void*& buffer, Index<index>) { + SerializeMember<Members>(object, writer, buffer, Index<index - 1>()); + SerializeObject(Members::template At<index - 1>::Resolve(object), writer, + buffer); +} + +// Serializes the members of a type using the given SerializableMembersType +// type. +template <typename Members, typename T> +inline void SerializeMembers(const T& object, MessageWriter* writer, + void*& buffer) { + SerializeMember<Members>(object, writer, buffer, + Index<Members::MemberCount>()); +} + +// Top level serialization function that replaces the buffer's contents. +template <typename T> +inline void Serialize(const T& object, MessageWriter* writer) { + PDX_TRACE_NAME("Serialize"); + const std::size_t size = GetSerializedSize(object); + + // Reserve the space needed for the object(s). + void* buffer = writer->GetNextWriteBufferSection(size); + SerializeObject(object, writer, buffer); +} + +/////////////////////////////////////////////////////////////////////////////// +// Object Deserialization // +/////////////////////////////////////////////////////////////////////////////// + +inline ErrorType ReadRawDataFromNextSection(void* dest, MessageReader* reader, + const void*& start, + const void*& end, size_t size) { + while (AdvancePointer(start, size) > end) { + auto remaining_size = PointerDistance(end, start); + if (remaining_size > 0) { + memcpy(dest, start, remaining_size); + dest = AdvancePointer(dest, remaining_size); + size -= remaining_size; + } + reader->ConsumeReadBufferSectionData(AdvancePointer(start, remaining_size)); + std::tie(start, end) = reader->GetNextReadBufferSection(); + if (start == end) + return ErrorCode::INSUFFICIENT_BUFFER; + } + memcpy(dest, start, size); + start = AdvancePointer(start, size); + return ErrorCode::NO_ERROR; +} + +inline ErrorType ReadRawData(void* dest, MessageReader* /*reader*/, + const void*& start, const void*& end, + size_t size) { + if (PDX_UNLIKELY(AdvancePointer(start, size) > end)) { + // TODO(avakulenko): Enabling reading from next sections of input buffer + // (using ReadRawDataFromNextSection) screws up clang compiler optimizations + // (probably inefficient inlining) making the whole deserialization + // code path about twice as slow. Investigate and enable more generic + // deserialization code, but right now we don't really need/support this + // scenario, so I keep this commented out for the time being... + + // return ReadRawDataFromNextSection(dest, reader, start, end, size); + return ErrorCode::INSUFFICIENT_BUFFER; + } + memcpy(dest, start, size); + start = AdvancePointer(start, size); + return ErrorCode::NO_ERROR; +} + +// Deserializes a primitive object from raw bytes. +template <typename T, + typename = typename std::enable_if<std::is_pod<T>::value>::type> +inline ErrorType DeserializeRaw(T* value, MessageReader* reader, + const void*& start, const void*& end) { + return ReadRawData(value, reader, start, end, sizeof(T)); +} + +// Utility to deserialize POD types when the serialized type is different +// (smaller) than the target real type. This happens when values are serialized +// into more compact encodings. +template <typename SerializedType, typename RealType> +ErrorType DeserializeValue(RealType* real_value, MessageReader* reader, + const void*& start, const void*& end) { + SerializedType serialized_value; + if (const auto error = + DeserializeRaw(&serialized_value, reader, start, end)) { + return error; + } else { + *real_value = serialized_value; + return ErrorCode::NO_ERROR; + } +} + +inline ErrorType DeserializeEncoding(EncodingType* encoding, + MessageReader* reader, const void*& start, + const void*& end) { + return DeserializeRaw(encoding, reader, start, end); +} + +// Overload to deserialize bool type. +inline ErrorType DeserializeObject(bool* value, MessageReader* reader, + const void*& start, const void*& end) { + EncodingType encoding; + if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) { + return error; + } else if (IsBoolEncoding(encoding)) { + *value = (encoding == ENCODING_TYPE_TRUE); + return ErrorCode::NO_ERROR; + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_BOOL, + encoding); + } +} + +// Specializations to deserialize float and double types. +inline ErrorType DeserializeObject(float* value, MessageReader* reader, + const void*& start, const void*& end) { + EncodingType encoding; + if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) { + return error; + } else if (IsFloat32Encoding(encoding)) { + return DeserializeValue<float>(value, reader, start, end); + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_FLOAT, + encoding); + } +} + +inline ErrorType DeserializeObject(double* value, MessageReader* reader, + const void*& start, const void*& end) { + EncodingType encoding; + if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) { + return error; + } else if (IsFloat32Encoding(encoding)) { + return DeserializeValue<float>(value, reader, start, end); + } else if (IsFloat64Encoding(encoding)) { + return DeserializeValue<double>(value, reader, start, end); + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_FLOAT, + encoding); + } +} + +// Specializations to deserialize standard integer types. +inline ErrorType DeserializeObject(char* value, MessageReader* reader, + const void*& start, const void*& end) { + EncodingType encoding; + if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) { + return error; + } else if (IsUnsignedFixintEncoding(encoding)) { + *value = static_cast<char>(encoding); + return ErrorCode::NO_ERROR; + } else if (IsUInt8Encoding(encoding)) { + return DeserializeValue<char>(value, reader, start, end); + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT, + encoding); + } +} + +inline ErrorType DeserializeObject(std::int8_t* value, MessageReader* reader, + const void*& start, const void*& end) { + EncodingType encoding; + if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) { + return error; + } else if (IsFixintEncoding(encoding)) { + *value = static_cast<std::int8_t>(encoding); + return ErrorCode::NO_ERROR; + } else if (IsInt8Encoding(encoding)) { + return DeserializeValue<std::int8_t>(value, reader, start, end); + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT, + encoding); + } +} + +inline ErrorType DeserializeObject(std::uint8_t* value, MessageReader* reader, + const void*& start, const void*& end) { + EncodingType encoding; + if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) { + return error; + } else if (IsUnsignedFixintEncoding(encoding)) { + *value = encoding; + return ErrorCode::NO_ERROR; + } else if (IsUInt8Encoding(encoding)) { + return DeserializeValue<std::uint8_t>(value, reader, start, end); + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT, + encoding); + } +} + +inline ErrorType DeserializeObject(std::int16_t* value, MessageReader* reader, + const void*& start, const void*& end) { + EncodingType encoding; + if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) { + return error; + } else if (IsFixintEncoding(encoding)) { + *value = static_cast<std::int8_t>(encoding); + return ErrorCode::NO_ERROR; + } else if (IsInt8Encoding(encoding)) { + return DeserializeValue<std::int8_t>(value, reader, start, end); + } else if (IsInt16Encoding(encoding)) { + return DeserializeValue<std::int16_t>(value, reader, start, end); + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT, + encoding); + } +} + +inline ErrorType DeserializeObject(std::uint16_t* value, MessageReader* reader, + const void*& start, const void*& end) { + EncodingType encoding; + if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) { + return error; + } else if (IsUnsignedFixintEncoding(encoding)) { + *value = encoding; + return ErrorCode::NO_ERROR; + } else if (IsUInt8Encoding(encoding)) { + return DeserializeValue<std::uint8_t>(value, reader, start, end); + } else if (IsUInt16Encoding(encoding)) { + return DeserializeValue<std::uint16_t>(value, reader, start, end); + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT, + encoding); + } +} + +inline ErrorType DeserializeObject(std::int32_t* value, MessageReader* reader, + const void*& start, const void*& end) { + EncodingType encoding; + if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) { + return error; + } else if (IsFixintEncoding(encoding)) { + *value = static_cast<std::int8_t>(encoding); + return ErrorCode::NO_ERROR; + } else if (IsInt8Encoding(encoding)) { + return DeserializeValue<std::int8_t>(value, reader, start, end); + } else if (IsInt16Encoding(encoding)) { + return DeserializeValue<std::int16_t>(value, reader, start, end); + } else if (IsInt32Encoding(encoding)) { + return DeserializeValue<std::int32_t>(value, reader, start, end); + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT, + encoding); + } +} + +inline ErrorType DeserializeObject(std::uint32_t* value, MessageReader* reader, + const void*& start, const void*& end) { + EncodingType encoding; + if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) { + return error; + } else if (IsUnsignedFixintEncoding(encoding)) { + *value = encoding; + return ErrorCode::NO_ERROR; + } else if (IsUInt8Encoding(encoding)) { + return DeserializeValue<std::uint8_t>(value, reader, start, end); + } else if (IsUInt16Encoding(encoding)) { + return DeserializeValue<std::uint16_t>(value, reader, start, end); + } else if (IsUInt32Encoding(encoding)) { + return DeserializeValue<std::uint32_t>(value, reader, start, end); + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT, + encoding); + } +} + +inline ErrorType DeserializeObject(std::int64_t* value, MessageReader* reader, + const void*& start, const void*& end) { + EncodingType encoding; + if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) { + return error; + } else if (IsFixintEncoding(encoding)) { + *value = static_cast<std::int8_t>(encoding); + return ErrorCode::NO_ERROR; + } else if (IsInt8Encoding(encoding)) { + return DeserializeValue<std::int8_t>(value, reader, start, end); + } else if (IsInt16Encoding(encoding)) { + return DeserializeValue<std::int16_t>(value, reader, start, end); + } else if (IsInt32Encoding(encoding)) { + return DeserializeValue<std::int32_t>(value, reader, start, end); + } else if (IsInt64Encoding(encoding)) { + return DeserializeValue<std::int64_t>(value, reader, start, end); + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT, + encoding); + } +} + +inline ErrorType DeserializeObject(std::uint64_t* value, MessageReader* reader, + const void*& start, const void*& end) { + EncodingType encoding; + if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) { + return error; + } else if (IsUnsignedFixintEncoding(encoding)) { + *value = encoding; + return ErrorCode::NO_ERROR; + } else if (IsUInt8Encoding(encoding)) { + return DeserializeValue<std::uint8_t>(value, reader, start, end); + } else if (IsUInt16Encoding(encoding)) { + return DeserializeValue<std::uint16_t>(value, reader, start, end); + } else if (IsUInt32Encoding(encoding)) { + return DeserializeValue<std::uint32_t>(value, reader, start, end); + } else if (IsUInt64Encoding(encoding)) { + return DeserializeValue<std::uint64_t>(value, reader, start, end); + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT, + encoding); + } +} + +template <typename T> +inline EnableIfEnum<T, ErrorType> DeserializeObject(T* value, + MessageReader* reader, + const void*& start, + const void*& end) { + std::underlying_type_t<T> enum_value; + ErrorType error = DeserializeObject(&enum_value, reader, start, end); + if (!error) + *value = static_cast<T>(enum_value); + return error; +} + +// Forward declarations for nested definitions. +template <typename T, typename Enabled = EnableIfHasSerializableMembers<T>> +inline ErrorType DeserializeObject(T*, MessageReader*, const void*&, + const void*&); +template <typename T> +inline ErrorType DeserializeObject(PointerWrapper<T>*, MessageReader*, + const void*&, const void*&); +inline ErrorType DeserializeObject(LocalHandle*, MessageReader*, const void*&, + const void*&); +inline ErrorType DeserializeObject(LocalChannelHandle*, MessageReader*, + const void*&, const void*&); +template <typename T, typename Allocator> +inline ErrorType DeserializeObject(BufferWrapper<std::vector<T, Allocator>>*, + MessageReader*, const void*&, const void*&); +template <typename T> +inline ErrorType DeserializeObject(BufferWrapper<T*>*, MessageReader*, + const void*&, const void*&); +inline ErrorType DeserializeObject(std::string*, MessageReader*, const void*&, + const void*&); +template <typename T> +inline ErrorType DeserializeObject(StringWrapper<T>*, MessageReader*, + const void*&, const void*&); +template <typename T, typename U> +inline ErrorType DeserializeObject(std::pair<T, U>*, MessageReader*, + const void*&, const void*&); +template <typename... T> +inline ErrorType DeserializeObject(std::tuple<T...>*, MessageReader*, + const void*&, const void*&); +template <typename T, typename Allocator> +inline ErrorType DeserializeObject(std::vector<T, Allocator>*, MessageReader*, + const void*&, const void*&); +template <typename Key, typename T, typename Compare, typename Allocator> +inline ErrorType DeserializeObject(std::map<Key, T, Compare, Allocator>*, + MessageReader*, const void*&, const void*&); +template <typename Key, typename T, typename Hash, typename KeyEqual, + typename Allocator> +inline ErrorType DeserializeObject( + std::unordered_map<Key, T, Hash, KeyEqual, Allocator>*, MessageReader*, + const void*&, const void*&); +template <typename T> +inline ErrorType DeserializeObject(ArrayWrapper<T>*, MessageReader*, + const void*&, const void*&); +template <typename T, std::size_t Size> +inline ErrorType DeserializeObject(std::array<T, Size>*, MessageReader*, + const void*&, const void*&); +template <typename T, typename U> +inline ErrorType DeserializeObject(std::pair<T, U>*, MessageReader*, + const void*&, const void*&); +template <typename... T> +inline ErrorType DeserializeObject(std::tuple<T...>*, MessageReader*, + const void*&, const void*&); +inline ErrorType DeserializeObject(EmptyVariant*, + MessageReader*, const void*&, + const void*&); +template <typename... Types> +inline ErrorType DeserializeObject(Variant<Types...>*, + MessageReader*, const void*&, + const void*&); + +// Deserializes a Serializable type. +template <typename T, typename Enable> +inline ErrorType DeserializeObject(T* value, MessageReader* reader, + const void*& start, const void*& end) { + return SerializableTraits<T>::DeserializeObject(value, reader, start, end); +} + +// Deserializes a PointerWrapper. +template <typename T> +inline ErrorType DeserializeObject(PointerWrapper<T>* pointer, + MessageReader* reader, const void*& start, + const void*& end) { + return DeserializeObject(&pointer->Dereference(), reader, start, end); +} + +// Deserializes the type code and size for extension types. +inline ErrorType DeserializeExtType(EncodingType* encoding, + EncodingExtType* type, std::size_t* size, + MessageReader* reader, const void*& start, + const void*& end) { + if (const auto error = DeserializeEncoding(encoding, reader, start, end)) { + return error; + } else if (IsFixextEncoding(*encoding)) { + *size = GetFixextSize(*encoding); + } else if (*encoding == ENCODING_TYPE_EXT8) { + if (const auto error = + DeserializeValue<std::uint8_t>(size, reader, start, end)) + return error; + } else if (*encoding == ENCODING_TYPE_EXT16) { + if (const auto error = + DeserializeValue<std::uint16_t>(size, reader, start, end)) + return error; + } else if (*encoding == ENCODING_TYPE_EXT32) { + if (const auto error = + DeserializeValue<std::uint32_t>(size, reader, start, end)) + return error; + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_EXTENSION, + *encoding); + } + + // The extension type code follows the encoding and size. + return DeserializeRaw(type, reader, start, end); +} + +// Deserializes a file handle and performs handle space translation, if +// required. +inline ErrorType DeserializeObject(LocalHandle* value, MessageReader* reader, + const void*& start, const void*& end) { + EncodingType encoding; + EncodingExtType type; + std::size_t size; + + if (const auto error = + DeserializeExtType(&encoding, &type, &size, reader, start, end)) { + return error; + } else if (size != 2) { + return ErrorType(ErrorCode::UNEXPECTED_TYPE_SIZE, ENCODING_CLASS_EXTENSION, + encoding); + } else if (type == ENCODING_EXT_TYPE_FILE_DESCRIPTOR) { + // Read the encoded file descriptor value. + FileReference ref; + if (const auto error = DeserializeRaw(&ref, reader, start, end)) { + return error; + } + + return reader->GetInputResourceMapper()->GetFileHandle(ref, value) + ? ErrorCode::NO_ERROR + : ErrorCode::GET_FILE_DESCRIPTOR_FAILED; + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_EXTENSION, + encoding); + } +} + +inline ErrorType DeserializeObject(LocalChannelHandle* value, + MessageReader* reader, const void*& start, + const void*& end) { + EncodingType encoding; + EncodingExtType type; + std::size_t size; + + if (const auto error = + DeserializeExtType(&encoding, &type, &size, reader, start, end)) { + return error; + } else if (size != 4) { + return ErrorType(ErrorCode::UNEXPECTED_TYPE_SIZE, ENCODING_CLASS_EXTENSION, + encoding); + } else if (type == ENCODING_EXT_TYPE_CHANNEL_HANDLE) { + // Read the encoded channel handle value. + ChannelReference ref; + if (const auto error = DeserializeRaw(&ref, reader, start, end)) { + return error; + } + return reader->GetInputResourceMapper()->GetChannelHandle(ref, value) + ? ErrorCode::NO_ERROR + : ErrorCode::GET_CHANNEL_HANDLE_FAILED; + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_EXTENSION, + encoding); + } +} + +// Deserializes the type code and size for bin types. +inline ErrorType DeserializeBinType(EncodingType* encoding, std::size_t* size, + MessageReader* reader, const void*& start, + const void*& end) { + if (const auto error = DeserializeEncoding(encoding, reader, start, end)) { + return error; + } else if (*encoding == ENCODING_TYPE_BIN8) { + return DeserializeValue<std::uint8_t>(size, reader, start, end); + } else if (*encoding == ENCODING_TYPE_BIN16) { + return DeserializeValue<std::uint16_t>(size, reader, start, end); + } else if (*encoding == ENCODING_TYPE_BIN32) { + return DeserializeValue<std::uint32_t>(size, reader, start, end); + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_BINARY, + *encoding); + } +} + +// Overload of DeserializeObject() for BufferWrapper types. +template <typename T, typename Allocator> +inline ErrorType DeserializeObject( + BufferWrapper<std::vector<T, Allocator>>* value, MessageReader* reader, + const void*& start, const void*& end) { + const auto value_type_size = + sizeof(typename BufferWrapper<std::vector<T, Allocator>>::value_type); + EncodingType encoding; + std::size_t size; + + if (const auto error = + DeserializeBinType(&encoding, &size, reader, start, end)) + return error; + + // Try to resize the BufferWrapper to the size of the payload. + value->resize(size / value_type_size); + + if (size > value->size() * value_type_size || size % value_type_size != 0) { + return ErrorCode::INSUFFICIENT_DESTINATION_SIZE; + } else if (size == 0U) { + return ErrorCode::NO_ERROR; + } else { + return ReadRawData(value->data(), reader, start, end, size); + } +} +template <typename T> +inline ErrorType DeserializeObject(BufferWrapper<T*>* value, + MessageReader* reader, const void*& start, + const void*& end) { + const auto value_type_size = sizeof(typename BufferWrapper<T*>::value_type); + EncodingType encoding; + std::size_t size; + + if (const auto error = + DeserializeBinType(&encoding, &size, reader, start, end)) + return error; + + // Try to resize the BufferWrapper to the size of the payload. + value->resize(size / value_type_size); + + if (size > value->size() * value_type_size || size % value_type_size != 0) { + return ErrorCode::INSUFFICIENT_DESTINATION_SIZE; + } else if (size == 0U) { + return ErrorCode::NO_ERROR; + } else { + return ReadRawData(value->data(), reader, start, end, size); + } +} + +// Deserializes the type code and size for string types. +inline ErrorType DeserializeStringType(EncodingType* encoding, + std::size_t* size, MessageReader* reader, + const void*& start, const void*& end) { + if (const auto error = DeserializeEncoding(encoding, reader, start, end)) { + return error; + } else if (IsFixstrEncoding(*encoding)) { + *size = GetFixstrSize(*encoding); + return ErrorCode::NO_ERROR; + } else if (*encoding == ENCODING_TYPE_STR8) { + return DeserializeValue<std::uint8_t>(size, reader, start, end); + } else if (*encoding == ENCODING_TYPE_STR16) { + return DeserializeValue<std::uint16_t>(size, reader, start, end); + } else if (*encoding == ENCODING_TYPE_STR32) { + return DeserializeValue<std::uint32_t>(size, reader, start, end); + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_STRING, + *encoding); + } +} + +// Overload of DeserializeObject() for std::string types. +inline ErrorType DeserializeObject(std::string* value, MessageReader* reader, + const void*& start, const void*& end) { + EncodingType encoding; + std::size_t size; + + if (const auto error = + DeserializeStringType(&encoding, &size, reader, start, end)) { + return error; + } else if (size == 0U) { + value->clear(); + return ErrorCode::NO_ERROR; + } else { + value->resize(size); + return ReadRawData(&(*value)[0], reader, start, end, size); + } +} + +// Overload of DeserializeObject() for StringWrapper types. +template <typename T> +inline ErrorType DeserializeObject(StringWrapper<T>* value, + MessageReader* reader, const void*& start, + const void*& end) { + const auto value_type_size = sizeof(typename StringWrapper<T>::value_type); + EncodingType encoding; + std::size_t size; + + if (const auto error = + DeserializeStringType(&encoding, &size, reader, start, end)) + return error; + + // Try to resize the StringWrapper to the size of the payload + // string. + value->resize(size / value_type_size); + + if (size > value->length() * value_type_size || size % value_type_size != 0) { + return ErrorCode::INSUFFICIENT_DESTINATION_SIZE; + } else if (size == 0U) { + return ErrorCode::NO_ERROR; + } else { + return ReadRawData(value->data(), reader, start, end, size); + } +} + +// Deserializes the type code and size of array types. +inline ErrorType DeserializeArrayType(EncodingType* encoding, std::size_t* size, + MessageReader* reader, const void*& start, + const void*& end) { + if (const auto error = DeserializeEncoding(encoding, reader, start, end)) { + return error; + } else if (IsFixarrayEncoding(*encoding)) { + *size = GetFixarraySize(*encoding); + return ErrorCode::NO_ERROR; + } else if (*encoding == ENCODING_TYPE_ARRAY16) { + return DeserializeValue<std::uint16_t>(size, reader, start, end); + } else if (*encoding == ENCODING_TYPE_ARRAY32) { + return DeserializeValue<std::uint32_t>(size, reader, start, end); + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_ARRAY, + *encoding); + } +} + +// Deserializes the type code and size of map types. +inline ErrorType DeserializeMapType(EncodingType* encoding, std::size_t* size, + MessageReader* reader, const void*& start, + const void*& end) { + if (const auto error = DeserializeEncoding(encoding, reader, start, end)) { + return error; + } else if (IsFixmapEncoding(*encoding)) { + *size = GetFixmapSize(*encoding); + return ErrorCode::NO_ERROR; + } else if (*encoding == ENCODING_TYPE_MAP16) { + return DeserializeValue<std::uint16_t>(size, reader, start, end); + } else if (*encoding == ENCODING_TYPE_MAP32) { + return DeserializeValue<std::uint32_t>(size, reader, start, end); + } else { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_MAP, + *encoding); + } +} + +// Overload for std::vector types. +template <typename T, typename Allocator> +inline ErrorType DeserializeObject(std::vector<T, Allocator>* value, + MessageReader* reader, const void*& start, + const void*& end) { + EncodingType encoding; + std::size_t size; + + if (const auto error = + DeserializeArrayType(&encoding, &size, reader, start, end)) + return error; + + std::vector<T, Allocator> result(size); + for (std::size_t i = 0; i < size; i++) { + if (const auto error = DeserializeObject(&result[i], reader, start, end)) + return error; + } + + *value = std::move(result); + return ErrorCode::NO_ERROR; + +// TODO(eieio): Consider the benefits and trade offs of this alternative. +#if 0 + value->resize(size); + for (std::size_t i = 0; i < size; i++) { + if (const auto error = DeserializeObject(&(*value)[i], reader, start, end)) + return error; + } + return ErrorCode::NO_ERROR; +#endif +} + +// Deserializes an EmptyVariant value. +inline ErrorType DeserializeObject(EmptyVariant* /*empty*/, + MessageReader* reader, const void*& start, + const void*& end) { + EncodingType encoding; + + if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) { + return error; + } else if (encoding != ENCODING_TYPE_NIL) { + return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_MAP, + encoding); + } else { + return ErrorCode::NO_ERROR; + } +} + +// Deserializes a Variant type. +template <typename... Types> +inline ErrorType DeserializeObject(Variant<Types...>* variant, + MessageReader* reader, const void*& start, + const void*& end) { + EncodingType encoding; + std::size_t size; + + if (const auto error = + DeserializeMapType(&encoding, &size, reader, start, end)) { + return error; + } + + if (size != 1) + return ErrorType(ErrorCode::UNEXPECTED_TYPE_SIZE, ENCODING_CLASS_MAP, + encoding); + + std::int32_t type; + if (const auto error = DeserializeObject(&type, reader, start, end)) { + return error; + } else if (type < Variant<Types...>::kEmptyIndex || + type >= static_cast<std::int32_t>(sizeof...(Types))) { + return ErrorCode::INVALID_VARIANT_ELEMENT; + } else { + variant->Become(type); + return variant->Visit([reader, &start, &end](auto&& value) { + return DeserializeObject(&value, reader, start, end); + }); + } +} + +// Deserializes map types. +template <typename MapType> +inline ErrorType DeserializeMap(MapType* value, MessageReader* reader, + const void*& start, const void*& end) { + EncodingType encoding; + std::size_t size; + + if (const auto error = + DeserializeMapType(&encoding, &size, reader, start, end)) + return error; + + MapType result; + for (std::size_t i = 0; i < size; i++) { + std::pair<typename MapType::key_type, typename MapType::mapped_type> + element; + if (const auto error = + DeserializeObject(&element.first, reader, start, end)) + return error; + if (const auto error = + DeserializeObject(&element.second, reader, start, end)) + return error; + result.emplace(std::move(element)); + } + + *value = std::move(result); + return ErrorCode::NO_ERROR; +} + +// Overload for std::map types. +template <typename Key, typename T, typename Compare, typename Allocator> +inline ErrorType DeserializeObject(std::map<Key, T, Compare, Allocator>* value, + MessageReader* reader, const void*& start, + const void*& end) { + return DeserializeMap(value, reader, start, end); +} + +// Overload for std::unordered_map types. +template <typename Key, typename T, typename Hash, typename KeyEqual, + typename Allocator> +inline ErrorType DeserializeObject( + std::unordered_map<Key, T, Hash, KeyEqual, Allocator>* value, + MessageReader* reader, const void*& start, const void*& end) { + return DeserializeMap(value, reader, start, end); +} + +// Overload for ArrayWrapper types. +template <typename T> +inline ErrorType DeserializeObject(ArrayWrapper<T>* value, + MessageReader* reader, const void*& start, + const void*& end) { + EncodingType encoding; + std::size_t size; + + if (const auto error = + DeserializeArrayType(&encoding, &size, reader, start, end)) { + return error; + } + + // Try to resize the wrapper. + value->resize(size); + + // Make sure there is enough space in the ArrayWrapper for the + // payload. + if (size > value->capacity()) + return ErrorCode::INSUFFICIENT_DESTINATION_SIZE; + + for (std::size_t i = 0; i < size; i++) { + if (const auto error = DeserializeObject(&(*value)[i], reader, start, end)) + return error; + } + + return ErrorCode::NO_ERROR; +} + +// Overload for std::array types. +template <typename T, std::size_t Size> +inline ErrorType DeserializeObject(std::array<T, Size>* value, + MessageReader* reader, const void*& start, + const void*& end) { + EncodingType encoding; + std::size_t size; + + if (const auto error = + DeserializeArrayType(&encoding, &size, reader, start, end)) { + return error; + } + + if (size != Size) + return ErrorCode::INSUFFICIENT_DESTINATION_SIZE; + + for (std::size_t i = 0; i < size; i++) { + if (const auto error = DeserializeObject(&(*value)[i], reader, start, end)) + return error; + } + + return ErrorCode::NO_ERROR; +} + +// Deserializes std::pair types. +template <typename T, typename U> +inline ErrorType DeserializeObject(std::pair<T, U>* value, + MessageReader* reader, const void*& start, + const void*& end) { + EncodingType encoding; + std::size_t size; + + if (const auto error = + DeserializeArrayType(&encoding, &size, reader, start, end)) { + return error; + } else if (size != 2) { + return ErrorCode::UNEXPECTED_TYPE_SIZE; + } else if (const auto error = + DeserializeObject(&value->first, reader, start, end)) { + return error; + } else if (const auto error = + DeserializeObject(&value->second, reader, start, end)) { + return error; + } else { + return ErrorCode::NO_ERROR; + } +} + +// Stops template recursion when the last tuple element is reached. +template <typename... T> +inline ErrorType DeserializeTuple(std::tuple<T...>*, MessageReader*, + const void*&, const void*, Index<0>) { + return ErrorCode::NO_ERROR; +} + +// Deserializes each element of a tuple recursively. +template <typename... T, std::size_t index> +inline ErrorType DeserializeTuple(std::tuple<T...>* tuple, + MessageReader* reader, const void*& start, + const void*& end, Index<index>) { + if (const auto error = + DeserializeTuple(tuple, reader, start, end, Index<index - 1>())) + return error; + else + return DeserializeObject(&std::get<index - 1>(*tuple), reader, start, end); +} + +// Overload for standard tuple types. +template <typename... T> +inline ErrorType DeserializeObject(std::tuple<T...>* value, + MessageReader* reader, const void*& start, + const void*& end) { + EncodingType encoding; + std::size_t size; + + if (const auto error = + DeserializeArrayType(&encoding, &size, reader, start, end)) { + return error; + } else if (size != sizeof...(T)) { + return ErrorCode::UNEXPECTED_TYPE_SIZE; + } else { + return DeserializeTuple(value, reader, start, end, Index<sizeof...(T)>()); + } +} + +// Stops template recursion when the last member of a Serializable type is +// reached. +template <typename Members, typename T> +inline ErrorType DeserializeMember(T*, MessageReader*, const void*&, + const void*, Index<0>) { + return ErrorCode::NO_ERROR; +} + +// Deserializes each member of a Serializable type recursively. +template <typename Members, typename T, std::size_t index> +inline ErrorType DeserializeMember(T* value, MessageReader* reader, + const void*& start, const void*& end, + Index<index>) { + if (const auto error = DeserializeMember<Members>(value, reader, start, end, + Index<index - 1>())) + return error; + else + return DeserializeObject(&Members::template At<index - 1>::Resolve(*value), + reader, start, end); +} + +// Deserializes the members of a Serializable type using the given +// SerializableMembersType type. +template <typename Members, typename T> +inline ErrorType DeserializeMembers(T* value, MessageReader* reader, + const void*& start, const void*& end) { + return DeserializeMember<Members>(value, reader, start, end, + Index<Members::MemberCount>()); +} + +// Top level deserialization function. +template <typename T> +inline ErrorType Deserialize(T* value, MessageReader* reader) { + PDX_TRACE_NAME("Deserialize"); + MessageReader::BufferSection section = reader->GetNextReadBufferSection(); + if (section.first == section.second) + return ErrorCode::INSUFFICIENT_BUFFER; + ErrorType error = + DeserializeObject(value, reader, section.first, section.second); + reader->ConsumeReadBufferSectionData(section.first); + return error; +} + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_SERIALIZATION_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h new file mode 100644 index 0000000000..19fc4c1323 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h @@ -0,0 +1,129 @@ +#ifndef ANDROID_PDX_RPC_STRING_WRAPPER_H_ +#define ANDROID_PDX_RPC_STRING_WRAPPER_H_ + +#include <cstddef> +#include <cstring> +#include <string> +#include <type_traits> + +namespace android { +namespace pdx { +namespace rpc { + +// Wrapper class for C string buffers, providing an interface suitable for +// SerializeObject and DeserializeObject. This class serializes to the same +// format as std::basic_string, and may be substituted for std::basic_string +// during serialization and deserialization. This substitution makes handling of +// C strings more efficient by avoiding unnecessary copies when remote method +// signatures specify std::basic_string arguments or return values. +template <typename CharT = std::string::value_type, + typename Traits = std::char_traits<CharT>> +class StringWrapper { + public: + // Define types in the style of STL strings to support STL operators. + typedef Traits traits_type; + typedef typename Traits::char_type value_type; + typedef std::size_t size_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef const value_type* const_pointer; + + StringWrapper() : buffer_(nullptr), capacity_(0), end_(0) {} + + StringWrapper(pointer buffer, size_type capacity, size_type size) + : buffer_(&buffer[0]), + capacity_(capacity), + end_(capacity < size ? capacity : size) {} + + StringWrapper(pointer buffer, size_type size) + : StringWrapper(buffer, size, size) {} + + explicit StringWrapper(pointer buffer) + : StringWrapper(buffer, std::strlen(buffer)) {} + + StringWrapper(const StringWrapper& other) { *this = other; } + + StringWrapper(StringWrapper&& other) { *this = std::move(other); } + + StringWrapper& operator=(const StringWrapper& other) { + if (&other == this) { + return *this; + } else { + buffer_ = other.buffer_; + capacity_ = other.capacity_; + end_ = other.end_; + } + + return *this; + } + + StringWrapper& operator=(StringWrapper&& other) { + if (&other == this) { + return *this; + } else { + buffer_ = other.buffer_; + capacity_ = other.capacity_; + end_ = other.end_; + other.buffer_ = nullptr; + other.capacity_ = 0; + other.end_ = 0; + } + + return *this; + } + + pointer data() { return buffer_; } + const_pointer data() const { return buffer_; } + + pointer begin() { return &buffer_[0]; } + pointer end() { return &buffer_[end_]; } + const_pointer begin() const { return &buffer_[0]; } + const_pointer end() const { return &buffer_[end_]; } + + size_type size() const { return end_; } + size_type length() const { return end_; } + size_type max_size() const { return capacity_; } + size_type capacity() const { return capacity_; } + + void resize(size_type size) { + if (size <= capacity_) + end_ = size; + else + end_ = capacity_; + } + + reference operator[](size_type pos) { return buffer_[pos]; } + const_reference operator[](size_type pos) const { return buffer_[pos]; } + + private: + pointer buffer_; + size_type capacity_; + size_type end_; +}; + +// Utility functions that infer the underlying type of the string, simplifying +// the wrapper interface. + +// TODO(eieio): Wrapping std::basic_string is here for completeness, but is it +// useful? +template <typename T, typename... Any> +StringWrapper<const T> WrapString(const std::basic_string<T, Any...>& s) { + return StringWrapper<const T>(s.c_str(), s.length()); +} + +template <typename T, typename SizeType = std::size_t> +StringWrapper<T> WrapString(T* s, SizeType size) { + return StringWrapper<T>(s, size); +} + +template <typename T> +StringWrapper<T> WrapString(T* s) { + return StringWrapper<T>(s); +} + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_STRING_WRAPPER_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h b/libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h new file mode 100644 index 0000000000..e5ef2aa787 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h @@ -0,0 +1,134 @@ +#ifndef ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_ +#define ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_ + +#include <cstdint> +#include <memory> +#include <vector> + +#include <pdx/rpc/default_initialization_allocator.h> +#include <pdx/trace.h> + +namespace android { +namespace pdx { +namespace rpc { + +// Utility class to distinguish between different thread local entries or +// "slots" in the thread local variable table. Each slot is uniquely identified +// by (T,Index) and is independent of any other slot. +template <typename T, std::size_t Index> +struct ThreadLocalSlot; + +// Utility class to specify thread local slots using only a type. +template <typename T> +struct ThreadLocalTypeSlot; + +// Utility class to specify thread local slots using only an index. +template <std::size_t Index> +struct ThreadLocalIndexSlot; + +// Initial capacity of thread local buffer, unless otherwise specified. +constexpr std::size_t InitialBufferCapacity = 4096; + +// Thread local slots for buffers used by this library to send, receive, and +// reply to messages. +using SendBuffer = ThreadLocalIndexSlot<0>; +using ReceiveBuffer = ThreadLocalIndexSlot<1>; +using ReplyBuffer = ThreadLocalIndexSlot<2>; + +// Provides a simple interface to thread local buffers for large IPC messages. +// Slot provides multiple thread local slots for a given T, Allocator, Capacity +// combination. +template <typename T, typename Allocator = DefaultInitializationAllocator<T>, + std::size_t Capacity = InitialBufferCapacity, + typename Slot = ThreadLocalSlot<void, 0>> +class ThreadLocalBuffer { + public: + using BufferType = std::vector<T, Allocator>; + using ValueType = T; + + // Reserves |capacity| number of elements of capacity in the underlying + // buffer. Call this during startup to avoid allocation during use. + static void Reserve(std::size_t capacity) { + PDX_TRACE_NAME("ThreadLocalBuffer::Reserve"); + InitializeBuffer(capacity); + buffer_->reserve(capacity); + } + + // Resizes the buffer to |size| elements. + static void Resize(std::size_t size) { + PDX_TRACE_NAME("ThreadLocalBuffer::Resize"); + InitializeBuffer(size); + buffer_->resize(size); + } + + // Gets a reference to the underlying buffer after reserving |capacity| + // elements. The current size of the buffer is left intact. The returned + // reference is valid until FreeBuffer() is called. + static BufferType& GetBuffer(std::size_t capacity = Capacity) { + PDX_TRACE_NAME("ThreadLocalBuffer::GetBuffer"); + Reserve(capacity); + return *buffer_; + } + + // Gets a reference to the underlying buffer after reserving |Capacity| + // elements. The current size of the buffer is set to zero. The returned + // reference is valid until FreeBuffer() is called. + static BufferType& GetEmptyBuffer() { + PDX_TRACE_NAME("ThreadLocalBuffer::GetEmptyBuffer"); + Reserve(Capacity); + buffer_->clear(); + return *buffer_; + } + + // Gets a reference to the underlying buffer after resizing it to |size| + // elements. The returned reference is valid until FreeBuffer() is called. + static BufferType& GetSizedBuffer(std::size_t size = Capacity) { + PDX_TRACE_NAME("ThreadLocalBuffer::GetSizedBuffer"); + Resize(size); + return *buffer_; + } + + // Frees the underlying buffer. The buffer will be reallocated if any of the + // methods above are called. + static void FreeBuffer() { + if (buffer_) { + GetBufferGuard().reset(buffer_ = nullptr); + } + } + + private: + friend class ThreadLocalBufferTest; + + static void InitializeBuffer(std::size_t capacity) { + if (!buffer_) { + GetBufferGuard().reset(buffer_ = new BufferType(capacity)); + } + } + + // Work around performance issues with thread-local dynamic initialization + // semantics by using a normal pointer in parallel with a std::unique_ptr. The + // std::unique_ptr is never dereferenced, only assigned, to avoid the high + // cost of dynamic initialization checks, while still providing automatic + // cleanup. The normal pointer provides fast access to the buffer object. + // Never dereference buffer_guard or performance could be severely impacted + // by slow implementations of TLS dynamic initialization. + static thread_local BufferType* buffer_; + + static std::unique_ptr<BufferType>& GetBufferGuard() { + PDX_TRACE_NAME("ThreadLocalBuffer::GetBufferGuard"); + static thread_local std::unique_ptr<BufferType> buffer_guard; + return buffer_guard; + } +}; + +// Instantiation of the static ThreadLocalBuffer::buffer_ member. +template <typename T, typename Allocator, std::size_t Capacity, typename Slot> +thread_local + typename ThreadLocalBuffer<T, Allocator, Capacity, Slot>::BufferType* + ThreadLocalBuffer<T, Allocator, Capacity, Slot>::buffer_; + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/type_operators.h b/libs/vr/libpdx/private/pdx/rpc/type_operators.h new file mode 100644 index 0000000000..811bd8733c --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/type_operators.h @@ -0,0 +1,195 @@ +#ifndef ANDROID_PDX_RPC_TYPE_OPERATORS_H_ +#define ANDROID_PDX_RPC_TYPE_OPERATORS_H_ + +#include <array> +#include <map> +#include <type_traits> +#include <unordered_map> +#include <vector> + +#include <pdx/channel_handle.h> +#include <pdx/file_handle.h> +#include <pdx/rpc/array_wrapper.h> +#include <pdx/rpc/buffer_wrapper.h> +#include <pdx/rpc/copy_cv_reference.h> +#include <pdx/rpc/pointer_wrapper.h> +#include <pdx/rpc/string_wrapper.h> + +namespace android { +namespace pdx { +namespace rpc { + +// Simplifies type expressions. +template <typename T> +using Decay = typename std::decay<T>::type; + +// Compares the underlying type of A and B. +template <typename A, typename B> +using IsEquivalent = typename std::is_same<Decay<A>, Decay<B>>::type; + +// Logical AND over template parameter pack. +template <typename... T> +struct And : std::false_type {}; +template <typename A, typename B> +struct And<A, B> : std::integral_constant<bool, A::value && B::value> {}; +template <typename A, typename B, typename... Rest> +struct And<A, B, Rest...> : And<A, And<B, Rest...>> {}; + +// Determines whether A is convertible to B (serializes to the same format) +// using these rules: +// 1. std:vector<T, Any...> is convertible to ArrayWrapper<T>. +// 2. ArrayWrapper<T> is convertible to std:vector<T, Any...>. +// 3. std::basic_string<T, Any...> is convertible to StringWrapper<T>. +// 4. StringWrapper<T> is convertible to std::basic_string<T, Any...>. +// 5. BufferWrapper<T*> is convertible to BufferWrapper<std::vector<T, +// Any...>>. +// 6. BufferWrapper<std::vector<T, ...>> is convertible to BufferWrapper<T*>. +// 7. The value type T of A and B must match. + +// Compares A and B for convertibility. This base type determines convertibility +// by equivalence of the underlying types of A and B. Specializations of this +// type handle the rules for which complex types are convertible. +template <typename A, typename B> +struct IsConvertible : IsEquivalent<A, B> {}; + +// Compares TT<A, ...> and TT<B, ...>; these are convertible if A and B are +// convertible. +template <template <typename, typename...> class TT, typename A, typename B, + typename... AnyA, typename... AnyB> +struct IsConvertible<TT<A, AnyA...>, TT<B, AnyB...>> + : IsConvertible<Decay<A>, Decay<B>> {}; + +// Compares TT<KeyA, ValueA, ...> and TT<KeyB, ValueB, ...>; these are +// convertible if KeyA and KeyB are +// convertible and ValueA and ValueB are convertible. +template <template <typename, typename, typename...> class TT, typename KeyA, + typename ValueA, typename KeyB, typename ValueB, typename... AnyA, + typename... AnyB> +struct IsConvertible<TT<KeyA, ValueA, AnyA...>, TT<KeyB, ValueB, AnyB...>> + : And<IsConvertible<Decay<KeyA>, Decay<KeyB>>, + IsConvertible<Decay<ValueA>, Decay<ValueB>>> {}; + +// Compares two std::pairs to see if the corresponding elements are convertible. +template <typename A, typename B, typename C, typename D> +struct IsConvertible<std::pair<A, B>, std::pair<C, D>> + : And<IsConvertible<Decay<A>, Decay<C>>, + IsConvertible<Decay<B>, Decay<D>>> {}; + +// Compares std::pair with a two-element std::tuple to see if the corresponding +// elements are convertible. +template <typename A, typename B, typename C, typename D> +struct IsConvertible<std::pair<A, B>, std::tuple<C, D>> + : And<IsConvertible<Decay<A>, Decay<C>>, + IsConvertible<Decay<B>, Decay<D>>> {}; +template <typename A, typename B, typename C, typename D> +struct IsConvertible<std::tuple<A, B>, std::pair<C, D>> + : And<IsConvertible<Decay<A>, Decay<C>>, + IsConvertible<Decay<B>, Decay<D>>> {}; + +// Compares two std::tuples to see if the corresponding elements are +// convertible. +template <typename... A, typename... B> +struct IsConvertible<std::tuple<A...>, std::tuple<B...>> + : And<IsConvertible<Decay<A>, Decay<B>>...> {}; + +// Compares std::vector, std::array, and ArrayWrapper; these are convertible if +// the value types are convertible. +template <typename A, typename B, typename... Any> +struct IsConvertible<std::vector<A, Any...>, ArrayWrapper<B>> + : IsConvertible<Decay<A>, Decay<B>> {}; +template <typename A, typename B, typename... Any> +struct IsConvertible<ArrayWrapper<A>, std::vector<B, Any...>> + : IsConvertible<Decay<A>, Decay<B>> {}; +template <typename A, typename B, typename... Any, std::size_t Size> +struct IsConvertible<std::vector<A, Any...>, std::array<B, Size>> + : IsConvertible<Decay<A>, Decay<B>> {}; +template <typename A, typename B, typename... Any, std::size_t Size> +struct IsConvertible<std::array<A, Size>, std::vector<B, Any...>> + : IsConvertible<Decay<A>, Decay<B>> {}; +template <typename A, typename B, std::size_t Size> +struct IsConvertible<ArrayWrapper<A>, std::array<B, Size>> + : IsConvertible<Decay<A>, Decay<B>> {}; +template <typename A, typename B, std::size_t Size> +struct IsConvertible<std::array<A, Size>, ArrayWrapper<B>> + : IsConvertible<Decay<A>, Decay<B>> {}; + +// Compares std::map and std::unordered_map; these are convertible if the keys +// are convertible and the values are convertible. +template <typename KeyA, typename ValueA, typename KeyB, typename ValueB, + typename... AnyA, typename... AnyB> +struct IsConvertible<std::map<KeyA, ValueA, AnyA...>, + std::unordered_map<KeyB, ValueB, AnyB...>> + : And<IsConvertible<Decay<KeyA>, Decay<KeyB>>, + IsConvertible<Decay<ValueA>, Decay<ValueB>>> {}; +template <typename KeyA, typename ValueA, typename KeyB, typename ValueB, + typename... AnyA, typename... AnyB> +struct IsConvertible<std::unordered_map<KeyA, ValueA, AnyA...>, + std::map<KeyB, ValueB, AnyB...>> + : And<IsConvertible<Decay<KeyA>, Decay<KeyB>>, + IsConvertible<Decay<ValueA>, Decay<ValueB>>> {}; + +// Compares BufferWrapper<A*> and BufferWrapper<std::vector<B>>; these are +// convertible if A and B are equivalent. Allocator types are not relevant to +// convertibility. +template <typename A, typename B, typename Allocator> +struct IsConvertible<BufferWrapper<A*>, + BufferWrapper<std::vector<B, Allocator>>> + : IsEquivalent<A, B> {}; +template <typename A, typename B, typename Allocator> +struct IsConvertible<BufferWrapper<std::vector<A, Allocator>>, + BufferWrapper<B*>> : IsEquivalent<A, B> {}; +template <typename A, typename B, typename AllocatorA, typename AllocatorB> +struct IsConvertible<BufferWrapper<std::vector<A, AllocatorA>>, + BufferWrapper<std::vector<B, AllocatorB>>> + : IsEquivalent<A, B> {}; +template <typename A, typename B> +struct IsConvertible<BufferWrapper<A*>, BufferWrapper<B*>> + : IsEquivalent<A, B> {}; + +// Compares std::basic_string<A, ...> and StringWrapper<B>; these are +// convertible if A and B are equivalent. +template <typename A, typename B, typename... Any> +struct IsConvertible<std::basic_string<A, Any...>, StringWrapper<B>> + : IsEquivalent<A, B> {}; +template <typename A, typename B, typename... Any> +struct IsConvertible<StringWrapper<A>, std::basic_string<B, Any...>> + : IsEquivalent<A, B> {}; + +// Compares PointerWrapper<A> and B; these are convertible if A and B are +// convertible. +template <typename A, typename B> +struct IsConvertible<PointerWrapper<A>, B> : IsConvertible<Decay<A>, Decay<B>> { +}; +template <typename A, typename B> +struct IsConvertible<A, PointerWrapper<B>> : IsConvertible<Decay<A>, Decay<B>> { +}; + +// LocalHandle is convertible to RemoteHandle on the service side. This means +// that a RemoteHandle may be supplied by a service when the protocol calls for +// a LocalHandle return value. The other way around is not safe and can leak +// file descriptors. The ServicePayload class enforces this policy by only +// supporting RemoteHandle for pushed handles. +template <> +struct IsConvertible<LocalHandle, RemoteHandle> : std::true_type {}; +template <> +struct IsConvertible<LocalHandle, BorrowedHandle> : std::true_type {}; + +template <> +struct IsConvertible<LocalChannelHandle, RemoteChannelHandle> : std::true_type { +}; +template <> +struct IsConvertible<LocalChannelHandle, BorrowedChannelHandle> + : std::true_type {}; + +// Conditionally "rewrites" type A as type B, including cv-reference qualifiers, +// iff A is convertible to B. +template <typename A, typename B> +using ConditionalRewrite = + typename std::conditional<IsConvertible<Decay<A>, Decay<B>>::value, + CopyCVReferenceType<A, B>, A>::type; + +} // namespace rpc +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_RPC_TYPE_OPERATORS_H_ diff --git a/libs/vr/libpdx/private/pdx/rpc/variant.h b/libs/vr/libpdx/private/pdx/rpc/variant.h new file mode 100644 index 0000000000..cb44a51b8d --- /dev/null +++ b/libs/vr/libpdx/private/pdx/rpc/variant.h @@ -0,0 +1,686 @@ +#ifndef ANDROID_PDX_RPC_VARIANT_H_ +#define ANDROID_PDX_RPC_VARIANT_H_ + +#include <cstdint> +#include <tuple> +#include <type_traits> + +namespace android { +namespace pdx { +namespace rpc { + +// Type tag denoting an empty variant. +struct EmptyVariant {}; + +namespace detail { + +// Type for matching tagged overloads. +template <typename T> +struct TypeTag {}; + +// Determines the type of the I-th element of Types.... +template <std::size_t I, typename... Types> +using TypeForIndex = std::tuple_element_t<I, std::tuple<Types...>>; + +// Determines the type tag for the I-th element of Types.... +template <std::size_t I, typename... Types> +using TypeTagForIndex = TypeTag<TypeForIndex<I, Types...>>; + +// Enable if T(Args...) is well formed. +template <typename R, typename T, typename... Args> +using EnableIfConstructible = + typename std::enable_if<std::is_constructible<T, Args...>::value, R>::type; +// Enable if T(Args...) is not well formed. +template <typename R, typename T, typename... Args> +using EnableIfNotConstructible = + typename std::enable_if<!std::is_constructible<T, Args...>::value, R>::type; + +// Determines whether T is an element of Types...; +template <typename... Types> +struct HasType : std::false_type {}; +template <typename T, typename U> +struct HasType<T, U> : std::is_same<std::decay_t<T>, std::decay_t<U>> {}; +template <typename T, typename First, typename... Rest> +struct HasType<T, First, Rest...> + : std::integral_constant<bool, HasType<T, First>::value || + HasType<T, Rest...>::value> {}; + +// Defines set operations on a set of Types... +template <typename... Types> +struct Set { + // Default specialization catches the empty set, which is always a subset. + template <typename...> + struct IsSubset : std::true_type {}; + template <typename T> + struct IsSubset<T> : HasType<T, Types...> {}; + template <typename First, typename... Rest> + struct IsSubset<First, Rest...> + : std::integral_constant<bool, IsSubset<First>::value && + IsSubset<Rest...>::value> {}; +}; + +// Determines the number of elements of Types... that are constructible from +// From. +template <typename... Types> +struct ConstructibleCount; +template <typename From, typename To> +struct ConstructibleCount<From, To> + : std::integral_constant<std::size_t, + std::is_constructible<To, From>::value> {}; +template <typename From, typename First, typename... Rest> +struct ConstructibleCount<From, First, Rest...> + : std::integral_constant<std::size_t, + std::is_constructible<First, From>::value + + ConstructibleCount<From, Rest...>::value> {}; + +// Enable if T is an element of Types... +template <typename R, typename T, typename... Types> +using EnableIfElement = + typename std::enable_if<HasType<T, Types...>::value, R>::type; +// Enable if T is not an element of Types... +template <typename R, typename T, typename... Types> +using EnableIfNotElement = + typename std::enable_if<!HasType<T, Types...>::value, R>::type; + +// Enable if T is convertible to an element of Types... T is considered +// convertible IIF a single element of Types... is assignable from T and T is +// not a direct element of Types... +template <typename R, typename T, typename... Types> +using EnableIfConvertible = + typename std::enable_if<!HasType<T, Types...>::value && + ConstructibleCount<T, Types...>::value == 1, + R>::type; + +// Enable if T is assignable to an element of Types... T is considered +// assignable IFF a single element of Types... is constructible from T or T is a +// direct element of Types.... Note that T is REQUIRED to be an element of +// Types... when multiple elements are constructible from T to prevent ambiguity +// in conversion. +template <typename R, typename T, typename... Types> +using EnableIfAssignable = + typename std::enable_if<HasType<T, Types...>::value || + ConstructibleCount<T, Types...>::value == 1, + R>::type; + +// Selects a type for SFINAE constructor selection. +template <bool CondA, typename SelectA, typename SelectB> +using Select = std::conditional_t<CondA, SelectA, SelectB>; + +// Recursive union type. +template <typename... Types> +union Union; + +// Specialization handling a singular type, terminating template recursion. +template <typename Type> +union Union<Type> { + Union() {} + ~Union() {} + + template <typename T> + Union(std::int32_t index, std::int32_t* index_out, TypeTag<Type>, T&& value) + : first_(std::forward<T>(value)) { + *index_out = index; + } + template <typename T, typename = EnableIfAssignable<void, T, Type>> + Union(std::int32_t index, std::int32_t* index_out, T&& value) + : first_(std::forward<T>(value)) { + *index_out = index; + } + + Type& get(TypeTag<Type>) { return first_; } + const Type& get(TypeTag<Type>) const { return first_; } + EmptyVariant get(TypeTag<EmptyVariant>) const { return {}; } + constexpr std::int32_t index(TypeTag<Type>) const { return 0; } + + template <typename... Args> + std::int32_t Construct(TypeTag<Type>, Args&&... args) { + new (&first_) Type(std::forward<Args>(args)...); + return 0; + } + template <typename... Args> + EnableIfConstructible<std::int32_t, Type, Args...> Construct(Args&&... args) { + new (&first_) Type(std::forward<Args>(args)...); + return 0; + } + + void Destruct(std::int32_t target_index) { + if (target_index == index(TypeTag<Type>{})) { + (&get(TypeTag<Type>{}))->~Type(); + } + } + + template <typename T> + bool Assign(TypeTag<Type>, std::int32_t target_index, T&& value) { + if (target_index == 0) { + first_ = std::forward<T>(value); + return true; + } else { + return false; + } + } + template <typename T> + EnableIfConstructible<bool, Type, T> Assign(std::int32_t target_index, + T&& value) { + if (target_index == 0) { + first_ = std::forward<T>(value); + return true; + } else { + return false; + } + } + template <typename T> + EnableIfNotConstructible<bool, Type, T> Assign(std::int32_t /*target_index*/, + T&& /*value*/) { + return false; + } + + template <typename Op> + decltype(auto) Visit(std::int32_t target_index, Op&& op) { + if (target_index == index(TypeTag<Type>{})) + return std::forward<Op>(op)(get(TypeTag<Type>{})); + else + return std::forward<Op>(op)(get(TypeTag<EmptyVariant>{})); + } + template <typename Op> + decltype(auto) Visit(std::int32_t target_index, Op&& op) const { + if (target_index == index(TypeTag<Type>{})) + return std::forward<Op>(op)(get(TypeTag<Type>{})); + else + return std::forward<Op>(op)(get(TypeTag<EmptyVariant>{})); + } + + template <typename... Args> + bool Become(std::int32_t target_index, Args&&... args) { + if (target_index == index(TypeTag<Type>{})) { + Construct(TypeTag<Type>{}, std::forward<Args>(args)...); + return true; + } else { + return false; + } + } + + private: + Type first_; +}; + +// Specialization that recursively unions types from the paramater pack. +template <typename First, typename... Rest> +union Union<First, Rest...> { + Union() {} + ~Union() {} + + template <typename T> + Union(std::int32_t index, std::int32_t* index_out, TypeTag<First>, T&& value) + : first_(std::forward<T>(value)) { + *index_out = index; + } + template <typename T, typename U> + Union(std::int32_t index, std::int32_t* index_out, TypeTag<T>, U&& value) + : rest_(index + 1, index_out, TypeTag<T>{}, std::forward<U>(value)) {} + + struct FirstType {}; + struct RestType {}; + template <typename T> + using SelectConstructor = + Select<ConstructibleCount<T, First>::value == 1, FirstType, RestType>; + + template <typename T> + Union(std::int32_t index, std::int32_t* index_out, T&& value) + : Union(index, index_out, std::forward<T>(value), + SelectConstructor<T>{}) {} + + template <typename T> + Union(std::int32_t index, std::int32_t* index_out, T&& value, FirstType) + : first_(std::forward<T>(value)) { + *index_out = index; + } + template <typename T> + Union(std::int32_t index, std::int32_t* index_out, T&& value, RestType) + : rest_(index + 1, index_out, std::forward<T>(value)) {} + + First& get(TypeTag<First>) { return first_; } + const First& get(TypeTag<First>) const { return first_; } + constexpr std::int32_t index(TypeTag<First>) const { return 0; } + + template <typename T> + T& get(TypeTag<T>) { + return rest_.template get(TypeTag<T>{}); + } + template <typename T> + const T& get(TypeTag<T>) const { + return rest_.template get(TypeTag<T>{}); + } + template <typename T> + constexpr std::int32_t index(TypeTag<T>) const { + return 1 + rest_.template index(TypeTag<T>{}); + } + + template <typename... Args> + std::int32_t Construct(TypeTag<First>, Args&&... args) { + new (&first_) First(std::forward<Args>(args)...); + return 0; + } + template <typename T, typename... Args> + std::int32_t Construct(TypeTag<T>, Args&&... args) { + return 1 + + rest_.template Construct(TypeTag<T>{}, std::forward<Args>(args)...); + } + + template <typename... Args> + EnableIfConstructible<std::int32_t, First, Args...> Construct( + Args&&... args) { + new (&first_) First(std::forward<Args>(args)...); + return 0; + } + template <typename... Args> + EnableIfNotConstructible<std::int32_t, First, Args...> Construct( + Args&&... args) { + return 1 + rest_.template Construct(std::forward<Args>(args)...); + } + + void Destruct(std::int32_t target_index) { + if (target_index == index(TypeTag<First>{})) { + (get(TypeTag<First>{})).~First(); + } else { + rest_.Destruct(target_index - 1); + } + } + + template <typename T> + bool Assign(TypeTag<First>, std::int32_t target_index, T&& value) { + if (target_index == 0) { + first_ = std::forward<T>(value); + return true; + } else { + return false; + } + } + template <typename T, typename U> + bool Assign(TypeTag<T>, std::int32_t target_index, U&& value) { + return rest_.Assign(TypeTag<T>{}, target_index - 1, std::forward<U>(value)); + } + template <typename T> + EnableIfConstructible<bool, First, T> Assign(std::int32_t target_index, + T&& value) { + if (target_index == 0) { + first_ = std::forward<T>(value); + return true; + } else { + return rest_.Assign(target_index - 1, std::forward<T>(value)); + } + } + template <typename T> + EnableIfNotConstructible<bool, First, T> Assign(std::int32_t target_index, + T&& value) { + return rest_.Assign(target_index - 1, std::forward<T>(value)); + } + + // Recursively traverses the union and calls Op on the active value when the + // active type is found. If the union is empty Op is called on EmptyVariant. + // TODO(eieio): This could be refactored into an array or jump table. It's + // unclear whether this would be more efficient for practical variant arity. + template <typename Op> + decltype(auto) Visit(std::int32_t target_index, Op&& op) { + if (target_index == index(TypeTag<First>{})) + return std::forward<Op>(op)(get(TypeTag<First>{})); + else + return rest_.Visit(target_index - 1, std::forward<Op>(op)); + } + template <typename Op> + decltype(auto) Visit(std::int32_t target_index, Op&& op) const { + if (target_index == index(TypeTag<First>{})) + return std::forward<Op>(op)(get(TypeTag<First>{})); + else + return rest_.Visit(target_index - 1, std::forward<Op>(op)); + } + + template <typename... Args> + bool Become(std::int32_t target_index, Args&&... args) { + if (target_index == index(TypeTag<First>{})) { + Construct(TypeTag<First>{}, std::forward<Args>(args)...); + return true; + } else { + return rest_.Become(target_index - 1, std::forward<Args>(args)...); + } + } + + private: + First first_; + Union<Rest...> rest_; +}; + +} // namespace detail + +template <typename... Types> +class Variant { + private: + // Convenience types. + template <typename T> + using TypeTag = detail::TypeTag<T>; + template <typename T> + using DecayedTypeTag = TypeTag<std::decay_t<T>>; + template <std::size_t I> + using TypeForIndex = detail::TypeForIndex<I, Types...>; + template <std::size_t I> + using TypeTagForIndex = detail::TypeTagForIndex<I, Types...>; + template <typename T> + using HasType = detail::HasType<T, Types...>; + template <typename R, typename T> + using EnableIfElement = detail::EnableIfElement<R, T, Types...>; + template <typename R, typename T> + using EnableIfConvertible = detail::EnableIfConvertible<R, T, Types...>; + template <typename R, typename T> + using EnableIfAssignable = detail::EnableIfAssignable<R, T, Types...>; + + struct Direct {}; + struct Convert {}; + template <typename T> + using SelectConstructor = detail::Select<HasType<T>::value, Direct, Convert>; + + // Constructs by type tag when T is an direct element of Types... + template <typename T> + explicit Variant(T&& value, Direct) + : value_(0, &index_, DecayedTypeTag<T>{}, std::forward<T>(value)) {} + // Conversion constructor when T is not a direct element of Types... + template <typename T> + explicit Variant(T&& value, Convert) + : value_(0, &index_, std::forward<T>(value)) {} + + public: + // Variants are default construcible, regardless of whether the elements are + // default constructible. Default consruction yields an empty Variant. + Variant() {} + explicit Variant(EmptyVariant) {} + ~Variant() { Destruct(); } + + // Copy and move construction from Variant types. Each element of OtherTypes + // must be convertible to an element of Types. + template <typename... OtherTypes> + explicit Variant(const Variant<OtherTypes...>& other) { + other.Visit([this](const auto& value) { Construct(value); }); + } + template <typename... OtherTypes> + explicit Variant(Variant<OtherTypes...>&& other) { + other.Visit([this](auto&& value) { Construct(std::move(value)); }); + } + + // Construction from non-Variant types. + template <typename T, typename = EnableIfAssignable<void, T>> + explicit Variant(T&& value) + : Variant(std::forward<T>(value), SelectConstructor<T>{}) {} + + // Performs assignment from type T belonging to Types. This overload takes + // priority to prevent implicit conversion in cases where T is implicitly + // convertible to multiple elements of Types. + template <typename T> + EnableIfElement<Variant&, T> operator=(T&& value) { + Assign(DecayedTypeTag<T>{}, std::forward<T>(value)); + return *this; + } + + // Performs assignment from type T not belonging to Types. This overload + // matches in cases where conversion is the only viable option. + template <typename T> + EnableIfConvertible<Variant&, T> operator=(T&& value) { + Assign(std::forward<T>(value)); + return *this; + } + + // Handles assignment from the empty type. This overload supports assignment + // in visitors using generic lambdas. + Variant& operator=(EmptyVariant) { + Assign(EmptyVariant{}); + return *this; + } + + // Assignment from Variant types. Each element of OtherTypes must be + // convertible to an element of Types. Forwards through non-Variant assignment + // operators to apply conversion checks. + template <typename... OtherTypes> + Variant& operator=(const Variant<OtherTypes...>& other) { + other.Visit([this](const auto& value) { *this = value; }); + return *this; + } + template <typename... OtherTypes> + Variant& operator=(Variant<OtherTypes...>&& other) { + other.Visit([this](auto&& value) { *this = std::move(value); }); + return *this; + } + + // Becomes the target type, constructing a new element from the given + // arguments if necessary. No action is taken if the active element is already + // the target type. Otherwise the active element is destroyed and replaced by + // constructing an element of the new type using |Args|. An invalid target + // type index results in an empty Variant. + template <typename... Args> + void Become(std::int32_t target_index, Args&&... args) { + if (target_index != index()) { + Destruct(); + index_ = value_.Become(target_index, std::forward<Args>(args)...) + ? target_index + : kEmptyIndex; + } + } + + // Invokes |Op| on the active element. If the Variant is empty |Op| is invoked + // on EmptyVariant. + template <typename Op> + decltype(auto) Visit(Op&& op) { + return value_.Visit(index_, std::forward<Op>(op)); + } + template <typename Op> + decltype(auto) Visit(Op&& op) const { + return value_.Visit(index_, std::forward<Op>(op)); + } + + // Index returned when the Variant is empty. + enum : std::int32_t { kEmptyIndex = -1 }; + + // Returns the index of the given type. + template <typename T> + constexpr std::int32_t index_of() const { + static_assert(HasType<T>::value, "T is not an element type of Variant."); + return value_.template index(DecayedTypeTag<T>{}); + } + + // Returns the index of the active type. If the Variant is empty -1 is + // returned. + std::int32_t index() const { return index_; } + + // Returns true if the given type is active, false otherwise. + template <typename T> + bool is() const { + static_assert(HasType<T>::value, "T is not an element type of Variant."); + return index() == index_of<T>(); + } + + // Returns true if the Variant is empty, false otherwise. + bool empty() const { return index() == kEmptyIndex; } + + // Element accessors. Returns a pointer to the active value if the given + // type/index is active, otherwise nullptr is returned. + template <typename T> + T* get() { + if (is<T>()) + return &value_.template get(DecayedTypeTag<T>{}); + else + return nullptr; + } + template <typename T> + const T* get() const { + if (is<T>()) + return &value_.template get(DecayedTypeTag<T>{}); + else + return nullptr; + } + template <std::size_t I> + TypeForIndex<I>* get() { + if (is<TypeForIndex<I>>()) + return &value_.template get(TypeTagForIndex<I>{}); + else + return nullptr; + } + template <std::size_t I> + const TypeForIndex<I>* get() const { + if (is<TypeForIndex<I>>()) + return &value_.template get(TypeTagForIndex<I>{}); + else + return nullptr; + } + + private: + std::int32_t index_ = kEmptyIndex; + detail::Union<std::decay_t<Types>...> value_; + + // Constructs an element from the given arguments and sets the Variant to the + // resulting type. + template <typename... Args> + void Construct(Args&&... args) { + index_ = value_.template Construct(std::forward<Args>(args)...); + } + void Construct(EmptyVariant) {} + + // Destroys the active element of the Variant. + void Destruct() { value_.Destruct(index_); } + + // Assigns the Variant when non-empty and the current type matches the target + // type, otherwise destroys the current value and constructs a element of the + // new type. Tagged assignment is used when T is an element of the Variant to + // prevent implicit conversion in cases where T is implicitly convertible to + // multiple element types. + template <typename T, typename U> + void Assign(TypeTag<T>, U&& value) { + if (!value_.template Assign(TypeTag<T>{}, index_, std::forward<U>(value))) { + Destruct(); + Construct(TypeTag<T>{}, std::forward<U>(value)); + } + } + template <typename T> + void Assign(T&& value) { + if (!value_.template Assign(index_, std::forward<T>(value))) { + Destruct(); + Construct(std::forward<T>(value)); + } + } + // Handles assignment from an empty Variant. + void Assign(EmptyVariant) { Destruct(); } +}; + +// Utility type to extract/convert values from a variant. This class simplifies +// conditional logic to get/move/swap/action values from a variant when one or +// more elements are compatible with the destination type. +// +// Example: +// Variant<int, bool, std::string> v(10); +// bool bool_value; +// if (IfAnyOf<int, bool>::Get(v, &bool_value)) { +// DoSomething(bool_value); +// } else { +// HandleInvalidType(); +// } +// IfAnyOf<int>::Call(v, [](const auto& value) { DoSomething(value); }); +// +template <typename... ValidTypes> +struct IfAnyOf { + // Calls Op on the underlying value of the variant and returns true when the + // variant is a valid type, otherwise does nothing and returns false. + template <typename Op, typename... Types> + static bool Call(Variant<Types...>* variant, Op&& op) { + static_assert( + detail::Set<Types...>::template IsSubset<ValidTypes...>::value, + "ValidTypes may only contain element types from the Variant."); + return variant->Visit(CallOp<Op>{std::forward<Op>(op)}); + } + template <typename Op, typename... Types> + static bool Call(const Variant<Types...>* variant, Op&& op) { + static_assert( + detail::Set<Types...>::template IsSubset<ValidTypes...>::value, + "ValidTypes may only contain element types from the Variant."); + return variant->Visit(CallOp<Op>{std::forward<Op>(op)}); + } + + // Gets/converts the underlying value of the variant to type T and returns + // true when the variant is a valid type, otherwise does nothing and returns + // false. + template <typename T, typename... Types> + static bool Get(const Variant<Types...>* variant, T* value_out) { + return Call(variant, + [value_out](const auto& value) { *value_out = value; }); + } + + // Moves the underlying value of the variant and returns true when the variant + // is a valid type, otherwise does nothing and returns false. + template <typename T, typename... Types> + static bool Take(Variant<Types...>* variant, T* value_out) { + return Call(variant, + [value_out](auto&& value) { *value_out = std::move(value); }); + } + + // Swaps the underlying value of the variant with |*value_out| and returns + // true when the variant is a valid type, otherwise does nothing and returns + // false. + template <typename T, typename... Types> + static bool Swap(Variant<Types...>* variant, T* value_out) { + return Call(variant, + [value_out](auto&& value) { std::swap(*value_out, value); }); + } + + private: + template <typename Op> + struct CallOp { + Op&& op; + template <typename U> + detail::EnableIfNotElement<bool, U, ValidTypes...> operator()(U&&) { + return false; + } + template <typename U> + detail::EnableIfElement<bool, U, ValidTypes...> operator()(const U& value) { + std::forward<Op>(op)(value); + return true; + } + template <typename U> + detail::EnableIfElement<bool, U, ValidTypes...> operator()(U&& value) { + std::forward<Op>(op)(std::forward<U>(value)); + return true; + } + }; +}; + +} // namespace rpc +} // namespace pdx +} // namespace android + +// Overloads of std::get<T> and std::get<I> for android::pdx::rpc::Variant. +namespace std { + +template <typename T, typename... Types> +inline T& get(::android::pdx::rpc::Variant<Types...>& v) { + return *v.template get<T>(); +} +template <typename T, typename... Types> +inline T&& get(::android::pdx::rpc::Variant<Types...>&& v) { + return std::move(*v.template get<T>()); +} +template <typename T, typename... Types> +inline const T& get(const ::android::pdx::rpc::Variant<Types...>& v) { + return *v.template get<T>(); +} +template <std::size_t I, typename... Types> +inline ::android::pdx::rpc::detail::TypeForIndex<I, Types...>& get( + ::android::pdx::rpc::Variant<Types...>& v) { + return *v.template get<I>(); +} +template <std::size_t I, typename... Types> +inline ::android::pdx::rpc::detail::TypeForIndex<I, Types...>&& get( + ::android::pdx::rpc::Variant<Types...>&& v) { + return std::move(*v.template get<I>()); +} +template <std::size_t I, typename... Types> +inline const ::android::pdx::rpc::detail::TypeForIndex<I, Types...>& get( + const ::android::pdx::rpc::Variant<Types...>& v) { + return *v.template get<I>(); +} + +} // namespace std + +#endif // ANDROID_PDX_RPC_VARIANT_H_ diff --git a/libs/vr/libpdx/private/pdx/service.h b/libs/vr/libpdx/private/pdx/service.h new file mode 100644 index 0000000000..0d30614562 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/service.h @@ -0,0 +1,742 @@ +#ifndef ANDROID_PDX_SERVICE_H_ +#define ANDROID_PDX_SERVICE_H_ + +#include <errno.h> +#include <log/log.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <algorithm> +#include <memory> +#include <mutex> +#include <string> +#include <unordered_map> +#include <vector> + +#include "pdx/channel_handle.h" +#include "pdx/file_handle.h" +#include "pdx/message_reader.h" +#include "pdx/message_writer.h" +#include "pdx/service_endpoint.h" + +namespace android { +namespace pdx { + +class Service; + +namespace opcodes { + +/* + * Reserved message opcodes used by libpdx. The reserved opcodes start at the + * max positive signed integer for the system and go down. + * In contrast, service opcodes start at zero and go up. This scheme leaves + * most of the positive integer space for services, a tiny fraction of the + * positive integer space for the framework, and the entire negative integer + * space for the kernel. + */ +#define PDX_OPCODE(name, n) name = ((-1U >> 1) - (n)) // 0x7fff..ffff - n + +enum { + // System message sent when a new client channel is open. + CHANNEL_OPEN = -1, + // System message sent when a channel is closed. + CHANNEL_CLOSE = -2, + // Request the service to reload system properties. + PDX_OPCODE(REPORT_SYSPROP_CHANGE, 0), + // Request the service to dump state and return it in a text buffer. + PDX_OPCODE(DUMP_STATE, 1), +}; + +} // namespace opcodes + +/* + * Base class of service-side per-channel context classes. + */ +class Channel : public std::enable_shared_from_this<Channel> { + public: + Channel() {} + virtual ~Channel() {} + + /* + * Utility to get a shared_ptr reference from the channel context pointer. + */ + static std::shared_ptr<Channel> GetFromMessageInfo(const MessageInfo& info); +}; + +/* + * Message class represents an RPC message, and implicitly the blocked sender + * waiting for a response. Every message should get a reply, at some point + * (unless the endpoint is closed), to prevent clients from blocking + * indefinitely. In order to enforce this and prevent leaking message ids, + * Message automatically replies with an error to the client on destruction, + * unless one of two things happens: + * + * 1. The service calls one of the reply methods before the Message is + * destroyed. + * 2. The responsibility for the message is moved to another instance of + * Message, using either move construction or move assignment. + * + * The second case is useful for services that need to delay responding to a + * sender until a later time. In this situation the service can move the + * Message to another instance in a suitable data structure for later use. The + * moved-to Message then takes on the same behavior and responsibilities + * described above. + */ +class Message : public OutputResourceMapper, public InputResourceMapper { + public: + Message(); + Message(const MessageInfo& info); + ~Message(); + + /* + * Message objects support move construction and assignment. + */ + Message(Message&& other); + Message& operator=(Message&& other); + + /* + * Read/write payload, in either single buffer or iovec form. + */ + Status<size_t> ReadVector(const iovec* vector, size_t vector_length); + Status<size_t> Read(void* buffer, size_t length); + Status<size_t> WriteVector(const iovec* vector, size_t vector_length); + Status<size_t> Write(const void* buffer, size_t length); + + template <size_t N> + inline Status<size_t> ReadVector(const iovec (&vector)[N]) { + return ReadVector(vector, N); + } + + template <size_t N> + inline Status<size_t> WriteVector(const iovec (&vector)[N]) { + return WriteVector(vector, N); + } + + // Helper functions to read/write all requested bytes, and return EIO if not + // all were read/written. + Status<void> ReadVectorAll(const iovec* vector, size_t vector_length); + Status<void> WriteVectorAll(const iovec* vector, size_t vector_length); + + inline Status<void> ReadAll(void* buffer, size_t length) { + Status<size_t> status = Read(buffer, length); + if (status && status.get() < length) + status.SetError(EIO); + Status<void> ret; + ret.PropagateError(status); + return ret; + } + inline Status<void> WriteAll(const void* buffer, size_t length) { + Status<size_t> status = Write(buffer, length); + if (status && status.get() < length) + status.SetError(EIO); + Status<void> ret; + ret.PropagateError(status); + return ret; + } + + template <size_t N> + inline Status<void> ReadVectorAll(const iovec (&vector)[N]) { + return ReadVectorAll(vector, N); + } + + template <size_t N> + inline Status<void> WriteVectorAll(const iovec (&vector)[N]) { + return WriteVectorAll(vector, N); + } + + // OutputResourceMapper + Status<FileReference> PushFileHandle(const LocalHandle& handle) override; + Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override; + Status<FileReference> PushFileHandle(const RemoteHandle& handle) override; + Status<ChannelReference> PushChannelHandle( + const LocalChannelHandle& handle) override; + Status<ChannelReference> PushChannelHandle( + const BorrowedChannelHandle& handle) override; + Status<ChannelReference> PushChannelHandle( + const RemoteChannelHandle& handle) override; + + // InputResourceMapper + bool GetFileHandle(FileReference ref, LocalHandle* handle) override; + bool GetChannelHandle(ChannelReference ref, + LocalChannelHandle* handle) override; + + /* + * Various ways to reply to a message. + */ + Status<void> Reply(int return_code); + Status<void> ReplyError(unsigned int error); + Status<void> ReplyFileDescriptor(unsigned int fd); + Status<void> Reply(const LocalHandle& handle); + Status<void> Reply(const BorrowedHandle& handle); + Status<void> Reply(const RemoteHandle& handle); + Status<void> Reply(const LocalChannelHandle& handle); + Status<void> Reply(const BorrowedChannelHandle& handle); + Status<void> Reply(const RemoteChannelHandle& handle); + + template <typename T> + inline Status<void> Reply(const Status<T>& status) { + return status ? Reply(status.get()) : ReplyError(status.error()); + } + + inline Status<void> Reply(const Status<void>& status) { + return status ? Reply(0) : ReplyError(status.error()); + } + + /* + * Update the channel event bits with the given clear and set masks. + */ + Status<void> ModifyChannelEvents(int clear_mask, int set_mask); + + /* + * Create a new channel and push it as a file descriptor to the client. See + * Service::PushChannel() for a detail description of this method's operation. + */ + Status<RemoteChannelHandle> PushChannel( + int flags, const std::shared_ptr<Channel>& channel, int* channel_id); + + /* + * Create a new channel and push it as a file descriptor to the client. See + * Service::PushChannel() for a detail description of this method's operation. + */ + Status<RemoteChannelHandle> PushChannel( + Service* service, int flags, const std::shared_ptr<Channel>& channel, + int* channel_id); + + /* + * Check whether the |ref| is a reference to channel to this service. + * If the channel reference in question is valid, the Channel object is + * returned in |channel| when non-nullptr. + * + * Return values: + * channel_id - id of the channel if the |ref| is a valid reference to + * this service's channel. + * Errors: + * EOPNOTSUPP - the file descriptor is not a channel or is a channel to + * another service. + * EBADF - the file descriptor is invalid. + * FAULT - |channel_id| or |channel| are non-nullptr and point to invalid + * memory addresses. + * EINVAL - the value of |ref| is invalid or the message id for this + * message is no longer valid. + */ + Status<int> CheckChannel(ChannelReference ref, + std::shared_ptr<Channel>* channel) const; + + /* + * Overload of CheckChannel() that checks whether the channel reference is for + * a channel to the service |service|. + */ + Status<int> CheckChannel(const Service* service, ChannelReference ref, + std::shared_ptr<Channel>* channel) const; + + /* + * Overload of CheckChannel() that automatically converts to shared pointers + * to types derived from Channel. + */ + template <class C> + Status<int> CheckChannel(ChannelReference ref, + std::shared_ptr<C>* channel) const { + std::shared_ptr<Channel> base_pointer; + const Status<int> ret = + CheckChannel(ref, channel ? &base_pointer : nullptr); + if (channel) + *channel = std::static_pointer_cast<C>(base_pointer); + return ret; + } + + template <class C> + Status<int> CheckChannel(const Service* service, ChannelReference ref, + std::shared_ptr<C>* channel) const { + std::shared_ptr<Channel> base_pointer; + const Status<int> ret = + CheckChannel(service, ref, channel ? &base_pointer : nullptr); + if (channel) + *channel = std::static_pointer_cast<C>(base_pointer); + return ret; + } + + /* + * MessageInfo accessors. + */ + pid_t GetProcessId() const; + pid_t GetThreadId() const; + uid_t GetEffectiveUserId() const; + gid_t GetEffectiveGroupId() const; + int GetChannelId() const; + int GetMessageId() const; + int GetOp() const; + int GetFlags() const; + size_t GetSendLength() const; + size_t GetReceiveLength() const; + size_t GetFileDescriptorCount() const; + + /* + * Impulses are asynchronous and cannot be replied to. All impulses have this + * invalid message id. + */ + enum { IMPULSE_MESSAGE_ID = -1 }; + + /* + * Returns true if this Message describes an asynchronous "impulse" message. + */ + bool IsImpulse() const { return GetMessageId() == IMPULSE_MESSAGE_ID; } + + /* + * Returns a pointer to the impulse payload. Impulses are a maximum of 32 + * bytes in size and the start of the impulse payload is guaranteed to be + * 8-byte aligned. Use GetSendLength() to determine the payload size. + */ + const std::uint8_t* ImpulseBegin() const; + + /* + * Returns one byte past the end of the impulse payload, as conventional for + * STL iterators. + */ + const std::uint8_t* ImpulseEnd() const; + + /* + * Get/set the Channel object for the channel associated + * with this message. It is up to the caller to synchronize + * these in multi-threaded services. + */ + std::shared_ptr<Channel> GetChannel() const; + Status<void> SetChannel(const std::shared_ptr<Channel>& channnel); + + /* + * Get the Channel object for the channel associated with this message, + * automatically converted to the desired subclass of Channel. + */ + template <class C> + std::shared_ptr<C> GetChannel() const { + return std::static_pointer_cast<C>(GetChannel()); + } + + /* + * Gets the service this message was received on. Returns nullptr if the + * service was destroyed. + */ + std::shared_ptr<Service> GetService() const; + + /* + * Raw access to the MessageInfo for this message. + */ + const MessageInfo& GetInfo() const; + + bool replied() const { return replied_; } + bool IsChannelExpired() const { return channel_.expired(); } + bool IsServiceExpired() const { return service_.expired(); } + + /* + * Returns true if the message is non-empty; that is the message can be + * replied to using this instance. + */ + explicit operator bool() const { return !replied_; } + + const void* GetState() const { return state_; } + void* GetState() { return state_; } + + private: + friend class Service; + + Message(const Message&) = delete; + void operator=(const Message&) = delete; + void Destroy(); + + std::weak_ptr<Service> service_; + std::weak_ptr<Channel> channel_; + MessageInfo info_; + void* state_{nullptr}; + bool replied_; +}; + +// Base class for RPC services. +class Service : public std::enable_shared_from_this<Service> { + public: + Service(const std::string& name, std::unique_ptr<Endpoint> endpoint); + virtual ~Service(); + + /* + * Utility to get a shared_ptr reference from the service context pointer. + */ + static std::shared_ptr<Service> GetFromMessageInfo(const MessageInfo& info); + + /* + * Returns whether initialization was successful. Subclasses that override + * this must call this base method and AND the results with their own. This + * method is not intended to do any initialization work itself, only to + * signal success or failure. + */ + virtual bool IsInitialized() const; + + /* + * Called by defaultHandleMessage in response to a CHANNEL_OPEN message. + * This gives subclasses of Service a convenient hook to create per-channel + * context in the form of a Channel subclass. + * + * The Channel instance returned by this is used to set the channel context + * pointer for the channel that was just opened. + */ + virtual std::shared_ptr<Channel> OnChannelOpen(Message& message); + + /* + * Called by defaultHandleMessage in response to a CHANNEL_CLOSE message. + * This give subclasses of Service a convenient hook to clean up per-channel + * context. + */ + virtual void OnChannelClose(Message& message, + const std::shared_ptr<Channel>& channel); + + /* + * Set the channel context for the given channel. This keeps a reference to + * the Channel object until the channel is closed or another call replaces + * the current value. + */ + Status<void> SetChannel(int channel_id, + const std::shared_ptr<Channel>& channel); + + /* + * Get the channel context for the given channel id. This method should be + * used sparingly because of the performance characteristics of the underlying + * map; it is intended for limited, non-critical path access from outside of + * message dispatch. In most cases lookup by id should be unnecessary in a + * properly designed service; Message::GetChannel() should be used instead + * whenever an operation is in the context of a message. + * + * Again, if you lookup a channel context object for a service by id in a + * message handling path for the same service, you're probably doing something + * wrong. + */ + std::shared_ptr<Channel> GetChannel(int channel_id) const; + + /* + * Get a snapshot of the active channels for this service. This is the + * preferred way to access the set of channels because it avoids potential + * deadlocks and race conditions that may occur when operating on the channel + * map directly. However, it is more expensive than direct iteration because + * of dynamic memory allocation and shared pointer reference costs. + * + * Automatically converts returned items to shared pointers of the type + * std::shared_ptr<C>, where C is the subclass of Channel used by the service. + */ + template <class C> + std::vector<std::shared_ptr<C>> GetChannels() const { + std::lock_guard<std::mutex> autolock(channels_mutex_); + std::vector<std::shared_ptr<C>> items; + items.reserve(channels_.size()); + + for (const auto& pair : channels_) { + items.push_back(std::static_pointer_cast<C>(pair.second)); + } + + return items; + } + + /* + * Close a channel, signaling the client file object and freeing the channel + * id. Once closed, the client side of the channel always returns the error + * ESHUTDOWN and signals the poll/epoll events POLLHUP and POLLFREE. + * + * The internal reference to the Channel instance associated with the channel + * is removed, which may result in the Channel object being freed. + * + * OnChannelClosed is not called in response to this method call. + */ + Status<void> CloseChannel(int channel_id); + + /* + * Update the event bits for the given channel (given by id), using the + * given clear and set masks. + * + * This is useful for asynchronously signaling events that clients may be + * waiting for using select/poll/epoll. + */ + Status<void> ModifyChannelEvents(int channel_id, int clear_mask, + int set_mask); + + /* + * Create a new channel and push it as a file descriptor to the process + * sending the |message|. |flags| may be set to O_NONBLOCK and/or + * O_CLOEXEC to control the initial behavior of the new file descriptor (the + * sending process may change these later using fcntl()). The internal Channel + * instance associated with this channel is set to |channel|, which may be + * nullptr. The new channel id allocated for this channel is returned in + * |channel_id|, which may also be nullptr if not needed. + * + * On success, returns the remote channel handle for the new channel in the + * sending process' handle space. This MUST be returned to the sender via + * Message::Reply(), Message::Write(), or Message::WriteVector(). + * + * On error, returns an errno code describing the cause of the error. + * + * Service::OnChannelCreate() is not called in response to the creation of the + * new channel. + */ + Status<RemoteChannelHandle> PushChannel( + Message* message, int flags, const std::shared_ptr<Channel>& channel, + int* channel_id); + + /* + * Check whether the |ref| is a reference to a channel to this service. + * If the channel reference in question is valid, the Channel object is + * returned in |channel| when non-nullptr. + * + * Return values: + * channel_id - id of the channel if the channel reference. + * Errors: + * EOPNOTSUPP - the file descriptor is not a channel or is a channel to + * another service. + * EBADF - the file descriptor is invalid. + * FAULT - |channel_id| or |channel| are non-nullptr and point to invalid + * memory addresses. + * EINVAL - the value of |ref| is invalid or the message id for this + * message is no longer valid. + */ + Status<int> CheckChannel(const Message* message, ChannelReference ref, + std::shared_ptr<Channel>* channel) const; + + /* + * Overload of CheckChannel() that automatically converts to shared pointers + * of types derived from Channel. + */ + template <class C> + Status<int> CheckChannel(const Message* message, ChannelReference ref, + std::shared_ptr<C>* channel) const { + std::shared_ptr<Channel> base_pointer; + const Status<int> ret = + CheckChannel(message, ref, channel ? &base_pointer : nullptr); + if (channel) + *channel = std::static_pointer_cast<C>(base_pointer); + return ret; + } + + /* + * Handle a message. Subclasses override this to receive messages and decide + * how to dispatch them. + * + * The default implementation simply calls defaultHandleMessage(). + * Subclasses should call the same for any unrecognized message opcodes. + */ + virtual Status<void> HandleMessage(Message& message); + + /* + * Handle an asynchronous message. Subclasses override this to receive + * asynchronous "impulse" messages. Impulses have a limited-size payload that + * is transferred upfront with the message description. + */ + virtual void HandleImpulse(Message& impulse); + + /* + * The default message handler. It is important that all messages + * (eventually) get a reply. This method should be called by subclasses for + * any unrecognized opcodes or otherwise unhandled messages to prevent + * erroneous requests from blocking indefinitely. + * + * Provides default handling of CHANNEL_OPEN and CHANNEL_CLOSE, calling + * OnChannelOpen() and OnChannelClose(), respectively. + * + * For all other message opcodes, this method replies with ENOTSUP. + */ + Status<void> DefaultHandleMessage(Message& message); + + /* + * Called when system properties have changed. Subclasses should implement + * this method if they need to handle when system properties change. + */ + virtual void OnSysPropChange(); + + /* + * Get the endpoint for the service. + */ + Endpoint* endpoint() const { return endpoint_.get(); } + + /* + * Cancels the endpoint, unblocking any receiver threads waiting in + * ReceiveAndDispatch(). + */ + Status<void> Cancel(); + + /* + * Iterator type for Channel map iterators. + */ + using ChannelIterator = + std::unordered_map<int, std::shared_ptr<Channel>>::iterator; + + /* + * Iterates over the Channel map and performs the action given by |action| on + * each channel map item (const ChannelIterator::value_type). + * |channels_mutex_| is not held; it is the responsibility of the caller to + * ensure serialization between threads that modify or iterate over the + * Channel map. + */ + template <class A> + void ForEachChannelUnlocked(A action) const { + std::for_each(channels_.begin(), channels_.end(), action); + } + + /* + * Iterates over the Channel map and performs the action given by |action| on + * each channel map item (const ChannelIterator::value_type). + * |channels_mutex_| is held to serialize access to the map; care must be + * taken to avoid recursively acquiring the mutex, for example, by calling + * Service::{GetChannel,SetChannel,CloseChannel,PushChannel}() or + * Message::SetChannel() in the action. + */ + template <class A> + void ForEachChannel(A action) const { + std::lock_guard<std::mutex> autolock(channels_mutex_); + ForEachChannelUnlocked(action); + } + + /* + * Subclasses of Service may override this method to provide a text string + * describing the state of the service. This method is called by + * HandleSystemMessage in response to the standard + * DUMP_STATE message. The string returned to the dump state client is + * truncated to |max_length| and reflects the maximum size the client can + * handle. + */ + virtual std::string DumpState(size_t max_length); + + /* + * Receives a message on this Service instance's endpoint and dispatches it. + * If the endpoint is in blocking mode this call blocks until a message is + * received, a signal is delivered to this thread, or the service is canceled. + * If the endpoint is in non-blocking mode and a message is not pending this + * call returns immediately with ETIMEDOUT. + */ + Status<void> ReceiveAndDispatch(); + + private: + friend class Message; + + Status<void> HandleSystemMessage(Message& message); + + Service(const Service&); + void operator=(const Service&) = delete; + + const std::string name_; + std::unique_ptr<Endpoint> endpoint_; + + /* + * Maintains references to active channels. + */ + mutable std::mutex channels_mutex_; + std::unordered_map<int, std::shared_ptr<Channel>> channels_; +}; + +/* + * Utility base class for services. This template handles allocation and + * initialization checks, reducing boiler plate code. + */ +template <typename TYPE> +class ServiceBase : public Service { + public: + /* + * Static service allocation method that check for initialization errors. + * If errors are encountered these automatically clean up and return + * nullptr. + */ + template <typename... Args> + static inline std::shared_ptr<TYPE> Create(Args&&... args) { + std::shared_ptr<TYPE> service(new TYPE(std::forward<Args>(args)...)); + if (service->IsInitialized()) + return service; + else + return nullptr; + } + + protected: + /* + * Shorthand for subclasses to refer to this base, particularly + * to call the base class constructor. + */ + typedef ServiceBase<TYPE> BASE; + + ServiceBase(const std::string& name, std::unique_ptr<Endpoint> endpoint) + : Service(name, std::move(endpoint)) {} +}; + +#ifndef STRINGIFY +#define STRINGIFY2(s) #s +#define STRINGIFY(s) STRINGIFY2(s) +#endif + +#define PDX_ERROR_PREFIX "[" __FILE__ ":" STRINGIFY(__LINE__) "]" + +/* + * Macros for replying to messages. Error handling can be tedious; + * these macros make things a little cleaner. + */ +#define CHECK_ERROR(cond, error, fmt, ...) \ + do { \ + if ((cond)) { \ + ALOGE(fmt, ##__VA_ARGS__); \ + goto error; \ + } \ + } while (0) + +#define REPLY_ERROR(message, error, error_label) \ + do { \ + auto __status = message.ReplyError(error); \ + CHECK_ERROR(!__status, error_label, \ + PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \ + __status.GetErrorMessage().c_str()); \ + goto error_label; \ + } while (0) + +#define REPLY_ERROR_RETURN(message, error, ...) \ + do { \ + auto __status = message.ReplyError(error); \ + ALOGE_IF(!__status, \ + PDX_ERROR_PREFIX " Failed to reply to message because: %s", \ + __status.GetErrorMessage().c_str()); \ + return __VA_ARGS__; \ + } while (0) + +#define REPLY_MESSAGE(message, message_return_code, error_label) \ + do { \ + auto __status = message.Reply(message_return_code); \ + CHECK_ERROR(!__status, error_label, \ + PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \ + __status.GetErrorMessage().c_str()); \ + goto error_label; \ + } while (0) + +#define REPLY_SUCCESS(message, message_return_code, error_label) \ + REPLY_MESSAGE(message, message_return_code, error_label) + +#define REPLY_MESSAGE_RETURN(message, message_return_code, ...) \ + do { \ + auto __status = message.Reply(message_return_code); \ + ALOGE_IF(!__status, \ + PDX_ERROR_PREFIX " Failed to reply to message because: %s", \ + __status.GetErrorMessage().c_str()); \ + return __VA_ARGS__; \ + } while (0) + +#define REPLY_SUCCESS_RETURN(message, message_return_code, ...) \ + REPLY_MESSAGE_RETURN(message, message_return_code, __VA_ARGS__) + +#define REPLY_FD(message, push_fd, error_label) \ + do { \ + auto __status = message.ReplyFileDescriptor(push_fd); \ + CHECK_ERROR(!__status, error_label, \ + PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \ + __status.GetErrorMessage().c_str()); \ + goto error_label; \ + } while (0) + +#define REPLY_FD_RETURN(message, push_fd, ...) \ + do { \ + auto __status = message.ReplyFileDescriptor(push_fd); \ + ALOGE_IF(__status < 0, \ + PDX_ERROR_PREFIX " Failed to reply to message because: %s", \ + __status.GetErrorMessage().c_str()); \ + return __VA_ARGS__; \ + } while (0) + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_SERVICE_H_ diff --git a/libs/vr/libpdx/private/pdx/service_dispatcher.h b/libs/vr/libpdx/private/pdx/service_dispatcher.h new file mode 100644 index 0000000000..c5e342af0c --- /dev/null +++ b/libs/vr/libpdx/private/pdx/service_dispatcher.h @@ -0,0 +1,79 @@ +#ifndef ANDROID_PDX_SERVICE_DISPATCHER_H_ +#define ANDROID_PDX_SERVICE_DISPATCHER_H_ + +#include <memory> + +namespace android { +namespace pdx { + +class Service; + +/* + * ServiceDispatcher manages a list of Service instances and handles message + * reception and dispatch to the services. This makes repetitive dispatch tasks + * easier to implement. + */ +class ServiceDispatcher { + public: + virtual ~ServiceDispatcher() = default; + + /* + * Adds a service to the list of services handled by this dispatcher. This + * will fail if any threads are blocked waiting for messages in this + * dispatcher. + * + * Returns 0 on success; -EEXIST if the service was already added. + */ + virtual int AddService(const std::shared_ptr<Service>& service) = 0; + + /* + * Removes a service from this dispatcher. This will fail if any threads are + * blocked waiting for messages in this dispatcher. + * + * Returns 0 on success; -ENOENT if the service was not previously added; + * -EBUSY if there are threads in the dispatcher. + */ + virtual int RemoveService(const std::shared_ptr<Service>& service) = 0; + + /* + * Receive and dispatch one set of messages. Multiple threads may enter this + * method to create an implicit thread pool, as described for + * enterDispatchLoop() below, however this method exits after one dispatch + * cycle, requiring an external loop. This is useful when other work needs + * to be done in the service dispatch loop. + */ + virtual int ReceiveAndDispatch() = 0; + + /* + * Same as above with timeout in milliseconds. A negative value means + * infinite timeout, while a value of 0 means return immediately if no + * messages are available to receive. + */ + virtual int ReceiveAndDispatch(int timeout) = 0; + + /* + * Receive and dispatch messages until canceled. When more than one thread + * enters this method it creates an implicit thread pool to dispatch messages. + * Explicit thread pools may be created by using a single dispatch thread that + * hands Message instances (via move assignment) over to a queue of threads + * (or perhaps one of several) to handle. + */ + virtual int EnterDispatchLoop() = 0; + + /* + * Sets the canceled state of the dispatcher. When canceled is true, any + * threads blocked waiting for messages will return. This method waits until + * all dispatch threads have exited the dispatcher. + */ + virtual void SetCanceled(bool cancel) = 0; + + /* + * Gets the canceled state of the dispatcher. + */ + virtual bool IsCanceled() const = 0; +}; + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_SERVICE_DISPATCHER_H_ diff --git a/libs/vr/libpdx/private/pdx/service_endpoint.h b/libs/vr/libpdx/private/pdx/service_endpoint.h new file mode 100644 index 0000000000..28bd6bc454 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/service_endpoint.h @@ -0,0 +1,144 @@ +#ifndef ANDROID_PDX_ENDPOINT_H_ +#define ANDROID_PDX_ENDPOINT_H_ + +#include <pdx/channel_handle.h> +#include <pdx/file_handle.h> +#include <pdx/status.h> + +struct iovec; + +namespace android { +namespace pdx { + +class Service; +class Channel; +class Message; + +struct MessageInfo { + int pid{0}; + int tid{0}; + int cid{0}; + int mid{0}; + int euid{0}; + int egid{0}; + int32_t op{0}; + uint32_t flags{0}; + Service* service{nullptr}; + Channel* channel{nullptr}; + size_t send_len{0}; + size_t recv_len{0}; + size_t fd_count{0}; + uint64_t impulse[4] = {}; +}; + +// Wrapper around transport endpoint. Abstracts the underlying transport APIs in +// a way, that the underlying IPC can be substituted for another technology +// without changing the Service, Client and Message classes of this library. +class Endpoint { + public: + virtual ~Endpoint() = default; + + // Returns a tag that uniquely identifies a specific underlying IPC transport. + virtual uint32_t GetIpcTag() const = 0; + + // Associates a Service instance with an endpoint by setting the service + // context pointer to the address of the Service. Only one Service may be + // associated with a given endpoint. + virtual Status<void> SetService(Service* service) = 0; + + // Set the channel context for the given channel. + virtual Status<void> SetChannel(int channel_id, Channel* channel) = 0; + + // Close a channel, signaling the client file object and freeing the channel + // id. Once closed, the client side of the channel always returns the error + // ESHUTDOWN and signals the poll/epoll events POLLHUP and POLLFREE. + virtual Status<void> CloseChannel(int channel_id) = 0; + + // Update the event bits for the given channel (given by id), using the + // given clear and set masks. + virtual Status<void> ModifyChannelEvents(int channel_id, int clear_mask, + int set_mask) = 0; + + // Create a new channel and push it as a file descriptor to the process + // sending the |message|. |flags| may be set to O_NONBLOCK and/or + // O_CLOEXEC to control the initial behavior of the new file descriptor (the + // sending process may change these later using fcntl()). The internal Channel + // instance associated with this channel is set to |channel|, which may be + // nullptr. The new channel id allocated for this channel is returned in + // |channel_id|, which may also be nullptr if not needed. + virtual Status<RemoteChannelHandle> PushChannel(Message* message, int flags, + Channel* channel, + int* channel_id) = 0; + + // Check whether the |ref| is a reference to a channel to the service + // represented by the |endpoint|. If the channel reference in question is + // valid, the Channel object is returned in |channel| when non-nullptr and + // the channel ID is returned through the Status object. + virtual Status<int> CheckChannel(const Message* message, ChannelReference ref, + Channel** channel) = 0; + + // Receives a message on the given endpoint file descriptor. + virtual Status<void> MessageReceive(Message* message) = 0; + + // Replies to the message with a return code. + virtual Status<void> MessageReply(Message* message, int return_code) = 0; + + // Replies to the message with a file descriptor. + virtual Status<void> MessageReplyFd(Message* message, + unsigned int push_fd) = 0; + + // Replies to the message with a local channel handle. + virtual Status<void> MessageReplyChannelHandle( + Message* message, const LocalChannelHandle& handle) = 0; + + // Replies to the message with a borrowed local channel handle. + virtual Status<void> MessageReplyChannelHandle( + Message* message, const BorrowedChannelHandle& handle) = 0; + + // Replies to the message with a remote channel handle. + virtual Status<void> MessageReplyChannelHandle( + Message* message, const RemoteChannelHandle& handle) = 0; + + // Reads message data into an array of memory buffers. + virtual Status<size_t> ReadMessageData(Message* message, const iovec* vector, + size_t vector_length) = 0; + + // Sends reply data for message. + virtual Status<size_t> WriteMessageData(Message* message, const iovec* vector, + size_t vector_length) = 0; + + // Records a file descriptor into the message buffer and returns the remapped + // reference to be sent to the remote process. + virtual Status<FileReference> PushFileHandle(Message* message, + const LocalHandle& handle) = 0; + virtual Status<FileReference> PushFileHandle( + Message* message, const BorrowedHandle& handle) = 0; + virtual Status<FileReference> PushFileHandle(Message* message, + const RemoteHandle& handle) = 0; + virtual Status<ChannelReference> PushChannelHandle( + Message* message, const LocalChannelHandle& handle) = 0; + virtual Status<ChannelReference> PushChannelHandle( + Message* message, const BorrowedChannelHandle& handle) = 0; + virtual Status<ChannelReference> PushChannelHandle( + Message* message, const RemoteChannelHandle& handle) = 0; + + // Obtains a file descriptor/channel handle from a message for the given + // reference. + virtual LocalHandle GetFileHandle(Message* message, + FileReference ref) const = 0; + virtual LocalChannelHandle GetChannelHandle(Message* message, + ChannelReference ref) const = 0; + + // Transport-specific message state management. + virtual void* AllocateMessageState() = 0; + virtual void FreeMessageState(void* state) = 0; + + // Cancels the endpoint, unblocking any receiver threads waiting for a + // message. + virtual Status<void> Cancel() = 0; +}; + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_ENDPOINT_H_ diff --git a/libs/vr/libpdx/private/pdx/status.h b/libs/vr/libpdx/private/pdx/status.h new file mode 100644 index 0000000000..067fe25e9c --- /dev/null +++ b/libs/vr/libpdx/private/pdx/status.h @@ -0,0 +1,180 @@ +#ifndef ANDROID_PDX_STATUS_H_ +#define ANDROID_PDX_STATUS_H_ + +#include <algorithm> +#include <memory> +#include <string> + +namespace android { +namespace pdx { + +// This is a helper class for constructing Status<T> with an error code. +struct ErrorStatus { + public: + ErrorStatus(int error) : error_{error} {} + int error() const { return error_; } + + static std::string ErrorToString(int error_code); + + private: + int error_; +}; + +// Status<T> is a container class that can be used to return a value of type T +// or error code to the caller. +template <typename T> +class Status { + public: + // Default constructor so an empty Status object can be created. + Status() : error_{-1} {} + + // Value copy/move constructors. These are intentionally not marked as + // explicit to allow direct value returns from functions without having + // to explicitly wrap them into Status<T>(). + Status(const T& value) : value_{value} {} // NOLINT(runtime/explicit) + Status(T&& value) : value_{std::move(value)} {} // NOLINT(runtime/explicit) + + // Constructor for storing an error code inside the Status object. + Status(const ErrorStatus& error_status) // NOLINT(runtime/explicit) + : error_{error_status.error()} {} + + // Copy/move constructors. Move constructor leaves |other| object in empty + // state. + Status(const Status& other) = default; + Status(Status&& other) + : value_{std::move(other.value_)}, error_{other.error_} { + other.error_ = -1; + } + + // Assignment operators. + Status& operator=(const Status& other) = default; + Status& operator=(Status&& other) { + error_ = other.error_; + value_ = std::move(other.value_); + other.error_ = -1; + T empty; + std::swap(other.value_, empty); + return *this; + } + + // Change the value/error code of the status object directly. + void SetValue(T value) { + error_ = 0; + value_ = std::move(value); + } + void SetError(int error) { + error_ = error; + T empty; + std::swap(value_, empty); + } + + // If |other| is in error state, copy the error code to this object. + // Returns true if error was propagated + template<typename U> + bool PropagateError(const Status<U>& other) { + if (!other.ok() && !other.empty()) { + SetError(other.error()); + return true; + } + return false; + } + + // Returns true if the status object contains valid value for type T. + // This means, the object is not empty and does not contain an error code. + bool ok() const { return error_ == 0; } + + // Checks if the object is empty (doesn't contain a valid value nor an error). + bool empty() const { return error_ < 0; } + + // Explicit bool conversion, equivalent to invoking ok(). + explicit operator bool() const { return ok(); } + + // Accessors for the value stored in Status. Calling when ok() is false leads + // to undefined behavior. + const T& get() const { return value_; } + T&& take() { + error_ = -1; + return std::move(value_); + } + + // Returns the error code stored in the object. These codes are positive + // non-zero values. + // Can be called only when an error is actually stored (that is, the object + // is not empty nor containing a valid value). + int error() const { return std::max(error_, 0); } + + // Returns the error code as ErrorStatus object. This is a helper method + // to aid in propagation of error codes between Status<T> of different types + // as in the following example: + // Status<int> foo() { + // Status<void> status = bar(); + // if(!status) + // return status.error_status(); + // return 12; + // } + inline ErrorStatus error_status() const { return ErrorStatus{error()}; } + + // Returns the error message associated with error code stored in the object. + // The message is the same as the string returned by strerror(status.error()). + // Can be called only when an error is actually stored (that is, the object + // is not empty nor containing a valid value). + std::string GetErrorMessage() const { + std::string message; + if (error_ > 0) + message = ErrorStatus::ErrorToString(error_); + return message; + } + + private: + T value_{}; + int error_{0}; +}; + +// Specialization for status containing no other value but the error code. +template <> +class Status<void> { + public: + Status() = default; + Status(const ErrorStatus& error_status) // NOLINT(runtime/explicit) + : error_{error_status.error()} {} + void SetValue() { error_ = 0; } + void SetError(int error) { error_ = error; } + + template<typename U> + bool PropagateError(const Status<U>& other) { + if (!other.ok() && !other.empty()) { + SetError(other.error()); + return true; + } + return false; + } + + bool ok() const { return error_ == 0; } + bool empty() const { return false; } + explicit operator bool() const { return ok(); } + int error() const { return std::max(error_, 0); } + inline ErrorStatus error_status() const { return ErrorStatus{error()}; } + std::string GetErrorMessage() const { + std::string message; + if (error_ > 0) + message = ErrorStatus::ErrorToString(error_); + return message; + } + + private: + int error_{0}; +}; + +// TODO(avakulenko): Remove these function once all callers of it are gone. +inline int ReturnStatusOrError(const Status<void>& status) { + return status ? 0 : -status.error(); +} + +inline int ReturnStatusOrError(const Status<int>& status) { + return status ? status.get() : -status.error(); +} + +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_STATUS_H_ diff --git a/libs/vr/libpdx/private/pdx/trace.h b/libs/vr/libpdx/private/pdx/trace.h new file mode 100644 index 0000000000..ebe8491ebc --- /dev/null +++ b/libs/vr/libpdx/private/pdx/trace.h @@ -0,0 +1,35 @@ +#ifndef ANDROID_PDX_TRACE_H_ +#define ANDROID_PDX_TRACE_H_ + +// Tracing utilities for libpdx. Tracing in the service framework is enabled +// under these conditions: +// 1. ATRACE_TAG is defined, AND +// 2. ATRACE_TAG does not equal ATRACE_TAG_NEVER, AND +// 3. PDX_TRACE_ENABLED is defined, AND +// 4. PDX_TRACE_ENABLED is equal to logical true. +// +// If any of these conditions are not met tracing is completely removed from the +// library and headers. + +// If ATRACE_TAG is not defined, default to never. +#ifndef ATRACE_TAG +#define ATRACE_TAG ATRACE_TAG_NEVER +#endif + +// Include tracing functions after the trace tag is defined. +#include <utils/Trace.h> + +// If PDX_TRACE_ENABLED is not defined, default to off. +#ifndef PDX_TRACE_ENABLED +#define PDX_TRACE_ENABLED 0 +#endif + +#if (ATRACE_TAG) != (ATRACE_TAG_NEVER) && (PDX_TRACE_ENABLED) +#define PDX_TRACE_NAME ATRACE_NAME +#else +#define PDX_TRACE_NAME(name) \ + do { \ + } while (0) +#endif + +#endif // ANDROID_PDX_TRACE_H_ diff --git a/libs/vr/libpdx/private/pdx/utility.h b/libs/vr/libpdx/private/pdx/utility.h new file mode 100644 index 0000000000..305c3b87a4 --- /dev/null +++ b/libs/vr/libpdx/private/pdx/utility.h @@ -0,0 +1,367 @@ +#ifndef ANDROID_PDX_UTILITY_H_ +#define ANDROID_PDX_UTILITY_H_ + +#include <cstdint> +#include <iterator> + +#include <pdx/rpc/sequence.h> + +// Utilities for testing object serialization. + +namespace android { +namespace pdx { + +class ByteBuffer { + public: + using iterator = uint8_t*; + using const_iterator = const uint8_t*; + using size_type = size_t; + + ByteBuffer() = default; + ByteBuffer(const ByteBuffer& other) { + resize(other.size()); + if (other.size()) + memcpy(data_, other.data(), other.size()); + } + + ByteBuffer& operator=(const ByteBuffer& other) { + resize(other.size()); + if (other.size()) + memcpy(data_, other.data(), other.size()); + return *this; + } + + ByteBuffer& operator=(ByteBuffer&& other) { + std::swap(data_, other.data_); + std::swap(size_, other.size_); + std::swap(capacity_, other.capacity_); + other.clear(); + return *this; + } + + inline const uint8_t* data() const { return data_; } + inline uint8_t* data() { return data_; } + inline size_t size() const { return size_; } + inline size_t capacity() const { return capacity_; } + + iterator begin() { return data_; } + const_iterator begin() const { return data_; } + iterator end() { return data_ + size_; } + const_iterator end() const { return data_ + size_; } + + inline bool operator==(const ByteBuffer& other) const { + return size_ == other.size_ && + (size_ == 0 || memcmp(data_, other.data_, size_) == 0); + } + + inline bool operator!=(const ByteBuffer& other) const { + return !operator==(other); + } + + inline void reserve(size_t size) { + if (size <= capacity_) + return; + // Find next power of 2 (assuming the size is 32 bits for now). + size--; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + size++; + void* new_data = data_ ? realloc(data_, size) : malloc(size); + // TODO(avakulenko): Check for allocation failures. + data_ = static_cast<uint8_t*>(new_data); + capacity_ = size; + } + + inline void resize(size_t size) { + reserve(size); + size_ = size; + } + + inline uint8_t* grow_by(size_t size_delta) { + size_t old_size = size_; + resize(old_size + size_delta); + return data_ + old_size; + } + + inline void clear() { size_ = 0; } + + private: + uint8_t* data_{nullptr}; + size_t size_{0}; + size_t capacity_{0}; +}; + +// Utility functions to increment/decrement void pointers to data buffers. +template <typename OFFSET_T> +inline const void* AdvancePointer(const void* ptr, OFFSET_T offset) { + return static_cast<const uint8_t*>(ptr) + offset; +} + +template <typename OFFSET_T> +inline void* AdvancePointer(void* ptr, OFFSET_T offset) { + return static_cast<uint8_t*>(ptr) + offset; +} + +inline ptrdiff_t PointerDistance(const void* end, const void* begin) { + return static_cast<const uint8_t*>(end) - static_cast<const uint8_t*>(begin); +} + +// Utility to build sequences of types. +template <typename, typename> +struct AppendTypeSequence; + +template <typename T, typename... S, template <typename...> class TT> +struct AppendTypeSequence<T, TT<S...>> { + using type = TT<S..., T>; +}; + +// Utility to generate repeated types. +template <typename T, std::size_t N, template <typename...> class TT> +struct RepeatedType { + using type = typename AppendTypeSequence< + T, typename RepeatedType<T, N - 1, TT>::type>::type; +}; + +template <typename T, template <typename...> class TT> +struct RepeatedType<T, 0, TT> { + using type = TT<>; +}; + +template <typename V, typename S> +inline V ReturnValueHelper(V value, S /*ignore*/) { + return value; +} + +template <typename R, typename V, size_t... S> +inline R GetNTupleHelper(V value, rpc::IndexSequence<S...>) { + return std::make_tuple(ReturnValueHelper(value, S)...); +} + +// Returns an N-tuple of type std::tuple<T,...T> containing |value| in each +// element. +template <size_t N, typename T, + typename R = typename RepeatedType<T, N, std::tuple>::type> +inline R GetNTuple(T value) { + return GetNTupleHelper<R>(value, rpc::MakeIndexSequence<N>{}); +} + +class NoOpOutputResourceMapper : public OutputResourceMapper { + public: + Status<FileReference> PushFileHandle(const LocalHandle& handle) override { + return handle.Get(); + } + + Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override { + return handle.Get(); + } + + Status<FileReference> PushFileHandle(const RemoteHandle& handle) override { + return handle.Get(); + } + + Status<ChannelReference> PushChannelHandle( + const LocalChannelHandle& handle) override { + return handle.value(); + } + + Status<ChannelReference> PushChannelHandle( + const BorrowedChannelHandle& handle) override { + return handle.value(); + } + + Status<ChannelReference> PushChannelHandle( + const RemoteChannelHandle& handle) override { + return handle.value(); + } +}; + +class NoOpInputResourceMapper : public InputResourceMapper { + public: + bool GetFileHandle(FileReference ref, LocalHandle* handle) override { + *handle = LocalHandle{ref}; + return true; + } + + bool GetChannelHandle(ChannelReference ref, + LocalChannelHandle* handle) override { + *handle = LocalChannelHandle{nullptr, ref}; + return true; + } +}; + +class NoOpResourceMapper : public NoOpOutputResourceMapper, + public NoOpInputResourceMapper {}; + +// Simple implementation of the payload interface, required by +// Serialize/Deserialize. This is intended for test cases, where compatibility +// with std::vector is helpful. +class Payload : public MessageWriter, + public MessageReader, + public OutputResourceMapper { + public: + using BaseType = ByteBuffer; + using iterator = typename BaseType::iterator; + using const_iterator = typename BaseType::const_iterator; + using size_type = typename BaseType::size_type; + + Payload() = default; + explicit Payload(size_type count, uint8_t value = 0) { Append(count, value); } + Payload(const Payload& other) : buffer_(other.buffer_) {} + Payload(const std::initializer_list<uint8_t>& initializer) { + buffer_.resize(initializer.size()); + std::copy(initializer.begin(), initializer.end(), buffer_.begin()); + } + + Payload& operator=(const Payload& other) { + buffer_ = other.buffer_; + read_pos_ = 0; + return *this; + } + Payload& operator=(const std::initializer_list<uint8_t>& initializer) { + buffer_.resize(initializer.size()); + std::copy(initializer.begin(), initializer.end(), buffer_.begin()); + read_pos_ = 0; + return *this; + } + + // Compares Payload with Payload. + bool operator==(const Payload& other) const { + return buffer_ == other.buffer_; + } + bool operator!=(const Payload& other) const { + return buffer_ != other.buffer_; + } + + // Compares Payload with std::vector. + template <typename Type, typename AllocatorType> + typename std::enable_if<sizeof(Type) == sizeof(uint8_t), bool>::type + operator==(const std::vector<Type, AllocatorType>& other) const { + return buffer_.size() == other.size() && + memcmp(buffer_.data(), other.data(), other.size()) == 0; + } + template <typename Type, typename AllocatorType> + typename std::enable_if<sizeof(Type) == sizeof(uint8_t), bool>::type + operator!=(const std::vector<Type, AllocatorType>& other) const { + return !operator!=(other); + } + + iterator begin() { return buffer_.begin(); } + const_iterator begin() const { return buffer_.begin(); } + iterator end() { return buffer_.end(); } + const_iterator end() const { return buffer_.end(); } + + void Append(size_type count, uint8_t value) { + auto* data = buffer_.grow_by(count); + std::fill(data, data + count, value); + } + + void Clear() { + buffer_.clear(); + file_handles_.clear(); + read_pos_ = 0; + } + + void Rewind() { read_pos_ = 0; } + + uint8_t* Data() { return buffer_.data(); } + const uint8_t* Data() const { return buffer_.data(); } + size_type Size() const { return buffer_.size(); } + + // MessageWriter + void* GetNextWriteBufferSection(size_t size) override { + return buffer_.grow_by(size); + } + + OutputResourceMapper* GetOutputResourceMapper() override { return this; } + + // OutputResourceMapper + Status<FileReference> PushFileHandle(const LocalHandle& handle) override { + if (handle) { + const int ref = file_handles_.size(); + file_handles_.push_back(handle.Get()); + return ref; + } else { + return handle.Get(); + } + } + + Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override { + if (handle) { + const int ref = file_handles_.size(); + file_handles_.push_back(handle.Get()); + return ref; + } else { + return handle.Get(); + } + } + + Status<FileReference> PushFileHandle(const RemoteHandle& handle) override { + return handle.Get(); + } + + Status<ChannelReference> PushChannelHandle( + const LocalChannelHandle& handle) override { + if (handle) { + const int ref = file_handles_.size(); + file_handles_.push_back(handle.value()); + return ref; + } else { + return handle.value(); + } + } + + Status<ChannelReference> PushChannelHandle( + const BorrowedChannelHandle& handle) override { + if (handle) { + const int ref = file_handles_.size(); + file_handles_.push_back(handle.value()); + return ref; + } else { + return handle.value(); + } + } + + Status<ChannelReference> PushChannelHandle( + const RemoteChannelHandle& handle) override { + return handle.value(); + } + + // MessageReader + BufferSection GetNextReadBufferSection() override { + return {buffer_.data() + read_pos_, &*buffer_.end()}; + } + + void ConsumeReadBufferSectionData(const void* new_start) override { + read_pos_ = PointerDistance(new_start, buffer_.data()); + } + + InputResourceMapper* GetInputResourceMapper() override { + return &input_resource_mapper_; + } + + const int* FdArray() const { return file_handles_.data(); } + std::size_t FdCount() const { return file_handles_.size(); } + + private: + NoOpInputResourceMapper input_resource_mapper_; + ByteBuffer buffer_; + std::vector<int> file_handles_; + size_t read_pos_{0}; +}; + +} // namespace pdx +} // namespace android + +// Helper macros for branch prediction hinting. +#ifdef __GNUC__ +#define PDX_LIKELY(x) __builtin_expect(!!(x), true) +#define PDX_UNLIKELY(x) __builtin_expect(!!(x), false) +#else +#define PDX_LIKELY(x) (x) +#define PDX_UNLIKELY(x) (x) +#endif + +#endif // ANDROID_PDX_UTILITY_H_ diff --git a/libs/vr/libpdx/serialization_tests.cpp b/libs/vr/libpdx/serialization_tests.cpp new file mode 100644 index 0000000000..5ad104764d --- /dev/null +++ b/libs/vr/libpdx/serialization_tests.cpp @@ -0,0 +1,2505 @@ +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include <memory> +#include <string> +#include <thread> +#include <utility> + +#include <gtest/gtest.h> +#include <pdx/rpc/argument_encoder.h> +#include <pdx/rpc/array_wrapper.h> +#include <pdx/rpc/default_initialization_allocator.h> +#include <pdx/rpc/payload.h> +#include <pdx/rpc/serializable.h> +#include <pdx/rpc/serialization.h> +#include <pdx/rpc/string_wrapper.h> +#include <pdx/utility.h> + +using namespace android::pdx; +using namespace android::pdx::rpc; + +// Tests the serialization/deserialization of all supported types, verifying all +// reasonable boundary conditions for types with multiple encodings. +// +// NOTE: Sometimes this file uses the construct "var = decltype(var)({...})" +// instead of the equivalent "var = {...}" to construct vectors. This is to +// prevent clang-format from producing annoyingly vertical code from long +// initializers. + +// TODO(eieio): Automatically generate some of these tests? + +namespace { + +// Test data for serialization/deserialization of floats. +const float kZeroFloat = 0.0f; +const float kOneFloat = 1.0f; +const auto kZeroFloatBytes = reinterpret_cast<const std::uint8_t*>(&kZeroFloat); +const auto kOneFloatBytes = reinterpret_cast<const std::uint8_t*>(&kOneFloat); +const double kZeroDouble = 0.0; +const double kOneDouble = 1.0; +const auto kZeroDoubleBytes = + reinterpret_cast<const std::uint8_t*>(&kZeroDouble); +const auto kOneDoubleBytes = reinterpret_cast<const std::uint8_t*>(&kOneDouble); + +struct TestType { + enum class Foo { kFoo, kBar, kBaz }; + + int a; + float b; + std::string c; + Foo d; + + TestType() {} + TestType(int a, float b, const std::string& c, Foo d) + : a(a), b(b), c(c), d(d) {} + + // Make gtest expressions simpler by defining equality operator. This is not + // needed for serialization. + bool operator==(const TestType& other) const { + return a == other.a && b == other.b && c == other.c && d == other.d; + } + + private: + PDX_SERIALIZABLE_MEMBERS(TestType, a, b, c, d); +}; + +template <typename FileHandleType> +struct TestTemplateType { + FileHandleType fd; + + TestTemplateType() {} + TestTemplateType(FileHandleType fd) : fd(std::move(fd)) {} + + bool operator==(const TestTemplateType& other) const { + return fd.Get() == other.fd.Get(); + } + + private: + PDX_SERIALIZABLE_MEMBERS(TestTemplateType<FileHandleType>, fd); +}; + +// Utilities to generate test maps and payloads. +template <typename MapType> +MapType MakeMap(std::size_t size) { + MapType result; + for (std::size_t i = 0; i < size; i++) { + result.emplace(i, i); + } + return result; +} + +template <typename MapType> +void InsertKeyValue(MessageWriter* writer, std::size_t size) { + MapType map; + for (std::size_t i = 0; i < size; i++) { + map.emplace(i, i); + } + for (const auto& element : map) { + Serialize(element.first, writer); + Serialize(element.second, writer); + } +} + +} // anonymous namespace + +TEST(SerializableTypes, Constructor) { + TestType tt(1, 2.0, "three", TestType::Foo::kBar); + EXPECT_EQ(1, tt.a); + EXPECT_EQ(2.0, tt.b); + EXPECT_EQ("three", tt.c); + EXPECT_EQ(TestType::Foo::kBar, tt.d); +} + +TEST(SerializationTest, bool) { + Payload result; + Payload expected; + bool value; + + // True. + value = true; + Serialize(value, &result); + expected = {ENCODING_TYPE_TRUE}; + EXPECT_EQ(expected, result); + result.Clear(); + + // False. + value = false; + Serialize(value, &result); + expected = {ENCODING_TYPE_FALSE}; + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, uint8_t) { + Payload result; + Payload expected; + uint8_t value; + + // Min FIXINT. + value = 0; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXINT. + value = (1 << 7) - 1; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min UINT8. + value = (1 << 7); + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT8, (1 << 7)}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max UINT8. + value = 0xff; + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT8, 0xff}; + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, uint16_t) { + Payload result; + Payload expected; + uint16_t value; + + // Min FIXINT. + value = 0; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXINT. + value = (1 << 7) - 1; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min UINT8. + value = (1 << 7); + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT8, (1 << 7)}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max UINT8. + value = 0xff; + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT8, 0xff}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min UINT16. + value = (1 << 8); + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT16, 0, 1}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max UINT16. + value = 0xffff; + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT16, 0xff, 0xff}; + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, uint32_t) { + Payload result; + Payload expected; + uint32_t value; + + // Min FIXINT. + value = 0; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXINT. + value = (1 << 7) - 1; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min UINT8. + value = (1 << 7); + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT8, (1 << 7)}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max UINT8. + value = 0xff; + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT8, 0xff}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min UINT16. + value = (1 << 8); + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT16, 0, 1}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max UINT16. + value = 0xffff; + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT16, 0xff, 0xff}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min UINT32. + value = (1 << 16); + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT32, 0, 0, 1, 0}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max UINT32. + value = 0xffffffff; + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff}; + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, uint64_t) { + Payload result; + Payload expected; + uint64_t value; + + // Min FIXINT. + value = 0; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXINT. + value = (1 << 7) - 1; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min UINT8. + value = (1 << 7); + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT8, (1 << 7)}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max UINT8. + value = 0xff; + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT8, 0xff}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min UINT16. + value = (1 << 8); + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT16, 0, 1}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max UINT16. + value = 0xffff; + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT16, 0xff, 0xff}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min UINT32. + value = (1 << 16); + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT32, 0, 0, 1, 0}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max UINT32. + value = 0xffffffff; + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min UINT64. + value = (1ULL << 32); + Serialize(value, &result); + expected = {ENCODING_TYPE_UINT64, 0, 0, 0, 0, 1, 0, 0, 0}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max UINT64. + value = 0xffffffffffffffffULL; + Serialize(value, &result); + expected = { + ENCODING_TYPE_UINT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, int8_t) { + Payload result; + Payload expected; + int8_t value; + + // Min NEGATIVE FIXINT. + value = -32; + Serialize(value, &result); + expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max NEGATIVE FIXINT. + value = -1; + Serialize(value, &result); + expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min FIXINT. + value = 0; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXINT. + value = 127; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min INT8. + value = -128; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT8, 0x80}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max INT8. + value = -33; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT8, 0xdf}; + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, int16_t) { + Payload result; + Payload expected; + int16_t value; + + // Min NEGATIVE FIXINT. + value = -32; + Serialize(value, &result); + expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max NEGATIVE FIXINT. + value = -1; + Serialize(value, &result); + expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min FIXINT. + value = 0; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXINT. + value = 127; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min INT8. + value = -128; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT8, 0x80}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max INT8. + value = -33; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT8, 0xdf}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min INT16. + value = -32768; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT16, 0x00, 0x80}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max INT16. + value = 32767; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT16, 0xff, 0x7f}; + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, int32_t) { + Payload result; + Payload expected; + int32_t value; + + // Min NEGATIVE FIXINT. + value = -32; + Serialize(value, &result); + expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max NEGATIVE FIXINT. + value = -1; + Serialize(value, &result); + expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min FIXINT. + value = 0; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXINT. + value = 127; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min INT8. + value = -128; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT8, 0x80}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max INT8. + value = -33; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT8, 0xdf}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min INT16. + value = -32768; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT16, 0x00, 0x80}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max INT16. + value = 32767; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT16, 0xff, 0x7f}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min INT32. + value = -2147483648; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max INT32. + value = 2147483647; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f}; + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, int64_t) { + Payload result; + Payload expected; + int64_t value; + + // Min NEGATIVE FIXINT. + value = -32; + Serialize(value, &result); + expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max NEGATIVE FIXINT. + value = -1; + Serialize(value, &result); + expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min FIXINT. + value = 0; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXINT. + value = 127; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min INT8. + value = -128; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT8, 0x80}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max INT8. + value = -33; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT8, 0xdf}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min INT16. + value = -32768; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT16, 0x00, 0x80}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max INT16. + value = 32767; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT16, 0xff, 0x7f}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min INT32. + value = -2147483648; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max INT32. + value = 2147483647; + Serialize(value, &result); + expected = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Min INT64. + value = -9223372036854775808ULL; + Serialize(value, &result); + expected = { + ENCODING_TYPE_INT64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max INT64. + value = 9223372036854775807ULL; + Serialize(value, &result); + expected = { + ENCODING_TYPE_INT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}; + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, float) { + Payload result; + Payload expected; + float value; + + value = 0.0f; + Serialize(value, &result); + expected = {ENCODING_TYPE_FLOAT32, kZeroFloatBytes[0], kZeroFloatBytes[1], + kZeroFloatBytes[2], kZeroFloatBytes[3]}; + EXPECT_EQ(expected, result); + result.Clear(); + + value = 1.0f; + Serialize(value, &result); + expected = {ENCODING_TYPE_FLOAT32, kOneFloatBytes[0], kOneFloatBytes[1], + kOneFloatBytes[2], kOneFloatBytes[3]}; + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, double) { + Payload result; + Payload expected; + double value; + + value = 0.0f; + Serialize(value, &result); + expected = {ENCODING_TYPE_FLOAT64, kZeroDoubleBytes[0], kZeroDoubleBytes[1], + kZeroDoubleBytes[2], kZeroDoubleBytes[3], kZeroDoubleBytes[4], + kZeroDoubleBytes[5], kZeroDoubleBytes[6], kZeroDoubleBytes[7]}; + EXPECT_EQ(expected, result); + result.Clear(); + + value = 1.0f; + Serialize(value, &result); + expected = {ENCODING_TYPE_FLOAT64, kOneDoubleBytes[0], kOneDoubleBytes[1], + kOneDoubleBytes[2], kOneDoubleBytes[3], kOneDoubleBytes[4], + kOneDoubleBytes[5], kOneDoubleBytes[6], kOneDoubleBytes[7]}; + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, Enum) { + Payload result; + Payload expected; + + enum Foo { kFoo, kBar, kBaz }; + Foo value = kBar; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1}; + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, EnumClass) { + Payload result; + Payload expected; + + enum class Foo { kFoo, kBar, kBaz }; + Foo value = Foo::kBaz; + Serialize(value, &result); + expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2}; + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, LocalHandle) { + Payload result; + Payload expected; + LocalHandle fd1; + LocalHandle fd2; + + fd1 = LocalHandle(100); + Serialize(fd1, &result); + expected = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0}; + EXPECT_EQ(expected, result); + EXPECT_EQ(1u, result.FdCount()); + EXPECT_EQ(100, result.FdArray()[0]); + result.Clear(); + + fd2 = LocalHandle(200); + Serialize(std::forward_as_tuple(fd1, fd2), &result); + expected = decltype(expected)( + {ENCODING_TYPE_FIXARRAY_MIN + 2, ENCODING_TYPE_FIXEXT2, + ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0, ENCODING_TYPE_FIXEXT2, + ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 1, 0}); + EXPECT_EQ(expected, result); + EXPECT_EQ(2u, result.FdCount()); + EXPECT_EQ(100, result.FdArray()[0]); + EXPECT_EQ(200, result.FdArray()[1]); + result.Clear(); + + fd1.Release(); // Don't try to close fd 100. + fd2.Release(); // Don't try to close fd 200. + + fd1 = LocalHandle(-2); + Serialize(fd1, &result); + expected = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xfe, + 0xff}; + EXPECT_EQ(expected, result); + EXPECT_EQ(0u, result.FdCount()); + result.Clear(); +} + +TEST(SerializationTest, string) { + Payload result; + Payload expected; + std::string value; + + // Min FIXSTR. + value = ""; + Serialize(value, &result); + expected = {ENCODING_TYPE_FIXSTR_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXSTR. + value = std::string((1 << 5) - 1, 'x'); + Serialize(value, &result); + expected = {ENCODING_TYPE_FIXSTR_MAX}; + expected.Append((1 << 5) - 1, 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min STR8. + value = std::string((1 << 5), 'x'); + Serialize(value, &result); + expected = {ENCODING_TYPE_STR8, (1 << 5)}; + expected.Append((1 << 5), 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Max STR8. + value = std::string((1 << 8) - 1, 'x'); + Serialize(value, &result); + expected = {ENCODING_TYPE_STR8, (1 << 8) - 1}; + expected.Append((1 << 8) - 1, 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min STR16. + value = std::string((1 << 8), 'x'); + Serialize(value, &result); + expected = {ENCODING_TYPE_STR16, 0x00, 0x01}; + expected.Append((1 << 8), 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Max STR16. + value = std::string((1 << 16) - 1, 'x'); + Serialize(value, &result); + expected = {ENCODING_TYPE_STR16, 0xff, 0xff}; + expected.Append((1 << 16) - 1, 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min STR32. + value = std::string((1 << 16), 'x'); + Serialize(value, &result); + expected = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x01, 0x00}; + expected.Append((1 << 16), 'x'); + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, StringWrapper) { + Payload result; + Payload expected; + std::string value; + + // Min FIXSTR. + value = ""; + Serialize(WrapString(value), &result); + expected = {ENCODING_TYPE_FIXSTR_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXSTR. + value = std::string((1 << 5) - 1, 'x'); + Serialize(WrapString(value), &result); + expected = {ENCODING_TYPE_FIXSTR_MAX}; + expected.Append((1 << 5) - 1, 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min STR8. + value = std::string((1 << 5), 'x'); + Serialize(WrapString(value), &result); + expected = {ENCODING_TYPE_STR8, (1 << 5)}; + expected.Append((1 << 5), 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Max STR8. + value = std::string((1 << 8) - 1, 'x'); + Serialize(WrapString(value), &result); + expected = {ENCODING_TYPE_STR8, (1 << 8) - 1}; + expected.Append((1 << 8) - 1, 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min STR16. + value = std::string((1 << 8), 'x'); + Serialize(WrapString(value), &result); + expected = {ENCODING_TYPE_STR16, 0x00, 0x01}; + expected.Append((1 << 8), 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Max STR16. + value = std::string((1 << 16) - 1, 'x'); + Serialize(WrapString(value), &result); + expected = {ENCODING_TYPE_STR16, 0xff, 0xff}; + expected.Append((1 << 16) - 1, 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min STR32. + value = std::string((1 << 16), 'x'); + Serialize(WrapString(value), &result); + expected = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x01, 0x00}; + expected.Append((1 << 16), 'x'); + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, vector) { + Payload result; + Payload expected; + std::vector<uint8_t> value; + + // Min FIXARRAY. + value = {}; + Serialize(value, &result); + expected = {ENCODING_TYPE_FIXARRAY_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXARRAY. + value = decltype(value)((1 << 4) - 1, 'x'); + Serialize(value, &result); + expected = {ENCODING_TYPE_FIXARRAY_MAX}; + expected.Append((1 << 4) - 1, 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min ARRAY16. + value = decltype(value)((1 << 4), 'x'); + Serialize(value, &result); + expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00}; + expected.Append((1 << 4), 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Max ARRAY16. + value = decltype(value)((1 << 16) - 1, 'x'); + Serialize(value, &result); + expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff}; + expected.Append((1 << 16) - 1, 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min ARRAY32. + value = decltype(value)((1 << 16), 'x'); + Serialize(value, &result); + expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00}; + expected.Append((1 << 16), 'x'); + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, map) { + Payload result; + Payload expected; + std::map<std::uint32_t, std::uint32_t> value; + + // Min FIXMAP. + value = {}; + Serialize(value, &result); + expected = {ENCODING_TYPE_FIXMAP_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXMAP. + value = MakeMap<decltype(value)>((1 << 4) - 1); + Serialize(value, &result); + expected = {ENCODING_TYPE_FIXMAP_MAX}; + InsertKeyValue<decltype(value)>(&expected, (1 << 4) - 1); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min MAP16. + value = MakeMap<decltype(value)>((1 << 4)); + Serialize(value, &result); + expected = {ENCODING_TYPE_MAP16, 0x10, 0x00}; + InsertKeyValue<decltype(value)>(&expected, (1 << 4)); + EXPECT_EQ(expected, result); + result.Clear(); + + // Max MAP16. + value = MakeMap<decltype(value)>((1 << 16) - 1); + Serialize(value, &result); + expected = {ENCODING_TYPE_MAP16, 0xff, 0xff}; + InsertKeyValue<decltype(value)>(&expected, (1 << 16) - 1); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min MAP32. + value = MakeMap<decltype(value)>((1 << 16)); + Serialize(value, &result); + expected = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00}; + InsertKeyValue<decltype(value)>(&expected, (1 << 16)); + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, unordered_map) { + Payload result; + Payload expected; + std::unordered_map<std::uint32_t, std::uint32_t> value; + + // Min FIXMAP. + value = {}; + Serialize(value, &result); + expected = {ENCODING_TYPE_FIXMAP_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXMAP. + value = MakeMap<decltype(value)>((1 << 4) - 1); + Serialize(value, &result); + expected = {ENCODING_TYPE_FIXMAP_MAX}; + InsertKeyValue<decltype(value)>(&expected, (1 << 4) - 1); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min MAP16. + value = MakeMap<decltype(value)>((1 << 4)); + Serialize(value, &result); + expected = {ENCODING_TYPE_MAP16, 0x10, 0x00}; + InsertKeyValue<decltype(value)>(&expected, (1 << 4)); + EXPECT_EQ(expected, result); + result.Clear(); + + // Max MAP16. + value = MakeMap<decltype(value)>((1 << 16) - 1); + Serialize(value, &result); + expected = {ENCODING_TYPE_MAP16, 0xff, 0xff}; + InsertKeyValue<decltype(value)>(&expected, (1 << 16) - 1); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min MAP32. + value = MakeMap<decltype(value)>((1 << 16)); + Serialize(value, &result); + expected = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00}; + InsertKeyValue<decltype(value)>(&expected, (1 << 16)); + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, array) { + Payload result; + Payload expected; + + // Min FIXARRAY. + std::array<std::uint8_t, 0> a0; + Serialize(a0, &result); + expected = {ENCODING_TYPE_FIXARRAY_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXARRAY. + std::array<std::uint8_t, (1 << 4) - 1> a1; + for (auto& element : a1) + element = 'x'; + Serialize(a1, &result); + expected = {ENCODING_TYPE_FIXARRAY_MAX}; + expected.Append((1 << 4) - 1, 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min ARRAY16. + std::array<std::uint8_t, (1 << 4)> a2; + for (auto& element : a2) + element = 'x'; + Serialize(a2, &result); + expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00}; + expected.Append((1 << 4), 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Max ARRAY16. + std::array<std::uint8_t, (1 << 16) - 1> a3; + for (auto& element : a3) + element = 'x'; + Serialize(a3, &result); + expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff}; + expected.Append((1 << 16) - 1, 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min ARRAY32. + std::array<std::uint8_t, (1 << 16)> a4; + for (auto& element : a4) + element = 'x'; + Serialize(a4, &result); + expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00}; + expected.Append((1 << 16), 'x'); + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, ArrayWrapper) { + Payload result; + Payload expected; + std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>> value; + ArrayWrapper<std::uint8_t> wrapper; + + // Min FIXARRAY. + value = {}; + Serialize(wrapper, &result); + expected = {ENCODING_TYPE_FIXARRAY_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXARRAY. + value = decltype(value)((1 << 4) - 1, 'x'); + wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size()); + Serialize(wrapper, &result); + expected = {ENCODING_TYPE_FIXARRAY_MAX}; + expected.Append((1 << 4) - 1, 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min ARRAY16. + value = decltype(value)((1 << 4), 'x'); + wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size()); + Serialize(wrapper, &result); + expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00}; + expected.Append((1 << 4), 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Max ARRAY16. + value = decltype(value)((1 << 16) - 1, 'x'); + wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size()); + Serialize(wrapper, &result); + expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff}; + expected.Append((1 << 16) - 1, 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min ARRAY32. + value = decltype(value)((1 << 16), 'x'); + wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size()); + Serialize(wrapper, &result); + expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00}; + expected.Append((1 << 16), 'x'); + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, pair) { + Payload result; + Payload expected; + + auto p1 = std::make_pair(1, 2); + Serialize(p1, &result); + expected = {ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2}; + EXPECT_EQ(expected, result); + result.Clear(); + + auto p2 = std::make_pair('x', std::string("12345")); + Serialize(p2, &result); + expected = decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 2, 'x', + ENCODING_TYPE_FIXSTR_MIN + 5, '1', '2', '3', + '4', '5'}); + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, tuple) { + Payload result; + Payload expected; + + // Min FIXARRAY. + auto t1 = std::make_tuple(); + Serialize(t1, &result); + expected = {ENCODING_TYPE_FIXARRAY_MIN}; + EXPECT_EQ(expected, result); + result.Clear(); + + // Max FIXARRAY. + auto t2 = GetNTuple<15>('x'); + Serialize(t2, &result); + expected = {ENCODING_TYPE_FIXARRAY_MAX}; + expected.Append((1 << 4) - 1, 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min ARRAY16. + auto t3 = GetNTuple<(1 << 4)>('x'); + Serialize(t3, &result); + expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00}; + expected.Append((1 << 4), 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + +// Template instantiation depth is an issue for these tests. They are commented +// out to document the expected behavior, even though tuples of this order are +// not expected in practice. +#if 0 + // Max ARRAY16. + auto t4 = GetNTuple<(1 << 16)-1>('x'); + Serialize(t4, &result); + expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff}; + expected.Append((1 << 16)-1, 'x'); + EXPECT_EQ(expected, result); + result.Clear(); + + // Min ARRAY32. + auto t5 = GetNTuple<(1 << 16)>('x'); + Serialize(t5, &result); + expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00}; + expected.Append((1 << 16), 'x'); + EXPECT_EQ(expected, result); + result.Clear(); +#endif +} + +// TODO(eieio): More exhaustive testing of type nesting. +TEST(SerializationTest, NestedTuple) { + Payload result; + Payload expected; + + auto t1 = std::make_tuple('x', std::make_tuple<int, int>(1, 2)); + Serialize(t1, &result); + expected = decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 2, 'x', + ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2}); + EXPECT_EQ(expected, result); + result.Clear(); + + auto t2 = std::make_tuple('x', std::make_tuple<int, int>(1, 2), + std::string("0123456789")); + Serialize(t2, &result); + expected = decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 3, 'x', + ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2, + ENCODING_TYPE_FIXSTR | 10, '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9'}); + EXPECT_EQ(expected, result); + result.Clear(); + + auto t3 = std::make_tuple(0.0f, std::uint64_t(10ULL), + std::vector<char>{'a', 'b', 'c'}); + Serialize(t3, &result); + expected = decltype(expected)( + {ENCODING_TYPE_FIXARRAY_MIN + 3, ENCODING_TYPE_FLOAT32, + kZeroFloatBytes[0], kZeroFloatBytes[1], kZeroFloatBytes[2], + kZeroFloatBytes[3], ENCODING_TYPE_POSITIVE_FIXINT_MIN + 10, + ENCODING_TYPE_FIXARRAY_MIN + 3, 'a', 'b', 'c'}); + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, NestedMap) { + Payload result; + Payload expected; + + std::map<int, std::pair<std::string, int>> m1 = {{0, {"a", 2}}, + {1, {"b", 10}}}; + Serialize(m1, &result); + expected = decltype(expected)( + {ENCODING_TYPE_FIXMAP_MIN + 2, 0, ENCODING_TYPE_FIXARRAY_MIN + 2, + ENCODING_TYPE_FIXSTR_MIN + 1, 'a', 2, 1, ENCODING_TYPE_FIXARRAY_MIN + 2, + ENCODING_TYPE_FIXSTR_MIN + 1, 'b', 10}); + EXPECT_EQ(expected, result); + result.Clear(); +} + +TEST(SerializationTest, Serializable) { + Payload result; + Payload expected; + + TestType t1{10, 0.0, "12345", TestType::Foo::kBaz}; + Serialize(t1, &result); + expected = decltype(expected)( + {ENCODING_TYPE_FIXARRAY_MIN + 4, 10, ENCODING_TYPE_FLOAT32, + kZeroFloatBytes[0], kZeroFloatBytes[1], kZeroFloatBytes[2], + kZeroFloatBytes[3], ENCODING_TYPE_FIXSTR_MIN + 5, '1', '2', '3', '4', + '5', ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2}); + EXPECT_EQ(expected, result); + result.Clear(); + + TestTemplateType<LocalHandle> tt{LocalHandle(-1)}; + Serialize(tt, &result); + expected = + decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 1, ENCODING_TYPE_FIXEXT2, + ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xff, 0xff}); + EXPECT_EQ(expected, result); +} + +TEST(SerializationTest, Variant) { + Payload result; + Payload expected; + + Variant<int, bool, float> v; + + // Empty variant. + Serialize(v, &result); + expected = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_NEGATIVE_FIXINT_MAX, + ENCODING_TYPE_NIL}; + EXPECT_EQ(expected, result); + result.Clear(); + + v = 10; + Serialize(v, &result); + expected = {ENCODING_TYPE_FIXMAP_MIN + 1, + ENCODING_TYPE_POSITIVE_FIXINT_MIN + 0, + ENCODING_TYPE_POSITIVE_FIXINT_MIN + 10}; + EXPECT_EQ(expected, result); + result.Clear(); + + v = true; + Serialize(v, &result); + expected = {ENCODING_TYPE_FIXMAP_MIN + 1, + ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1, ENCODING_TYPE_TRUE}; + EXPECT_EQ(expected, result); + result.Clear(); + + v = false; + Serialize(v, &result); + expected = {ENCODING_TYPE_FIXMAP_MIN + 1, + ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1, ENCODING_TYPE_FALSE}; + EXPECT_EQ(expected, result); + result.Clear(); + + v = 1.0f; + Serialize(v, &result); + expected = {ENCODING_TYPE_FIXMAP_MIN + 1, + ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2, + ENCODING_TYPE_FLOAT32, + kOneFloatBytes[0], + kOneFloatBytes[1], + kOneFloatBytes[2], + kOneFloatBytes[3]}; + EXPECT_EQ(expected, result); + result.Clear(); + + // TODO(eieio): Add more serialization tests for Variant. +} + +TEST(DeserializationTest, bool) { + Payload buffer; + bool result = false; + ErrorType error; + + // True. + buffer = {ENCODING_TYPE_TRUE}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(1, result); // Gtest generates warning from bool literals. + + // False. + buffer = {ENCODING_TYPE_FALSE}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0, result); // Gtest generates warning from bool literals. +} + +TEST(DeserializationTest, uint8_t) { + Payload buffer; + std::uint8_t result = 0; + ErrorType error; + + // Min FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0U, result); + + // Max FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(127U, result); + + // Min UINT8. + buffer = {ENCODING_TYPE_UINT8, 0x00}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0U, result); + + // Max UINT8. + buffer = {ENCODING_TYPE_UINT8, 0xff}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0xffU, result); + + // UINT16 out of range. + buffer = {ENCODING_TYPE_UINT16}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_UINT16, error.encoding_type()); + + // UINT32 out of range. + buffer = {ENCODING_TYPE_UINT32}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_UINT32, error.encoding_type()); + + // UINT64 out of range. + buffer = {ENCODING_TYPE_UINT64}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_UINT64, error.encoding_type()); +} + +TEST(DeserializationTest, uint16_t) { + Payload buffer; + std::uint16_t result = 0; + ErrorType error; + + // Min FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0U, result); + + // Max FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(127U, result); + + // Min UINT8. + buffer = {ENCODING_TYPE_UINT8, 0x00}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0U, result); + + // Max UINT8. + buffer = {ENCODING_TYPE_UINT8, 0xff}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0xffU, result); + + // Min UINT16. + buffer = {ENCODING_TYPE_UINT16, 0x00, 0x00}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0U, result); + + // Max UINT16. + buffer = {ENCODING_TYPE_UINT16, 0xff, 0xff}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0xffffU, result); + + // UINT32 out of range. + buffer = {ENCODING_TYPE_UINT32}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_UINT32, error.encoding_type()); + + // UINT64 out of range. + buffer = {ENCODING_TYPE_UINT64}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_UINT64, error.encoding_type()); +} + +TEST(DeserializationTest, uint32_t) { + Payload buffer; + std::uint32_t result = 0; + ErrorType error; + + // Min FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0U, result); + + // Max FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(127U, result); + + // Min UINT8. + buffer = {ENCODING_TYPE_UINT8, 0x00}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0U, result); + + // Max UINT8. + buffer = {ENCODING_TYPE_UINT8, 0xff}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0xffU, result); + + // Min UINT16. + buffer = {ENCODING_TYPE_UINT16, 0x00, 0x00}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0U, result); + + // Max UINT16. + buffer = {ENCODING_TYPE_UINT16, 0xff, 0xff}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0xffffU, result); + + // Min UINT32. + buffer = {ENCODING_TYPE_UINT32, 0x00, 0x00, 0x00, 0x00}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0U, result); + + // Max UINT32. + buffer = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0xffffffffU, result); + + // UINT64 out of range. + buffer = {ENCODING_TYPE_UINT64}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_UINT64, error.encoding_type()); +} + +TEST(DeserializationTest, uint64_t) { + Payload buffer; + std::uint64_t result = 0; + ErrorType error; + + // Min FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0U, result); + + // Max FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(127U, result); + + // Min UINT8. + buffer = {ENCODING_TYPE_UINT8, 0x00}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0U, result); + + // Max UINT8. + buffer = {ENCODING_TYPE_UINT8, 0xff}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0xffU, result); + + // Min UINT16. + buffer = {ENCODING_TYPE_UINT16, 0x00, 0x00}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0U, result); + + // Max UINT16. + buffer = {ENCODING_TYPE_UINT16, 0xff, 0xff}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0xffffU, result); + + // Min UINT32. + buffer = {ENCODING_TYPE_UINT32, 0x00, 0x00, 0x00, 0x00}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0U, result); + + // Max UINT32. + buffer = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0xffffffffU, result); + + // Min UINT64. + buffer = { + ENCODING_TYPE_UINT64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0U, result); + + // Max UINT64. + buffer = { + ENCODING_TYPE_UINT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0xffffffffffffffffUL, result); +} + +TEST(DeserializationTest, int8_t) { + Payload buffer; + std::int8_t result = 0; + ErrorType error; + + // Min NEGATIVE FIXINT. + buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-32, result); + + // Max NEGATIVE FIXINT. + buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-1, result); + + // Min FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0, result); + + // Max FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(127, result); + + // Min INT8. + buffer = {ENCODING_TYPE_INT8, 0x80}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-128, result); + + // Max INT8. + buffer = {ENCODING_TYPE_INT8, 0x7f}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(127, result); + + // INT16 out of range. + buffer = {ENCODING_TYPE_INT16}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_INT16, error.encoding_type()); + + // INT32 out of range. + buffer = {ENCODING_TYPE_INT32}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_INT32, error.encoding_type()); + + // INT64 out of range. + buffer = {ENCODING_TYPE_INT64}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_INT64, error.encoding_type()); +} + +TEST(DeserializationTest, int16_t) { + Payload buffer; + std::int16_t result = 0; + ErrorType error; + + // Min NEGATIVE FIXINT. + buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-32, result); + + // Max NEGATIVE FIXINT. + buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-1, result); + + // Min FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0, result); + + // Max FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(127, result); + + // Min INT8. + buffer = {ENCODING_TYPE_INT8, 0x80}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-128, result); + + // Max INT8. + buffer = {ENCODING_TYPE_INT8, 0x7f}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(127, result); + + // Min INT16. + buffer = {ENCODING_TYPE_INT16, 0x00, 0x80}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-32768, result); + + // Max INT16. + buffer = {ENCODING_TYPE_INT16, 0xff, 0x7f}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(32767, result); + + // INT32 out of range. + buffer = {ENCODING_TYPE_INT32}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_INT32, error.encoding_type()); + + // INT64 out of range. + buffer = {ENCODING_TYPE_INT64}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_INT64, error.encoding_type()); +} + +TEST(DeserializationTest, int32_t) { + Payload buffer; + std::int32_t result = 0; + ErrorType error; + + // Min NEGATIVE FIXINT. + buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-32, result); + + // Max NEGATIVE FIXINT. + buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-1, result); + + // Min FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0, result); + + // Max FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(127, result); + + // Min INT8. + buffer = {ENCODING_TYPE_INT8, 0x80}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-128, result); + + // Max INT8. + buffer = {ENCODING_TYPE_INT8, 0x7f}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(127, result); + + // Min INT16. + buffer = {ENCODING_TYPE_INT16, 0x00, 0x80}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-32768, result); + + // Max INT16. + buffer = {ENCODING_TYPE_INT16, 0xff, 0x7f}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(32767, result); + + // Min INT32. + buffer = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-2147483648, result); + + // Max INT32. + buffer = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(2147483647, result); + + // INT64 out of range. + buffer = {ENCODING_TYPE_INT64}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_INT64, error.encoding_type()); +} + +TEST(DeserializationTest, int64_t) { + Payload buffer; + std::int64_t result = 0; + ErrorType error; + + // Min NEGATIVE FIXINT. + buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-32, result); + + // Max NEGATIVE FIXINT. + buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-1, result); + + // Min FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0, result); + + // Max FIXINT. + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(127, result); + + // Min INT8. + buffer = {ENCODING_TYPE_INT8, 0x80}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-128, result); + + // Max INT8. + buffer = {ENCODING_TYPE_INT8, 0x7f}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(127, result); + + // Min INT16. + buffer = {ENCODING_TYPE_INT16, 0x00, 0x80}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-32768, result); + + // Max INT16. + buffer = {ENCODING_TYPE_INT16, 0xff, 0x7f}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(32767, result); + + // Min INT32. + buffer = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-2147483648, result); + + // Max INT32. + buffer = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(2147483647, result); + + // Min INT64. + buffer = { + ENCODING_TYPE_INT64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + // Believe it or not, this is actually the correct way to specify the most + // negative signed long long. + EXPECT_EQ(-9223372036854775807LL - 1, result); + + // Max INT64. + buffer = { + ENCODING_TYPE_INT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(9223372036854775807LL, result); +} + +TEST(DeserializationTest, float) { + Payload buffer; + float result; + ErrorType error; + + // FLOAT32. + buffer = {ENCODING_TYPE_FLOAT32, kZeroFloatBytes[0], kZeroFloatBytes[1], + kZeroFloatBytes[2], kZeroFloatBytes[3]}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(kZeroFloat, result); + + // FLOAT32. + buffer = {ENCODING_TYPE_FLOAT32, kOneFloatBytes[0], kOneFloatBytes[1], + kOneFloatBytes[2], kOneFloatBytes[3]}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(kOneFloat, result); +} + +TEST(DeserializationTest, double) { + Payload buffer; + double result; + ErrorType error; + + // FLOAT32. + buffer = {ENCODING_TYPE_FLOAT32, kZeroFloatBytes[0], kZeroFloatBytes[1], + kZeroFloatBytes[2], kZeroFloatBytes[3]}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(kZeroDouble, result); + + // FLOAT64. + buffer = {ENCODING_TYPE_FLOAT64, kZeroDoubleBytes[0], kZeroDoubleBytes[1], + kZeroDoubleBytes[2], kZeroDoubleBytes[3], kZeroDoubleBytes[4], + kZeroDoubleBytes[5], kZeroDoubleBytes[6], kZeroDoubleBytes[7]}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(kZeroDouble, result); + + // FLOAT32. + buffer = {ENCODING_TYPE_FLOAT32, kOneFloatBytes[0], kOneFloatBytes[1], + kOneFloatBytes[2], kOneFloatBytes[3]}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(kOneDouble, result); + + // FLOAT64. + buffer = {ENCODING_TYPE_FLOAT64, kOneDoubleBytes[0], kOneDoubleBytes[1], + kOneDoubleBytes[2], kOneDoubleBytes[3], kOneDoubleBytes[4], + kOneDoubleBytes[5], kOneDoubleBytes[6], kOneDoubleBytes[7]}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(kOneDouble, result); +} + +TEST(DeserializationTest, Enum) { + Payload buffer; + enum Foo { kFoo, kBar, kBaz } result; + ErrorType error; + + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(kBar, result); +} + +TEST(DeserializationTest, EnumClass) { + Payload buffer; + enum Foo { kFoo, kBar, kBaz } result; + ErrorType error; + + buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(Foo::kBaz, result); +} + +TEST(DeserializationTest, LocalHandle) { + Payload buffer; + LocalHandle result1; + LocalHandle result2; + ErrorType error; + + buffer = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0}; + error = Deserialize(&result1, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0, result1.Get()); + result1.Release(); // Don't close fd 0. + + std::tuple<LocalHandle&, LocalHandle&> t1(result1, result2); + buffer = decltype(buffer)( + {ENCODING_TYPE_FIXARRAY_MIN + 2, ENCODING_TYPE_FIXEXT2, + ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0, ENCODING_TYPE_FIXEXT2, + ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 1, 0}); + error = Deserialize(&t1, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(0, result1.Get()); + EXPECT_EQ(1, result2.Get()); + result1.Release(); // Don't close fd 0. + result2.Release(); // Don't close fd 1. + + buffer = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xfe, + 0xff}; + error = Deserialize(&result1, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(-2, result1.Get()); +} + +TEST(DeserializationTest, string) { + Payload buffer; + std::string result = ""; + ErrorType error; + + // Min FIXSTR. + buffer = {ENCODING_TYPE_FIXSTR_MIN}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ("", result); + + // Max FIXSTR. + buffer = {ENCODING_TYPE_FIXSTR_MAX}; + buffer.Append((1 << 5) - 1, 'x'); + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(std::string((1 << 5) - 1, 'x'), result); + + // Min STR8. + buffer = {ENCODING_TYPE_STR8, 0x00}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ("", result); + + // Max STR8. + buffer = {ENCODING_TYPE_STR8, 0xff}; + buffer.Append(0xff, 'x'); + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(std::string(0xff, 'x'), result); + + // Min STR16. + buffer = {ENCODING_TYPE_STR16, 0x00, 0x00}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ("", result); + + // Max STR16. + buffer = {ENCODING_TYPE_STR16, 0xff, 0xff}; + buffer.Append(0xffff, 'x'); + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(std::string(0xffff, 'x'), result); + + // Min STR32. + buffer = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x00, 0x00}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ("", result); + + // Test STR32 with max STR16 + 1 bytes. It's not practical to test max + // STR32. + buffer = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x01, 0x00}; + buffer.Append(0x10000, 'x'); + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(std::string(0x10000, 'x'), result); +} + +TEST(DeserializationTest, vector) { + Payload buffer; + std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>> + result; + Payload expected; + ErrorType error; + + // Min FIXARRAY. + buffer = {ENCODING_TYPE_FIXARRAY_MIN}; + error = Deserialize(&result, &buffer); + expected = {}; + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // Max FIXARRAY. + buffer = {ENCODING_TYPE_FIXARRAY_MAX}; + buffer.Append((1 << 4) - 1, 1); + error = Deserialize(&result, &buffer); + expected = decltype(expected)((1 << 4) - 1, 1); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // Min ARRAY16. + buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00}; + error = Deserialize(&result, &buffer); + expected = {}; + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // Max ARRAY16. + buffer = {ENCODING_TYPE_ARRAY16, 0xff, 0xff}; + buffer.Append(0xffff, 1); + error = Deserialize(&result, &buffer); + expected = decltype(expected)(0xffff, 1); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // Min ARRAY32. + buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00}; + error = Deserialize(&result, &buffer); + expected = {}; + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // ARRAY32 with max ARRAY16 + 1. It's not practical to test max ARRAY32. + buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00}; + buffer.Append(0x10000, 1); + error = Deserialize(&result, &buffer); + expected = decltype(expected)(0x10000, 1); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); +} + +TEST(DeserializationTest, map) { + Payload buffer; + std::map<std::uint32_t, std::uint32_t> result; + std::map<std::uint32_t, std::uint32_t> expected; + ErrorType error; + + // Min FIXMAP. + buffer = {ENCODING_TYPE_FIXMAP_MIN}; + error = Deserialize(&result, &buffer); + expected = {}; + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // Size mismatch. + buffer = {ENCODING_TYPE_FIXMAP_MIN + 1}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::INSUFFICIENT_BUFFER, error); + + // Max FIXMAP. + buffer = {ENCODING_TYPE_FIXMAP_MAX}; + InsertKeyValue<decltype(result)>(&buffer, (1 << 4) - 1); + error = Deserialize(&result, &buffer); + expected = MakeMap<decltype(expected)>((1 << 4) - 1); + EXPECT_EQ(ErrorCode::NO_ERROR, error) << std::string(error); + EXPECT_EQ(expected, result); + + // Min MAP16. + buffer = {ENCODING_TYPE_MAP16, 0x00, 0x00}; + error = Deserialize(&result, &buffer); + expected = {}; + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // Max MAP16. + buffer = {ENCODING_TYPE_MAP16, 0xff, 0xff}; + InsertKeyValue<decltype(result)>(&buffer, (1 << 16) - 1); + error = Deserialize(&result, &buffer); + expected = MakeMap<decltype(expected)>((1 << 16) - 1); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // Min MAP32. + buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x00, 0x00}; + error = Deserialize(&result, &buffer); + expected = {}; + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // MAP32 with max MAP16 + 1. It's not practical to test max MAP32. + buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00}; + InsertKeyValue<decltype(result)>(&buffer, (1 << 16)); + error = Deserialize(&result, &buffer); + expected = MakeMap<decltype(expected)>((1 << 16)); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); +} + +TEST(DeserializationTest, unordered_map) { + Payload buffer; + std::unordered_map<std::uint32_t, std::uint32_t> result; + std::unordered_map<std::uint32_t, std::uint32_t> expected; + ErrorType error; + + // Min FIXMAP. + buffer = {ENCODING_TYPE_FIXMAP_MIN}; + error = Deserialize(&result, &buffer); + expected = {}; + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // Size mismatch. + buffer = {ENCODING_TYPE_FIXMAP_MIN + 1}; + error = Deserialize(&result, &buffer); + EXPECT_EQ(ErrorCode::INSUFFICIENT_BUFFER, error); + + // Max FIXMAP. + buffer = {ENCODING_TYPE_FIXMAP_MAX}; + InsertKeyValue<decltype(result)>(&buffer, (1 << 4) - 1); + error = Deserialize(&result, &buffer); + expected = MakeMap<decltype(expected)>((1 << 4) - 1); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // Min MAP16. + buffer = {ENCODING_TYPE_MAP16, 0x00, 0x00}; + error = Deserialize(&result, &buffer); + expected = {}; + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // Max MAP16. + buffer = {ENCODING_TYPE_MAP16, 0xff, 0xff}; + InsertKeyValue<decltype(result)>(&buffer, (1 << 16) - 1); + error = Deserialize(&result, &buffer); + expected = MakeMap<decltype(expected)>((1 << 16) - 1); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // Min MAP32. + buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x00, 0x00}; + error = Deserialize(&result, &buffer); + expected = {}; + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // MAP32 with max MAP16 + 1. It's not practical to test max MAP32. + buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00}; + InsertKeyValue<decltype(result)>(&buffer, (1 << 16)); + error = Deserialize(&result, &buffer); + expected = MakeMap<decltype(expected)>((1 << 16)); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); +} + +TEST(DeserializationTest, array) { + Payload buffer; + ErrorType error; + + // Min FIXARRAY. + buffer = {ENCODING_TYPE_FIXARRAY_MIN}; + std::array<std::uint8_t, 0> a0; + error = Deserialize(&a0, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + + // Size mismatch. + buffer = {ENCODING_TYPE_FIXARRAY_MIN + 1}; + error = Deserialize(&a0, &buffer); + EXPECT_EQ(ErrorCode::INSUFFICIENT_DESTINATION_SIZE, error); + + // Max FIXARRAY. + buffer = {ENCODING_TYPE_FIXARRAY_MAX}; + buffer.Append((1 << 4) - 1, 'x'); + std::array<std::uint8_t, (1 << 4) - 1> a1, expected1; + for (auto& element : expected1) + element = 'x'; + error = Deserialize(&a1, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected1, a1); + + // Min ARRAY16. + buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00}; + error = Deserialize(&a0, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + + // Max ARRAY16. + buffer = {ENCODING_TYPE_ARRAY16, 0xff, 0xff}; + buffer.Append((1 << 16) - 1, 'x'); + std::array<std::uint8_t, (1 << 16) - 1> a3, expected3; + for (auto& element : expected3) + element = 'x'; + error = Deserialize(&a3, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected3, a3); + + // Min ARRAY32. + buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00}; + error = Deserialize(&a0, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + + // ARRAY32 with max ARRAY16 + 1. It's not practical to test max ARRAY32. + buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00}; + buffer.Append((1 << 16), 'x'); + std::array<std::uint8_t, (1 << 16)> a4, expected4; + for (auto& element : expected4) + element = 'x'; + error = Deserialize(&a4, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected4, a4); +} + +TEST(DeserializationTest, ArrayWrapper) { + Payload buffer; + std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>> + result; + std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>> + expected; + ErrorType error; + + result.reserve(0x10000); + ArrayWrapper<std::uint8_t> wrapper(result.data(), result.capacity()); + + // Min FIXARRAY. + buffer = {ENCODING_TYPE_FIXARRAY_MIN}; + error = Deserialize(&wrapper, &buffer); + expected = {}; + result.resize(wrapper.size()); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // Max FIXARRAY. + buffer = {ENCODING_TYPE_FIXARRAY_MAX}; + buffer.Append((1 << 4) - 1, 1); + error = Deserialize(&wrapper, &buffer); + expected = decltype(expected)((1 << 4) - 1, 1); + result.resize(wrapper.size()); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // Min ARRAY16. + buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00}; + error = Deserialize(&wrapper, &buffer); + expected = {}; + result.resize(wrapper.size()); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // Max ARRAY16. + buffer = {ENCODING_TYPE_ARRAY16, 0xff, 0xff}; + buffer.Append(0xffff, 1); + error = Deserialize(&wrapper, &buffer); + expected = decltype(expected)(0xffff, 1); + result.resize(wrapper.size()); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // Min ARRAY32. + buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00}; + error = Deserialize(&wrapper, &buffer); + expected = {}; + result.resize(wrapper.size()); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); + + // ARRAY32 with max ARRAY16 + 1. It's not practical to test max ARRAY32. + buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00}; + buffer.Append(0x10000, 1); + error = Deserialize(&wrapper, &buffer); + expected = decltype(expected)(0x10000, 1); + result.resize(wrapper.size()); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(expected, result); +} + +TEST(DeserializationTest, pair) { + Payload buffer; + ErrorType error; + + std::pair<int, int> p1; + buffer = {ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2}; + error = Deserialize(&p1, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(std::make_pair(1, 2), p1); +} + +TEST(DeserializationTest, tuple) { + Payload buffer; + ErrorType error; + + // Min FIXARRAY. + std::tuple<> t1; + buffer = {ENCODING_TYPE_FIXARRAY_MIN}; + error = Deserialize(&t1, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(std::make_tuple(), t1); // Superfluous. + + // Max FIXARRAY. + auto t2 = GetNTuple<15, int>(0); + buffer = {ENCODING_TYPE_FIXARRAY_MAX}; + buffer.Append((1 << 4) - 1, 1); + error = Deserialize(&t2, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ((GetNTuple<15, int>(1)), t2); + + // Min ARRAY16. + // Using t1 above. + buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00}; + error = Deserialize(&t1, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(std::make_tuple(), t1); + + // ARRAY16 at Max FIXARRAY + 1 + auto t3 = GetNTuple<(1 << 4), int>(0); + buffer = {ENCODING_TYPE_ARRAY16, 0x10, 0x00}; + buffer.Append((1 << 4), 1); + error = Deserialize(&t3, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ((GetNTuple<(1 << 4), int>(1)), t3); + + // Min ARRAY32. + // Using t1 from above. + buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00}; + error = Deserialize(&t1, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(std::make_tuple(), t1); + + // ARRAY32 at Max FIXARRAY + 1 + auto t4 = GetNTuple<(1 << 4), int>(0); + buffer = {ENCODING_TYPE_ARRAY32, 0x10, 0x00, 0x00, 0x00}; + buffer.Append((1 << 4), 1); + error = Deserialize(&t4, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ((GetNTuple<(1 << 4), int>(1)), t4); + + // Template instantiation depth is an issue for tuples with large numbers of + // elements. As these are not expected in practice, the limits of ARRAY16 + // and ARRAY32 are not tested. +} + +TEST(DeserializationTest, Serializable) { + Payload buffer; + ErrorType error; + + buffer = decltype(buffer)( + {ENCODING_TYPE_FIXARRAY_MIN + 4, 10, ENCODING_TYPE_FLOAT32, + kZeroFloatBytes[0], kZeroFloatBytes[1], kZeroFloatBytes[2], + kZeroFloatBytes[3], ENCODING_TYPE_FIXSTR_MIN + 5, '1', '2', '3', '4', + '5', ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1}); + TestType t1; + error = Deserialize(&t1, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(TestType(10, 0.f, "12345", TestType::Foo::kBar), t1); + + buffer = + decltype(buffer)({ENCODING_TYPE_FIXARRAY_MIN + 1, ENCODING_TYPE_FIXEXT2, + ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xff, 0xff}); + TestTemplateType<LocalHandle> tt; + error = Deserialize(&tt, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_EQ(TestTemplateType<LocalHandle>(LocalHandle(-1)), tt); +} + +TEST(DeserializationTest, Variant) { + Payload buffer; + ErrorType error; + + Variant<int, bool, float> v; + + buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_NEGATIVE_FIXINT_MAX, + ENCODING_TYPE_NIL}; + error = Deserialize(&v, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + EXPECT_TRUE(v.empty()); + + buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_POSITIVE_FIXINT_MIN + 0, + ENCODING_TYPE_POSITIVE_FIXINT_MIN + 10}; + error = Deserialize(&v, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + ASSERT_TRUE(v.is<int>()); + EXPECT_EQ(10, std::get<int>(v)); + + buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1, + ENCODING_TYPE_TRUE}; + error = Deserialize(&v, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + ASSERT_TRUE(v.is<bool>()); + EXPECT_EQ(true, std::get<bool>(v)); + + buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1, + ENCODING_TYPE_FALSE}; + error = Deserialize(&v, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + ASSERT_TRUE(v.is<bool>()); + EXPECT_EQ(false, std::get<bool>(v)); + + buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, + ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2, + ENCODING_TYPE_FLOAT32, + kOneFloatBytes[0], + kOneFloatBytes[1], + kOneFloatBytes[2], + kOneFloatBytes[3]}; + error = Deserialize(&v, &buffer); + EXPECT_EQ(ErrorCode::NO_ERROR, error); + ASSERT_TRUE(v.is<float>()); + EXPECT_FLOAT_EQ(1.0, std::get<float>(v)); + + // TODO(eieio): Add more deserialization tests for Variant. +} + +TEST(DeserializationTest, ErrorType) { + Payload buffer; + ErrorType error; + + std::uint8_t u8; + buffer = {ENCODING_TYPE_STR8}; + error = Deserialize(&u8, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type()); + + std::uint16_t u16; + buffer = {ENCODING_TYPE_STR8}; + error = Deserialize(&u16, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type()); + + std::uint32_t u32; + buffer = {ENCODING_TYPE_STR8}; + error = Deserialize(&u32, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type()); + + std::uint64_t u64; + buffer = {ENCODING_TYPE_STR8}; + error = Deserialize(&u64, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type()); + + std::int8_t i8; + buffer = {ENCODING_TYPE_STR8}; + error = Deserialize(&i8, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type()); + + std::int16_t i16; + buffer = {ENCODING_TYPE_STR8}; + error = Deserialize(&i16, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type()); + + std::int32_t i32; + buffer = {ENCODING_TYPE_STR8}; + error = Deserialize(&i32, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type()); + + std::int64_t i64; + buffer = {ENCODING_TYPE_STR8}; + error = Deserialize(&i64, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type()); + + std::string s; + buffer = {ENCODING_TYPE_POSITIVE_FIXINT}; + error = Deserialize(&s, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_STRING, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_POSITIVE_FIXINT, error.encoding_type()); + + std::vector<std::uint8_t> v; + buffer = {ENCODING_TYPE_POSITIVE_FIXINT}; + error = Deserialize(&v, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_ARRAY, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_POSITIVE_FIXINT, error.encoding_type()); + + buffer = {ENCODING_TYPE_FIXARRAY_MIN + 1, ENCODING_TYPE_STR8}; + error = Deserialize(&v, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error); + EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class()); + EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type()); + + buffer = {ENCODING_TYPE_FIXARRAY_MIN + 2, 0, 1}; + std::tuple<int> t; + error = Deserialize(&t, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_TYPE_SIZE, error); + + buffer = {ENCODING_TYPE_FIXARRAY_MIN + 3, 0, 1, 2}; + std::pair<int, int> p; + error = Deserialize(&p, &buffer); + EXPECT_EQ(ErrorCode::UNEXPECTED_TYPE_SIZE, error); +} diff --git a/libs/vr/libpdx/service.cpp b/libs/vr/libpdx/service.cpp new file mode 100644 index 0000000000..fab4770b7f --- /dev/null +++ b/libs/vr/libpdx/service.cpp @@ -0,0 +1,684 @@ +#define LOG_TAG "ServiceFramework" +#include "pdx/service.h" + +#include <fcntl.h> +#include <log/log.h> +#include <utils/misc.h> + +#include <algorithm> +#include <cstdint> + +#include <pdx/trace.h> + +#define TRACE 0 + +namespace android { +namespace pdx { + +std::shared_ptr<Channel> Channel::GetFromMessageInfo(const MessageInfo& info) { + return info.channel ? info.channel->shared_from_this() + : std::shared_ptr<Channel>(); +} + +Message::Message() : replied_(true) {} + +Message::Message(const MessageInfo& info) + : service_{Service::GetFromMessageInfo(info)}, + channel_{Channel::GetFromMessageInfo(info)}, + info_{info}, + replied_{IsImpulse()} { + auto svc = service_.lock(); + if (svc) + state_ = svc->endpoint()->AllocateMessageState(); +} + +// C++11 specifies the move semantics for shared_ptr but not weak_ptr. This +// means we have to manually implement the desired move semantics for Message. +Message::Message(Message&& other) { *this = std::move(other); } + +Message& Message::operator=(Message&& other) { + Destroy(); + auto base = reinterpret_cast<std::uint8_t*>(&info_); + std::fill(&base[0], &base[sizeof(info_)], 0); + replied_ = true; + std::swap(service_, other.service_); + std::swap(channel_, other.channel_); + std::swap(info_, other.info_); + std::swap(state_, other.state_); + std::swap(replied_, other.replied_); + return *this; +} + +Message::~Message() { Destroy(); } + +void Message::Destroy() { + auto svc = service_.lock(); + if (svc) { + if (!replied_) { + ALOGE( + "ERROR: Service \"%s\" failed to reply to message: op=%d pid=%d " + "cid=%d\n", + svc->name_.c_str(), info_.op, info_.pid, info_.cid); + svc->DefaultHandleMessage(*this); + } + svc->endpoint()->FreeMessageState(state_); + } + state_ = nullptr; + service_.reset(); + channel_.reset(); +} + +const std::uint8_t* Message::ImpulseBegin() const { + return reinterpret_cast<const std::uint8_t*>(info_.impulse); +} + +const std::uint8_t* Message::ImpulseEnd() const { + return ImpulseBegin() + (IsImpulse() ? GetSendLength() : 0); +} + +Status<size_t> Message::ReadVector(const struct iovec* vector, + size_t vector_length) { + PDX_TRACE_NAME("Message::ReadVector"); + if (auto svc = service_.lock()) { + return svc->endpoint()->ReadMessageData(this, vector, vector_length); + } else { + return ErrorStatus{ESHUTDOWN}; + } +} + +Status<void> Message::ReadVectorAll(const struct iovec* vector, + size_t vector_length) { + PDX_TRACE_NAME("Message::ReadVectorAll"); + if (auto svc = service_.lock()) { + const auto status = + svc->endpoint()->ReadMessageData(this, vector, vector_length); + if (!status) + return status.error_status(); + size_t size_to_read = 0; + for (size_t i = 0; i < vector_length; i++) + size_to_read += vector[i].iov_len; + if (status.get() < size_to_read) + return ErrorStatus{EIO}; + return {}; + } else { + return ErrorStatus{ESHUTDOWN}; + } +} + +Status<size_t> Message::Read(void* buffer, size_t length) { + PDX_TRACE_NAME("Message::Read"); + if (auto svc = service_.lock()) { + const struct iovec vector = {buffer, length}; + return svc->endpoint()->ReadMessageData(this, &vector, 1); + } else { + return ErrorStatus{ESHUTDOWN}; + } +} + +Status<size_t> Message::WriteVector(const struct iovec* vector, + size_t vector_length) { + PDX_TRACE_NAME("Message::WriteVector"); + if (auto svc = service_.lock()) { + return svc->endpoint()->WriteMessageData(this, vector, vector_length); + } else { + return ErrorStatus{ESHUTDOWN}; + } +} + +Status<void> Message::WriteVectorAll(const struct iovec* vector, + size_t vector_length) { + PDX_TRACE_NAME("Message::WriteVector"); + if (auto svc = service_.lock()) { + const auto status = + svc->endpoint()->WriteMessageData(this, vector, vector_length); + if (!status) + return status.error_status(); + size_t size_to_write = 0; + for (size_t i = 0; i < vector_length; i++) + size_to_write += vector[i].iov_len; + if (status.get() < size_to_write) + return ErrorStatus{EIO}; + return {}; + } else { + return ErrorStatus{ESHUTDOWN}; + } +} + +Status<size_t> Message::Write(const void* buffer, size_t length) { + PDX_TRACE_NAME("Message::Write"); + if (auto svc = service_.lock()) { + const struct iovec vector = {const_cast<void*>(buffer), length}; + return svc->endpoint()->WriteMessageData(this, &vector, 1); + } else { + return ErrorStatus{ESHUTDOWN}; + } +} + +Status<FileReference> Message::PushFileHandle(const LocalHandle& handle) { + PDX_TRACE_NAME("Message::PushFileHandle"); + if (auto svc = service_.lock()) { + return svc->endpoint()->PushFileHandle(this, handle); + } else { + return ErrorStatus{ESHUTDOWN}; + } +} + +Status<FileReference> Message::PushFileHandle(const BorrowedHandle& handle) { + PDX_TRACE_NAME("Message::PushFileHandle"); + if (auto svc = service_.lock()) { + return svc->endpoint()->PushFileHandle(this, handle); + } else { + return ErrorStatus{ESHUTDOWN}; + } +} + +Status<FileReference> Message::PushFileHandle(const RemoteHandle& handle) { + PDX_TRACE_NAME("Message::PushFileHandle"); + if (auto svc = service_.lock()) { + return svc->endpoint()->PushFileHandle(this, handle); + } else { + return ErrorStatus{ESHUTDOWN}; + } +} + +Status<ChannelReference> Message::PushChannelHandle( + const LocalChannelHandle& handle) { + PDX_TRACE_NAME("Message::PushChannelHandle"); + if (auto svc = service_.lock()) { + return svc->endpoint()->PushChannelHandle(this, handle); + } else { + return ErrorStatus{ESHUTDOWN}; + } +} + +Status<ChannelReference> Message::PushChannelHandle( + const BorrowedChannelHandle& handle) { + PDX_TRACE_NAME("Message::PushChannelHandle"); + if (auto svc = service_.lock()) { + return svc->endpoint()->PushChannelHandle(this, handle); + } else { + return ErrorStatus{ESHUTDOWN}; + } +} + +Status<ChannelReference> Message::PushChannelHandle( + const RemoteChannelHandle& handle) { + PDX_TRACE_NAME("Message::PushChannelHandle"); + if (auto svc = service_.lock()) { + return svc->endpoint()->PushChannelHandle(this, handle); + } else { + return ErrorStatus{ESHUTDOWN}; + } +} + +bool Message::GetFileHandle(FileReference ref, LocalHandle* handle) { + PDX_TRACE_NAME("Message::GetFileHandle"); + auto svc = service_.lock(); + if (!svc) + return false; + + if (ref >= 0) { + *handle = svc->endpoint()->GetFileHandle(this, ref); + if (!handle->IsValid()) + return false; + } else { + *handle = LocalHandle{ref}; + } + return true; +} + +bool Message::GetChannelHandle(ChannelReference ref, + LocalChannelHandle* handle) { + PDX_TRACE_NAME("Message::GetChannelHandle"); + auto svc = service_.lock(); + if (!svc) + return false; + + if (ref >= 0) { + *handle = svc->endpoint()->GetChannelHandle(this, ref); + if (!handle->valid()) + return false; + } else { + *handle = LocalChannelHandle{nullptr, ref}; + } + return true; +} + +Status<void> Message::Reply(int return_code) { + PDX_TRACE_NAME("Message::Reply"); + auto svc = service_.lock(); + if (!replied_ && svc) { + const auto ret = svc->endpoint()->MessageReply(this, return_code); + replied_ = ret.ok(); + return ret; + } else { + return ErrorStatus{EINVAL}; + } +} + +Status<void> Message::ReplyFileDescriptor(unsigned int fd) { + PDX_TRACE_NAME("Message::ReplyFileDescriptor"); + auto svc = service_.lock(); + if (!replied_ && svc) { + const auto ret = svc->endpoint()->MessageReplyFd(this, fd); + replied_ = ret.ok(); + return ret; + } else { + return ErrorStatus{EINVAL}; + } +} + +Status<void> Message::ReplyError(unsigned int error) { + PDX_TRACE_NAME("Message::ReplyError"); + auto svc = service_.lock(); + if (!replied_ && svc) { + const auto ret = + svc->endpoint()->MessageReply(this, -static_cast<int>(error)); + replied_ = ret.ok(); + return ret; + } else { + return ErrorStatus{EINVAL}; + } +} + +Status<void> Message::Reply(const LocalHandle& handle) { + PDX_TRACE_NAME("Message::ReplyFileHandle"); + auto svc = service_.lock(); + if (!replied_ && svc) { + Status<void> ret; + + if (handle) + ret = svc->endpoint()->MessageReplyFd(this, handle.Get()); + else + ret = svc->endpoint()->MessageReply(this, handle.Get()); + + replied_ = ret.ok(); + return ret; + } else { + return ErrorStatus{EINVAL}; + } +} + +Status<void> Message::Reply(const BorrowedHandle& handle) { + PDX_TRACE_NAME("Message::ReplyFileHandle"); + auto svc = service_.lock(); + if (!replied_ && svc) { + Status<void> ret; + + if (handle) + ret = svc->endpoint()->MessageReplyFd(this, handle.Get()); + else + ret = svc->endpoint()->MessageReply(this, handle.Get()); + + replied_ = ret.ok(); + return ret; + } else { + return ErrorStatus{EINVAL}; + } +} + +Status<void> Message::Reply(const RemoteHandle& handle) { + PDX_TRACE_NAME("Message::ReplyFileHandle"); + auto svc = service_.lock(); + if (!replied_ && svc) { + Status<void> ret; + + if (handle) + ret = svc->endpoint()->MessageReply(this, handle.Get()); + else + ret = svc->endpoint()->MessageReply(this, handle.Get()); + + replied_ = ret.ok(); + return ret; + } else { + return ErrorStatus{EINVAL}; + } +} + +Status<void> Message::Reply(const LocalChannelHandle& handle) { + auto svc = service_.lock(); + if (!replied_ && svc) { + const auto ret = svc->endpoint()->MessageReplyChannelHandle(this, handle); + replied_ = ret.ok(); + return ret; + } else { + return ErrorStatus{EINVAL}; + } +} + +Status<void> Message::Reply(const BorrowedChannelHandle& handle) { + auto svc = service_.lock(); + if (!replied_ && svc) { + const auto ret = svc->endpoint()->MessageReplyChannelHandle(this, handle); + replied_ = ret.ok(); + return ret; + } else { + return ErrorStatus{EINVAL}; + } +} + +Status<void> Message::Reply(const RemoteChannelHandle& handle) { + auto svc = service_.lock(); + if (!replied_ && svc) { + const auto ret = svc->endpoint()->MessageReplyChannelHandle(this, handle); + replied_ = ret.ok(); + return ret; + } else { + return ErrorStatus{EINVAL}; + } +} + +Status<void> Message::ModifyChannelEvents(int clear_mask, int set_mask) { + PDX_TRACE_NAME("Message::ModifyChannelEvents"); + if (auto svc = service_.lock()) { + return svc->endpoint()->ModifyChannelEvents(info_.cid, clear_mask, + set_mask); + } else { + return ErrorStatus{ESHUTDOWN}; + } +} + +Status<RemoteChannelHandle> Message::PushChannel( + int flags, const std::shared_ptr<Channel>& channel, int* channel_id) { + PDX_TRACE_NAME("Message::PushChannel"); + if (auto svc = service_.lock()) { + return svc->PushChannel(this, flags, channel, channel_id); + } else { + return ErrorStatus(ESHUTDOWN); + } +} + +Status<RemoteChannelHandle> Message::PushChannel( + Service* service, int flags, const std::shared_ptr<Channel>& channel, + int* channel_id) { + PDX_TRACE_NAME("Message::PushChannel"); + return service->PushChannel(this, flags, channel, channel_id); +} + +Status<int> Message::CheckChannel(ChannelReference ref, + std::shared_ptr<Channel>* channel) const { + PDX_TRACE_NAME("Message::CheckChannel"); + if (auto svc = service_.lock()) { + return svc->CheckChannel(this, ref, channel); + } else { + return ErrorStatus(ESHUTDOWN); + } +} + +Status<int> Message::CheckChannel(const Service* service, ChannelReference ref, + std::shared_ptr<Channel>* channel) const { + PDX_TRACE_NAME("Message::CheckChannel"); + return service->CheckChannel(this, ref, channel); +} + +pid_t Message::GetProcessId() const { return info_.pid; } + +pid_t Message::GetThreadId() const { return info_.tid; } + +uid_t Message::GetEffectiveUserId() const { return info_.euid; } + +gid_t Message::GetEffectiveGroupId() const { return info_.egid; } + +int Message::GetChannelId() const { return info_.cid; } + +int Message::GetMessageId() const { return info_.mid; } + +int Message::GetOp() const { return info_.op; } + +int Message::GetFlags() const { return info_.flags; } + +size_t Message::GetSendLength() const { return info_.send_len; } + +size_t Message::GetReceiveLength() const { return info_.recv_len; } + +size_t Message::GetFileDescriptorCount() const { return info_.fd_count; } + +std::shared_ptr<Channel> Message::GetChannel() const { return channel_.lock(); } + +Status<void> Message::SetChannel(const std::shared_ptr<Channel>& chan) { + channel_ = chan; + Status<void> status; + if (auto svc = service_.lock()) + status = svc->SetChannel(info_.cid, chan); + return status; +} + +std::shared_ptr<Service> Message::GetService() const { return service_.lock(); } + +const MessageInfo& Message::GetInfo() const { return info_; } + +Service::Service(const std::string& name, std::unique_ptr<Endpoint> endpoint) + : name_(name), endpoint_{std::move(endpoint)} { + if (!endpoint_) + return; + + const auto status = endpoint_->SetService(this); + ALOGE_IF(!status, "Failed to set service context because: %s", + status.GetErrorMessage().c_str()); +} + +Service::~Service() { + if (endpoint_) { + const auto status = endpoint_->SetService(nullptr); + ALOGE_IF(!status, "Failed to clear service context because: %s", + status.GetErrorMessage().c_str()); + } +} + +std::shared_ptr<Service> Service::GetFromMessageInfo(const MessageInfo& info) { + return info.service ? info.service->shared_from_this() + : std::shared_ptr<Service>(); +} + +bool Service::IsInitialized() const { return endpoint_.get() != nullptr; } + +std::shared_ptr<Channel> Service::OnChannelOpen(Message& /*message*/) { + return nullptr; +} + +void Service::OnChannelClose(Message& /*message*/, + const std::shared_ptr<Channel>& /*channel*/) {} + +Status<void> Service::SetChannel(int channel_id, + const std::shared_ptr<Channel>& channel) { + PDX_TRACE_NAME("Service::SetChannel"); + std::lock_guard<std::mutex> autolock(channels_mutex_); + + const auto status = endpoint_->SetChannel(channel_id, channel.get()); + if (!status) { + ALOGE("%s::SetChannel: Failed to set channel context: %s\n", name_.c_str(), + status.GetErrorMessage().c_str()); + + // It's possible someone mucked with things behind our back by calling the C + // API directly. Since we know the channel id isn't valid, make sure we + // don't have it in the channels map. + if (status.error() == ENOENT) + channels_.erase(channel_id); + } else { + if (channel != nullptr) + channels_[channel_id] = channel; + else + channels_.erase(channel_id); + } + return status; +} + +std::shared_ptr<Channel> Service::GetChannel(int channel_id) const { + PDX_TRACE_NAME("Service::GetChannel"); + std::lock_guard<std::mutex> autolock(channels_mutex_); + + auto search = channels_.find(channel_id); + if (search != channels_.end()) + return search->second; + else + return nullptr; +} + +Status<void> Service::CloseChannel(int channel_id) { + PDX_TRACE_NAME("Service::CloseChannel"); + std::lock_guard<std::mutex> autolock(channels_mutex_); + + const auto status = endpoint_->CloseChannel(channel_id); + + // Always erase the map entry, in case someone mucked with things behind our + // back using the C API directly. + channels_.erase(channel_id); + + return status; +} + +Status<void> Service::ModifyChannelEvents(int channel_id, int clear_mask, + int set_mask) { + PDX_TRACE_NAME("Service::ModifyChannelEvents"); + return endpoint_->ModifyChannelEvents(channel_id, clear_mask, set_mask); +} + +Status<RemoteChannelHandle> Service::PushChannel( + Message* message, int flags, const std::shared_ptr<Channel>& channel, + int* channel_id) { + PDX_TRACE_NAME("Service::PushChannel"); + + std::lock_guard<std::mutex> autolock(channels_mutex_); + + int channel_id_temp = -1; + Status<RemoteChannelHandle> ret = + endpoint_->PushChannel(message, flags, channel.get(), &channel_id_temp); + ALOGE_IF(!ret.ok(), "%s::PushChannel: Failed to push channel: %s", + name_.c_str(), strerror(ret.error())); + + if (channel && channel_id_temp != -1) + channels_[channel_id_temp] = channel; + if (channel_id) + *channel_id = channel_id_temp; + + return ret; +} + +Status<int> Service::CheckChannel(const Message* message, ChannelReference ref, + std::shared_ptr<Channel>* channel) const { + PDX_TRACE_NAME("Service::CheckChannel"); + + // Synchronization to maintain consistency between the kernel's channel + // context pointer and the userspace channels_ map. Other threads may attempt + // to modify the map at the same time, which could cause the channel context + // pointer returned by the kernel to be invalid. + std::lock_guard<std::mutex> autolock(channels_mutex_); + + Channel* channel_context = nullptr; + Status<int> ret = endpoint_->CheckChannel( + message, ref, channel ? &channel_context : nullptr); + if (ret && channel) { + if (channel_context) + *channel = channel_context->shared_from_this(); + else + *channel = nullptr; + } + + return ret; +} + +std::string Service::DumpState(size_t /*max_length*/) { return ""; } + +Status<void> Service::HandleMessage(Message& message) { + return DefaultHandleMessage(message); +} + +void Service::HandleImpulse(Message& /*impulse*/) {} + +Status<void> Service::HandleSystemMessage(Message& message) { + const MessageInfo& info = message.GetInfo(); + + switch (info.op) { + case opcodes::CHANNEL_OPEN: { + ALOGD("%s::OnChannelOpen: pid=%d cid=%d\n", name_.c_str(), info.pid, + info.cid); + message.SetChannel(OnChannelOpen(message)); + return message.Reply(0); + } + + case opcodes::CHANNEL_CLOSE: { + ALOGD("%s::OnChannelClose: pid=%d cid=%d\n", name_.c_str(), info.pid, + info.cid); + OnChannelClose(message, Channel::GetFromMessageInfo(info)); + message.SetChannel(nullptr); + return message.Reply(0); + } + + case opcodes::REPORT_SYSPROP_CHANGE: + ALOGD("%s:REPORT_SYSPROP_CHANGE: pid=%d cid=%d\n", name_.c_str(), + info.pid, info.cid); + OnSysPropChange(); + android::report_sysprop_change(); + return message.Reply(0); + + case opcodes::DUMP_STATE: { + ALOGD("%s:DUMP_STATE: pid=%d cid=%d\n", name_.c_str(), info.pid, + info.cid); + auto response = DumpState(message.GetReceiveLength()); + const size_t response_size = response.size() < message.GetReceiveLength() + ? response.size() + : message.GetReceiveLength(); + const Status<size_t> status = + message.Write(response.data(), response_size); + if (status && status.get() < response_size) + return message.ReplyError(EIO); + else + return message.Reply(status); + } + + default: + return ErrorStatus{EOPNOTSUPP}; + } +} + +Status<void> Service::DefaultHandleMessage(Message& message) { + const MessageInfo& info = message.GetInfo(); + + ALOGD_IF(TRACE, "Service::DefaultHandleMessage: pid=%d cid=%d op=%d\n", + info.pid, info.cid, info.op); + + switch (info.op) { + case opcodes::CHANNEL_OPEN: + case opcodes::CHANNEL_CLOSE: + case opcodes::REPORT_SYSPROP_CHANGE: + case opcodes::DUMP_STATE: + return HandleSystemMessage(message); + + default: + return message.ReplyError(EOPNOTSUPP); + } +} + +void Service::OnSysPropChange() {} + +Status<void> Service::ReceiveAndDispatch() { + Message message; + const auto status = endpoint_->MessageReceive(&message); + if (!status) { + ALOGE("Failed to receive message: %s\n", status.GetErrorMessage().c_str()); + return status; + } + + std::shared_ptr<Service> service = message.GetService(); + + if (!service) { + ALOGE("Service::ReceiveAndDispatch: service context is NULL!!!\n"); + // Don't block the sender indefinitely in this error case. + endpoint_->MessageReply(&message, -EINVAL); + return ErrorStatus{EINVAL}; + } + + if (message.IsImpulse()) { + service->HandleImpulse(message); + return {}; + } else if (service->HandleSystemMessage(message)) { + return {}; + } else { + return service->HandleMessage(message); + } +} + +Status<void> Service::Cancel() { return endpoint_->Cancel(); } + +} // namespace pdx +} // namespace android diff --git a/libs/vr/libpdx/service_tests.cpp b/libs/vr/libpdx/service_tests.cpp new file mode 100644 index 0000000000..c7412b7aae --- /dev/null +++ b/libs/vr/libpdx/service_tests.cpp @@ -0,0 +1,807 @@ +#include <pdx/service.h> + +#include <memory> +#include <string> + +#include <gmock/gmock.h> +#include <pdx/mock_service_endpoint.h> + +using android::pdx::BorrowedChannelHandle; +using android::pdx::BorrowedHandle; +using android::pdx::Channel; +using android::pdx::ChannelReference; +using android::pdx::ErrorStatus; +using android::pdx::FileReference; +using android::pdx::LocalChannelHandle; +using android::pdx::LocalHandle; +using android::pdx::Message; +using android::pdx::MessageInfo; +using android::pdx::MockEndpoint; +using android::pdx::RemoteChannelHandle; +using android::pdx::RemoteHandle; +using android::pdx::Service; +using android::pdx::Status; + +using testing::A; +using testing::ByMove; +using testing::DoAll; +using testing::Invoke; +using testing::Matcher; +using testing::Ref; +using testing::Return; +using testing::SetArgPointee; +using testing::WithArg; +using testing::WithoutArgs; +using testing::_; + +namespace { + +// Helper functions to construct fake void pointers for tests. +inline void* IntToPtr(intptr_t addr) { return reinterpret_cast<void*>(addr); } + +// Helper matchers for working with iovec structures in tests. +// Simple matcher for one element iovec array: +// EXPECT_CALL(mock, method(IoVecMatcher(ptr, size))); +MATCHER_P2(IoVecMatcher, ptr, size, "") { + return arg->iov_base == ptr && arg->iov_len == size; +} + +// Matcher for an array of iovecs: +// EXPECT_CALL(mock, +// method(IoVecMatcher(IoVecArray{{ptr1, size1}, {ptr2, size2}}))); +using IoVecArray = std::vector<iovec>; +MATCHER_P(IoVecMatcher, iovec_array, "") { + for (const iovec& item : iovec_array) { + if (arg->iov_base != item.iov_base || arg->iov_len != item.iov_len) + return false; + arg++; + } + return true; +} + +using IoVecData = std::vector<std::string>; +MATCHER_P(IoVecDataMatcher, iovec_data, "") { + for (const std::string& item : iovec_data) { + std::string data{reinterpret_cast<const char*>(arg->iov_base), + arg->iov_len}; + if (data != item) + return false; + arg++; + } + return true; +} + +MATCHER_P(FileHandleMatcher, value, "") { return arg.Get() == value; } +MATCHER_P(ChannelHandleMatcher, value, "") { return arg.value() == value; } + +enum : int { + kTestPid = 1, + kTestTid, + kTestCid, + kTestMid, + kTestEuid, + kTestEgid, + kTestOp, +}; + +class MockService : public Service { + public: + using Service::Service; + MOCK_METHOD1(OnChannelOpen, std::shared_ptr<Channel>(Message& message)); + MOCK_METHOD2(OnChannelClose, + void(Message& message, const std::shared_ptr<Channel>& channel)); + MOCK_METHOD1(HandleMessage, Status<void>(Message& message)); + MOCK_METHOD1(HandleImpulse, void(Message& impulse)); + MOCK_METHOD0(OnSysPropChange, void()); + MOCK_METHOD1(DumpState, std::string(size_t max_length)); +}; + +class ServiceTest : public testing::Test { + public: + ServiceTest() { + auto endpoint = std::make_unique<testing::StrictMock<MockEndpoint>>(); + EXPECT_CALL(*endpoint, SetService(_)) + .Times(2) + .WillRepeatedly(Return(Status<void>{})); + service_ = std::make_shared<MockService>("MockSvc", std::move(endpoint)); + } + + MockEndpoint* endpoint() { + return static_cast<MockEndpoint*>(service_->endpoint()); + } + + void SetupMessageInfo(MessageInfo* info, int32_t op, bool impulse = false) { + info->pid = kTestPid; + info->tid = kTestTid; + info->cid = kTestCid; + info->mid = impulse ? Message::IMPULSE_MESSAGE_ID : kTestMid; + info->euid = kTestEuid; + info->egid = kTestEgid; + info->op = op; + info->flags = 0; + info->service = service_.get(); + info->channel = nullptr; + info->send_len = 0; + info->recv_len = 0; + info->fd_count = 0; + memset(info->impulse, 0, sizeof(info->impulse)); + } + + void SetupMessageInfoAndDefaultExpectations(MessageInfo* info, int32_t op, + bool impulse = false) { + SetupMessageInfo(info, op, impulse); + EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(kState)); + EXPECT_CALL(*endpoint(), FreeMessageState(kState)); + } + + void ExpectDefaultHandleMessage() { + EXPECT_CALL(*endpoint(), MessageReply(_, -EOPNOTSUPP)) + .WillOnce(Return(Status<void>{})); + } + + std::shared_ptr<MockService> service_; + void* kState = IntToPtr(123456); +}; + +class ServiceMessageTest : public ServiceTest { + public: + ServiceMessageTest() { + MessageInfo info; + SetupMessageInfoAndDefaultExpectations(&info, kTestOp); + message_ = std::make_unique<Message>(info); + } + + std::unique_ptr<Message> message_; +}; + +} // anonymous namespace + +/////////////////////////////////////////////////////////////////////////////// +// Service class tests +/////////////////////////////////////////////////////////////////////////////// + +TEST_F(ServiceTest, IsInitialized) { + EXPECT_TRUE(service_->IsInitialized()); + service_ = std::make_shared<MockService>("MockSvc2", nullptr); + EXPECT_FALSE(service_->IsInitialized()); +} + +TEST_F(ServiceTest, ConstructMessage) { + MessageInfo info; + SetupMessageInfo(&info, kTestOp); + auto test_channel = std::make_shared<Channel>(); + info.channel = test_channel.get(); + EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(kState)); + + Message message{info}; + + EXPECT_FALSE(message.IsImpulse()); + EXPECT_EQ(kTestPid, message.GetProcessId()); + EXPECT_EQ(kTestTid, message.GetThreadId()); + EXPECT_EQ(kTestCid, message.GetChannelId()); + EXPECT_EQ(kTestMid, message.GetMessageId()); + EXPECT_EQ(kTestEuid, message.GetEffectiveUserId()); + EXPECT_EQ(kTestEgid, message.GetEffectiveGroupId()); + EXPECT_EQ(kTestOp, message.GetOp()); + EXPECT_EQ(service_, message.GetService()); + EXPECT_EQ(test_channel, message.GetChannel()); + EXPECT_FALSE(message.replied()); + EXPECT_FALSE(message.IsChannelExpired()); + EXPECT_FALSE(message.IsServiceExpired()); + EXPECT_EQ(kState, message.GetState()); + + ExpectDefaultHandleMessage(); + EXPECT_CALL(*endpoint(), FreeMessageState(kState)); +} + +TEST_F(ServiceTest, ConstructImpulseMessage) { + MessageInfo info; + SetupMessageInfo(&info, kTestOp, true); + auto test_channel = std::make_shared<Channel>(); + info.channel = test_channel.get(); + EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(nullptr)); + + Message message{info}; + + EXPECT_TRUE(message.IsImpulse()); + EXPECT_EQ(kTestOp, message.GetOp()); + EXPECT_EQ(service_, message.GetService()); + EXPECT_EQ(test_channel, message.GetChannel()); + EXPECT_TRUE(message.replied()); + EXPECT_FALSE(message.IsChannelExpired()); + EXPECT_FALSE(message.IsServiceExpired()); + + // DefaultHandleMessage should not be called here. + EXPECT_CALL(*endpoint(), FreeMessageState(nullptr)); +} + +TEST_F(ServiceTest, HandleMessageChannelOpen) { + MessageInfo info; + SetupMessageInfoAndDefaultExpectations(&info, + android::pdx::opcodes::CHANNEL_OPEN); + Message message{info}; + + auto channel = std::make_shared<Channel>(); + EXPECT_CALL(*service_, OnChannelOpen(Ref(message))).WillOnce(Return(channel)); + EXPECT_CALL(*endpoint(), SetChannel(kTestCid, channel.get())) + .WillOnce(Return(Status<void>{})); + EXPECT_CALL(*endpoint(), MessageReply(&message, 0)) + .WillOnce(Return(Status<void>{})); + + EXPECT_TRUE(service_->Service::HandleMessage(message)); +} + +TEST_F(ServiceTest, HandleMessageChannelClose) { + MessageInfo info; + SetupMessageInfoAndDefaultExpectations(&info, + android::pdx::opcodes::CHANNEL_CLOSE); + auto channel = std::make_shared<Channel>(); + info.channel = channel.get(); + Message message{info}; + + EXPECT_CALL(*service_, OnChannelClose(Ref(message), channel)); + EXPECT_CALL(*endpoint(), SetChannel(kTestCid, nullptr)) + .WillOnce(Return(Status<void>{})); + EXPECT_CALL(*endpoint(), MessageReply(&message, 0)) + .WillOnce(Return(Status<void>{})); + + EXPECT_TRUE(service_->Service::HandleMessage(message)); +} + +TEST_F(ServiceTest, HandleMessageOnSysPropChange) { + MessageInfo info; + SetupMessageInfoAndDefaultExpectations( + &info, android::pdx::opcodes::REPORT_SYSPROP_CHANGE); + Message message{info}; + + EXPECT_CALL(*service_, OnSysPropChange()); + EXPECT_CALL(*endpoint(), MessageReply(&message, 0)) + .WillOnce(Return(Status<void>{})); + + EXPECT_TRUE(service_->Service::HandleMessage(message)); +} + +TEST_F(ServiceTest, HandleMessageOnDumpState) { + const size_t kRecvBufSize = 1000; + MessageInfo info; + SetupMessageInfoAndDefaultExpectations(&info, + android::pdx::opcodes::DUMP_STATE); + info.recv_len = kRecvBufSize; + Message message{info}; + + const std::string kReply = "foo"; + EXPECT_CALL(*service_, DumpState(kRecvBufSize)).WillOnce(Return(kReply)); + EXPECT_CALL( + *endpoint(), + WriteMessageData(&message, IoVecDataMatcher(IoVecData{kReply}), 1)) + .WillOnce(Return(kReply.size())); + EXPECT_CALL(*endpoint(), MessageReply(&message, kReply.size())) + .WillOnce(Return(Status<void>{})); + + EXPECT_TRUE(service_->Service::HandleMessage(message)); +} + +TEST_F(ServiceTest, HandleMessageOnDumpStateTooLarge) { + const size_t kRecvBufSize = 3; + MessageInfo info; + SetupMessageInfoAndDefaultExpectations(&info, + android::pdx::opcodes::DUMP_STATE); + info.recv_len = kRecvBufSize; + Message message{info}; + + const std::string kReply = "0123456789"; + const std::string kActualReply = kReply.substr(0, kRecvBufSize); + EXPECT_CALL(*service_, DumpState(kRecvBufSize)).WillOnce(Return(kReply)); + EXPECT_CALL( + *endpoint(), + WriteMessageData(&message, IoVecDataMatcher(IoVecData{kActualReply}), 1)) + .WillOnce(Return(kActualReply.size())); + EXPECT_CALL(*endpoint(), MessageReply(&message, kActualReply.size())) + .WillOnce(Return(Status<void>{})); + + EXPECT_TRUE(service_->Service::HandleMessage(message)); +} + +TEST_F(ServiceTest, HandleMessageOnDumpStateFail) { + const size_t kRecvBufSize = 1000; + MessageInfo info; + SetupMessageInfoAndDefaultExpectations(&info, + android::pdx::opcodes::DUMP_STATE); + info.recv_len = kRecvBufSize; + Message message{info}; + + const std::string kReply = "foo"; + EXPECT_CALL(*service_, DumpState(kRecvBufSize)).WillOnce(Return(kReply)); + EXPECT_CALL( + *endpoint(), + WriteMessageData(&message, IoVecDataMatcher(IoVecData{kReply}), 1)) + .WillOnce(Return(1)); + EXPECT_CALL(*endpoint(), MessageReply(&message, -EIO)) + .WillOnce(Return(Status<void>{})); + + EXPECT_TRUE(service_->Service::HandleMessage(message)); +} + +TEST_F(ServiceTest, HandleMessageCustom) { + MessageInfo info; + SetupMessageInfoAndDefaultExpectations(&info, kTestOp); + Message message{info}; + + EXPECT_CALL(*endpoint(), MessageReply(&message, -EOPNOTSUPP)) + .WillOnce(Return(Status<void>{})); + + EXPECT_TRUE(service_->Service::HandleMessage(message)); +} + +TEST_F(ServiceTest, ReplyMessageWithoutService) { + MessageInfo info; + SetupMessageInfo(&info, kTestOp); + EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(nullptr)); + + Message message{info}; + + EXPECT_FALSE(message.IsServiceExpired()); + service_.reset(); + EXPECT_TRUE(message.IsServiceExpired()); + + EXPECT_EQ(EINVAL, message.Reply(12).error()); +} + +TEST_F(ServiceTest, ReceiveAndDispatchMessage) { + MessageInfo info; + SetupMessageInfoAndDefaultExpectations(&info, kTestOp); + ExpectDefaultHandleMessage(); + + auto on_receive = [&info](Message* message) -> Status<void> { + *message = Message{info}; + return {}; + }; + EXPECT_CALL(*endpoint(), MessageReceive(_)).WillOnce(Invoke(on_receive)); + EXPECT_CALL(*service_, HandleMessage(_)).WillOnce(Return(Status<void>{})); + + EXPECT_TRUE(service_->ReceiveAndDispatch()); +} + +TEST_F(ServiceTest, ReceiveAndDispatchImpulse) { + MessageInfo info; + SetupMessageInfoAndDefaultExpectations(&info, kTestOp, true); + + auto on_receive = [&info](Message* message) -> Status<void> { + *message = Message{info}; + return {}; + }; + EXPECT_CALL(*endpoint(), MessageReceive(_)).WillOnce(Invoke(on_receive)); + EXPECT_CALL(*service_, HandleImpulse(_)); + + EXPECT_TRUE(service_->ReceiveAndDispatch()); +} + +TEST_F(ServiceTest, Cancel) { + EXPECT_CALL(*endpoint(), Cancel()).WillOnce(Return(Status<void>{})); + EXPECT_TRUE(service_->Cancel()); +} + +/////////////////////////////////////////////////////////////////////////////// +// Message class tests +/////////////////////////////////////////////////////////////////////////////// + +TEST_F(ServiceMessageTest, Reply) { + EXPECT_CALL(*endpoint(), MessageReply(message_.get(), 12)) + .WillOnce(Return(Status<void>{})); + EXPECT_FALSE(message_->replied()); + EXPECT_TRUE(message_->Reply(12)); + EXPECT_TRUE(message_->replied()); + + EXPECT_EQ(EINVAL, message_->Reply(12).error()); // Already replied. +} + +TEST_F(ServiceMessageTest, ReplyFail) { + EXPECT_CALL(*endpoint(), MessageReply(message_.get(), 12)) + .WillOnce(Return(ErrorStatus{EIO})); + EXPECT_EQ(EIO, message_->Reply(12).error()); + + ExpectDefaultHandleMessage(); +} + +TEST_F(ServiceMessageTest, ReplyError) { + EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -12)) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(message_->ReplyError(12)); +} + +TEST_F(ServiceMessageTest, ReplyFileDescriptor) { + EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), 5)) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(message_->ReplyFileDescriptor(5)); +} + +TEST_F(ServiceMessageTest, ReplyLocalFileHandle) { + const int kFakeFd = 12345; + LocalHandle handle{kFakeFd}; + EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), kFakeFd)) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(message_->Reply(handle)); + handle.Release(); // Make sure we do not close the fake file descriptor. +} + +TEST_F(ServiceMessageTest, ReplyLocalFileHandleError) { + LocalHandle handle{-EINVAL}; + EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EINVAL)) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(message_->Reply(handle)); +} + +TEST_F(ServiceMessageTest, ReplyBorrowedFileHandle) { + const int kFakeFd = 12345; + BorrowedHandle handle{kFakeFd}; + EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), kFakeFd)) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(message_->Reply(handle)); +} + +TEST_F(ServiceMessageTest, ReplyBorrowedFileHandleError) { + BorrowedHandle handle{-EACCES}; + EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EACCES)) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(message_->Reply(handle)); +} + +TEST_F(ServiceMessageTest, ReplyRemoteFileHandle) { + RemoteHandle handle{123}; + EXPECT_CALL(*endpoint(), MessageReply(message_.get(), handle.Get())) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(message_->Reply(handle)); +} + +TEST_F(ServiceMessageTest, ReplyRemoteFileHandleError) { + RemoteHandle handle{-EIO}; + EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EIO)) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(message_->Reply(handle)); +} + +TEST_F(ServiceMessageTest, ReplyLocalChannelHandle) { + LocalChannelHandle handle{nullptr, 12345}; + EXPECT_CALL(*endpoint(), MessageReplyChannelHandle( + message_.get(), A<const LocalChannelHandle&>())) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(message_->Reply(handle)); +} + +TEST_F(ServiceMessageTest, ReplyBorrowedChannelHandle) { + BorrowedChannelHandle handle{12345}; + EXPECT_CALL(*endpoint(), + MessageReplyChannelHandle(message_.get(), + A<const BorrowedChannelHandle&>())) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(message_->Reply(handle)); +} + +TEST_F(ServiceMessageTest, ReplyRemoteChannelHandle) { + RemoteChannelHandle handle{12345}; + EXPECT_CALL(*endpoint(), MessageReplyChannelHandle( + message_.get(), A<const RemoteChannelHandle&>())) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(message_->Reply(handle)); +} + +TEST_F(ServiceMessageTest, ReplyStatusInt) { + Status<int> status{123}; + EXPECT_CALL(*endpoint(), MessageReply(message_.get(), status.get())) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(message_->Reply(status)); +} + +TEST_F(ServiceMessageTest, ReplyStatusError) { + Status<int> status{ErrorStatus{EIO}}; + EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -status.error())) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(message_->Reply(status)); +} + +TEST_F(ServiceMessageTest, Read) { + ExpectDefaultHandleMessage(); + void* const kDataBuffer = IntToPtr(12345); + const size_t kDataSize = 100; + EXPECT_CALL( + *endpoint(), + ReadMessageData(message_.get(), IoVecMatcher(kDataBuffer, kDataSize), 1)) + .WillOnce(Return(50)) + .WillOnce(Return(ErrorStatus{EACCES})); + EXPECT_EQ(50u, message_->Read(kDataBuffer, kDataSize).get()); + EXPECT_EQ(EACCES, message_->Read(kDataBuffer, kDataSize).error()); +} + +TEST_F(ServiceMessageTest, ReadVector) { + ExpectDefaultHandleMessage(); + char buffer1[10]; + char buffer2[20]; + iovec vec[] = {{buffer1, sizeof(buffer1)}, {buffer2, sizeof(buffer2)}}; + EXPECT_CALL(*endpoint(), + ReadMessageData( + message_.get(), + IoVecMatcher(IoVecArray{std::begin(vec), std::end(vec)}), 2)) + .WillOnce(Return(30)) + .WillOnce(Return(15)) + .WillOnce(Return(ErrorStatus{EBADF})); + EXPECT_EQ(30u, message_->ReadVector(vec, 2).get()); + EXPECT_EQ(15u, message_->ReadVector(vec).get()); + EXPECT_EQ(EBADF, message_->ReadVector(vec).error()); +} + +TEST_F(ServiceMessageTest, Write) { + ExpectDefaultHandleMessage(); + void* const kDataBuffer = IntToPtr(12345); + const size_t kDataSize = 100; + EXPECT_CALL( + *endpoint(), + WriteMessageData(message_.get(), IoVecMatcher(kDataBuffer, kDataSize), 1)) + .WillOnce(Return(50)) + .WillOnce(Return(ErrorStatus{EBADMSG})); + EXPECT_EQ(50u, message_->Write(kDataBuffer, kDataSize).get()); + EXPECT_EQ(EBADMSG, message_->Write(kDataBuffer, kDataSize).error()); +} + +TEST_F(ServiceMessageTest, WriteVector) { + ExpectDefaultHandleMessage(); + char buffer1[10]; + char buffer2[20]; + iovec vec[] = {{buffer1, sizeof(buffer1)}, {buffer2, sizeof(buffer2)}}; + EXPECT_CALL(*endpoint(), + WriteMessageData( + message_.get(), + IoVecMatcher(IoVecArray{std::begin(vec), std::end(vec)}), 2)) + .WillOnce(Return(30)) + .WillOnce(Return(15)) + .WillOnce(Return(ErrorStatus{EIO})); + EXPECT_EQ(30u, message_->WriteVector(vec, 2).get()); + EXPECT_EQ(15u, message_->WriteVector(vec).get()); + EXPECT_EQ(EIO, message_->WriteVector(vec, 2).error()); +} + +TEST_F(ServiceMessageTest, PushLocalFileHandle) { + ExpectDefaultHandleMessage(); + const int kFakeFd = 12345; + LocalHandle handle{kFakeFd}; + EXPECT_CALL(*endpoint(), + PushFileHandle(message_.get(), Matcher<const LocalHandle&>( + FileHandleMatcher(kFakeFd)))) + .WillOnce(Return(12)) + .WillOnce(Return(ErrorStatus{EIO})); + EXPECT_EQ(12, message_->PushFileHandle(handle).get()); + EXPECT_EQ(EIO, message_->PushFileHandle(handle).error()); + handle.Release(); // Make sure we do not close the fake file descriptor. +} + +TEST_F(ServiceMessageTest, PushBorrowedFileHandle) { + ExpectDefaultHandleMessage(); + const int kFakeFd = 12345; + BorrowedHandle handle{kFakeFd}; + EXPECT_CALL(*endpoint(), + PushFileHandle(message_.get(), Matcher<const BorrowedHandle&>( + FileHandleMatcher(kFakeFd)))) + .WillOnce(Return(13)) + .WillOnce(Return(ErrorStatus{EACCES})); + EXPECT_EQ(13, message_->PushFileHandle(handle).get()); + EXPECT_EQ(EACCES, message_->PushFileHandle(handle).error()); +} + +TEST_F(ServiceMessageTest, PushRemoteFileHandle) { + ExpectDefaultHandleMessage(); + const int kFakeFd = 12345; + RemoteHandle handle{kFakeFd}; + EXPECT_CALL(*endpoint(), + PushFileHandle(message_.get(), Matcher<const RemoteHandle&>( + FileHandleMatcher(kFakeFd)))) + .WillOnce(Return(kFakeFd)) + .WillOnce(Return(ErrorStatus{EIO})); + EXPECT_EQ(kFakeFd, message_->PushFileHandle(handle).get()); + EXPECT_EQ(EIO, message_->PushFileHandle(handle).error()); +} + +TEST_F(ServiceMessageTest, PushLocalChannelHandle) { + ExpectDefaultHandleMessage(); + int32_t kValue = 12345; + LocalChannelHandle handle{nullptr, kValue}; + EXPECT_CALL(*endpoint(), PushChannelHandle(message_.get(), + Matcher<const LocalChannelHandle&>( + ChannelHandleMatcher(kValue)))) + .WillOnce(Return(7)) + .WillOnce(Return(ErrorStatus{EIO})); + EXPECT_EQ(7, message_->PushChannelHandle(handle).get()); + EXPECT_EQ(EIO, message_->PushChannelHandle(handle).error()); +} + +TEST_F(ServiceMessageTest, PushBorrowedChannelHandle) { + ExpectDefaultHandleMessage(); + int32_t kValue = 12345; + BorrowedChannelHandle handle{kValue}; + EXPECT_CALL( + *endpoint(), + PushChannelHandle(message_.get(), Matcher<const BorrowedChannelHandle&>( + ChannelHandleMatcher(kValue)))) + .WillOnce(Return(8)) + .WillOnce(Return(ErrorStatus{EIO})); + EXPECT_EQ(8, message_->PushChannelHandle(handle).get()); + EXPECT_EQ(EIO, message_->PushChannelHandle(handle).error()); +} + +TEST_F(ServiceMessageTest, PushRemoteChannelHandle) { + ExpectDefaultHandleMessage(); + int32_t kValue = 12345; + RemoteChannelHandle handle{kValue}; + EXPECT_CALL( + *endpoint(), + PushChannelHandle(message_.get(), Matcher<const RemoteChannelHandle&>( + ChannelHandleMatcher(kValue)))) + .WillOnce(Return(kValue)) + .WillOnce(Return(ErrorStatus{EIO})); + EXPECT_EQ(kValue, message_->PushChannelHandle(handle).get()); + EXPECT_EQ(EIO, message_->PushChannelHandle(handle).error()); +} + +TEST_F(ServiceMessageTest, GetFileHandle) { + ExpectDefaultHandleMessage(); + auto make_file_handle = [](FileReference ref) { return LocalHandle{ref}; }; + EXPECT_CALL(*endpoint(), GetFileHandle(message_.get(), _)) + .WillOnce(WithArg<1>(Invoke(make_file_handle))); + LocalHandle handle; + FileReference kRef = 12345; + EXPECT_TRUE(message_->GetFileHandle(kRef, &handle)); + EXPECT_EQ(kRef, handle.Get()); + handle.Release(); // Make sure we do not close the fake file descriptor. +} + +TEST_F(ServiceMessageTest, GetFileHandleInvalid) { + ExpectDefaultHandleMessage(); + LocalHandle handle; + FileReference kRef = -12; + EXPECT_TRUE(message_->GetFileHandle(kRef, &handle)); + EXPECT_EQ(kRef, handle.Get()); +} + +TEST_F(ServiceMessageTest, GetFileHandleError) { + ExpectDefaultHandleMessage(); + EXPECT_CALL(*endpoint(), GetFileHandle(message_.get(), _)) + .WillOnce(WithoutArgs(Invoke([] { return LocalHandle{-EIO}; }))); + LocalHandle handle; + FileReference kRef = 12345; + EXPECT_FALSE(message_->GetFileHandle(kRef, &handle)); + EXPECT_EQ(-EIO, handle.Get()); +} + +TEST_F(ServiceMessageTest, GetChannelHandle) { + ExpectDefaultHandleMessage(); + auto make_channel_handle = [](ChannelReference ref) { + return LocalChannelHandle{nullptr, ref}; + }; + EXPECT_CALL(*endpoint(), GetChannelHandle(message_.get(), _)) + .WillOnce(WithArg<1>(Invoke(make_channel_handle))); + LocalChannelHandle handle; + ChannelReference kRef = 12345; + EXPECT_TRUE(message_->GetChannelHandle(kRef, &handle)); + EXPECT_EQ(kRef, handle.value()); +} + +TEST_F(ServiceMessageTest, GetChannelHandleInvalid) { + ExpectDefaultHandleMessage(); + LocalChannelHandle handle; + ChannelReference kRef = -12; + EXPECT_TRUE(message_->GetChannelHandle(kRef, &handle)); + EXPECT_EQ(-12, handle.value()); +} + +TEST_F(ServiceMessageTest, GetChannelHandleError) { + ExpectDefaultHandleMessage(); + EXPECT_CALL(*endpoint(), GetChannelHandle(message_.get(), _)) + .WillOnce(WithoutArgs(Invoke([] { + return LocalChannelHandle{nullptr, -EIO}; + }))); + LocalChannelHandle handle; + ChannelReference kRef = 12345; + EXPECT_FALSE(message_->GetChannelHandle(kRef, &handle)); + EXPECT_EQ(-EIO, handle.value()); +} + +TEST_F(ServiceMessageTest, ModifyChannelEvents) { + ExpectDefaultHandleMessage(); + int kClearMask = 1; + int kSetMask = 2; + EXPECT_CALL(*endpoint(), ModifyChannelEvents(kTestCid, kClearMask, kSetMask)) + .WillOnce(Return(Status<void>{})); + EXPECT_TRUE(message_->ModifyChannelEvents(kClearMask, kSetMask)); +} + +TEST_F(ServiceMessageTest, PushChannelSameService) { + ExpectDefaultHandleMessage(); + int kFlags = 123; + int32_t kValue = 12; + EXPECT_CALL(*endpoint(), PushChannel(message_.get(), kFlags, nullptr, _)) + .WillOnce(DoAll(SetArgPointee<3>(kTestCid), + Return(ByMove(RemoteChannelHandle{kValue})))); + int channel_id = -1; + auto status = message_->PushChannel(kFlags, nullptr, &channel_id); + ASSERT_TRUE(status); + EXPECT_EQ(kValue, status.get().value()); + EXPECT_EQ(kTestCid, channel_id); +} + +TEST_F(ServiceMessageTest, PushChannelFailure) { + ExpectDefaultHandleMessage(); + int kFlags = 123; + EXPECT_CALL(*endpoint(), PushChannel(message_.get(), kFlags, nullptr, _)) + .WillOnce(Return(ByMove(ErrorStatus{EIO}))); + int channel_id = -1; + auto status = message_->PushChannel(kFlags, nullptr, &channel_id); + ASSERT_FALSE(status); + EXPECT_EQ(EIO, status.error()); +} + +TEST_F(ServiceMessageTest, PushChannelDifferentService) { + ExpectDefaultHandleMessage(); + auto endpoint2 = std::make_unique<testing::StrictMock<MockEndpoint>>(); + EXPECT_CALL(*endpoint2, SetService(_)) + .Times(2) + .WillRepeatedly(Return(Status<void>{})); + auto service2 = + std::make_shared<MockService>("MockSvc2", std::move(endpoint2)); + + int kFlags = 123; + int32_t kValue = 12; + EXPECT_CALL(*static_cast<MockEndpoint*>(service2->endpoint()), + PushChannel(message_.get(), kFlags, nullptr, _)) + .WillOnce(DoAll(SetArgPointee<3>(kTestCid), + Return(ByMove(RemoteChannelHandle{kValue})))); + int channel_id = -1; + auto status = + message_->PushChannel(service2.get(), kFlags, nullptr, &channel_id); + ASSERT_TRUE(status); + EXPECT_EQ(kValue, status.get().value()); + EXPECT_EQ(kTestCid, channel_id); +} + +TEST_F(ServiceMessageTest, CheckChannelSameService) { + ExpectDefaultHandleMessage(); + + auto test_channel = std::make_shared<Channel>(); + ChannelReference kRef = 123; + EXPECT_CALL(*endpoint(), CheckChannel(message_.get(), kRef, _)) + .WillOnce(DoAll(SetArgPointee<2>(test_channel.get()), Return(kTestCid))); + std::shared_ptr<Channel> channel; + auto status = message_->CheckChannel(kRef, &channel); + ASSERT_TRUE(status); + EXPECT_EQ(kTestCid, status.get()); + EXPECT_EQ(test_channel, channel); +} + +TEST_F(ServiceMessageTest, CheckChannelFailure) { + ExpectDefaultHandleMessage(); + ChannelReference kRef = 123; + EXPECT_CALL(*endpoint(), CheckChannel(message_.get(), kRef, _)) + .WillOnce(Return(ByMove(ErrorStatus{EOPNOTSUPP}))); + std::shared_ptr<Channel> channel; + auto status = message_->CheckChannel(kRef, &channel); + ASSERT_FALSE(status); + EXPECT_EQ(EOPNOTSUPP, status.error()); +} + +TEST_F(ServiceMessageTest, CheckChannelDifferentService) { + ExpectDefaultHandleMessage(); + auto endpoint2 = std::make_unique<testing::StrictMock<MockEndpoint>>(); + EXPECT_CALL(*endpoint2, SetService(_)) + .Times(2) + .WillRepeatedly(Return(Status<void>{})); + auto service2 = + std::make_shared<MockService>("MockSvc2", std::move(endpoint2)); + + auto test_channel = std::make_shared<Channel>(); + ChannelReference kRef = 123; + EXPECT_CALL(*static_cast<MockEndpoint*>(service2->endpoint()), + CheckChannel(message_.get(), kRef, _)) + .WillOnce(DoAll(SetArgPointee<2>(test_channel.get()), Return(kTestCid))); + std::shared_ptr<Channel> channel; + auto status = message_->CheckChannel(service2.get(), kRef, &channel); + ASSERT_TRUE(status); + EXPECT_EQ(kTestCid, status.get()); + EXPECT_EQ(test_channel, channel); +} diff --git a/libs/vr/libpdx/status.cpp b/libs/vr/libpdx/status.cpp new file mode 100644 index 0000000000..c275dafc5f --- /dev/null +++ b/libs/vr/libpdx/status.cpp @@ -0,0 +1,15 @@ +#include "pdx/status.h" + +#include <pdx/rpc/serialization.h> +#include <string.h> + +namespace android { +namespace pdx { + +std::string ErrorStatus::ErrorToString(int error_code) { + char message[1024] = {}; + return strerror_r(error_code, message, sizeof(message)); +} + +} // namespace pdx +} // namespace android diff --git a/libs/vr/libpdx/status_tests.cpp b/libs/vr/libpdx/status_tests.cpp new file mode 100644 index 0000000000..d4e697caee --- /dev/null +++ b/libs/vr/libpdx/status_tests.cpp @@ -0,0 +1,125 @@ +#include <pdx/status.h> + +#include <gtest/gtest.h> + +using android::pdx::ErrorStatus; +using android::pdx::Status; + +TEST(Status, DefaultInit) { + Status<int> status; + EXPECT_FALSE(status.ok()); + EXPECT_TRUE(status.empty()); + EXPECT_EQ(0, status.get()); + EXPECT_EQ(0, status.error()); +} + +TEST(Status, InitalizeSuccess) { + Status<int> status_int{0}; + EXPECT_FALSE(status_int.empty()); + EXPECT_TRUE(status_int.ok()); + EXPECT_EQ(0, status_int.get()); + status_int = Status<int>(3); + EXPECT_FALSE(status_int.empty()); + EXPECT_TRUE(status_int.ok()); + EXPECT_EQ(3, status_int.get()); + status_int = Status<int>(-3); + EXPECT_FALSE(status_int.empty()); + EXPECT_TRUE(status_int.ok()); + EXPECT_EQ(-3, status_int.get()); + + Status<std::string> status_str{"foo"}; + EXPECT_FALSE(status_str.empty()); + EXPECT_TRUE(status_str.ok()); + EXPECT_EQ("foo", status_str.get()); +} + +TEST(Status, InitalizeError) { + Status<int> status_int = ErrorStatus(12); + EXPECT_FALSE(status_int.empty()); + EXPECT_FALSE(status_int.ok()); + EXPECT_EQ(0, status_int.get()); + EXPECT_EQ(12, status_int.error()); + + Status<std::string> status_str = ErrorStatus(EIO); + EXPECT_FALSE(status_str.empty()); + EXPECT_FALSE(status_str.ok()); + EXPECT_EQ(EIO, status_str.error()); +} + +TEST(Status, ErrorMessage) { + Status<int> status = ErrorStatus(EIO); + EXPECT_EQ(status.GetErrorMessage(), strerror(EIO)); + + status = ErrorStatus(EINVAL); + EXPECT_EQ(status.GetErrorMessage(), strerror(EINVAL)); +} + +TEST(Status, Copy) { + Status<int> status1; + Status<int> status2; + + status1 = Status<int>{12}; + status2 = ErrorStatus(13); + EXPECT_FALSE(status1.empty()); + EXPECT_FALSE(status2.empty()); + EXPECT_TRUE(status1.ok()); + EXPECT_FALSE(status2.ok()); + EXPECT_EQ(12, status1.get()); + EXPECT_EQ(0, status1.error()); + EXPECT_EQ(0, status2.get()); + EXPECT_EQ(13, status2.error()); + + status1 = status2; + EXPECT_FALSE(status1.empty()); + EXPECT_FALSE(status2.empty()); + EXPECT_FALSE(status1.ok()); + EXPECT_FALSE(status2.ok()); + EXPECT_EQ(0, status1.get()); + EXPECT_EQ(13, status1.error()); + EXPECT_EQ(0, status2.get()); + EXPECT_EQ(13, status2.error()); +} + +TEST(Status, Move) { + Status<std::unique_ptr<int>> status1; + Status<std::unique_ptr<int>> status2; + + status1 = Status<std::unique_ptr<int>>{std::unique_ptr<int>{new int{11}}}; + status2 = Status<std::unique_ptr<int>>{std::unique_ptr<int>{new int{12}}}; + EXPECT_FALSE(status1.empty()); + EXPECT_FALSE(status2.empty()); + EXPECT_TRUE(status1.ok()); + EXPECT_TRUE(status2.ok()); + EXPECT_EQ(11, *status1.get()); + EXPECT_EQ(12, *status2.get()); + + Status<std::unique_ptr<int>> status3 = std::move(status2); + EXPECT_FALSE(status1.empty()); + EXPECT_TRUE(status2.empty()); + EXPECT_FALSE(status3.empty()); + EXPECT_TRUE(status1.ok()); + EXPECT_FALSE(status2.ok()); + EXPECT_TRUE(status3.ok()); + EXPECT_EQ(11, *status1.get()); + EXPECT_EQ(nullptr, status2.get()); + EXPECT_EQ(12, *status3.get()); + + std::swap(status1, status3); + EXPECT_EQ(12, *status1.get()); + EXPECT_EQ(11, *status3.get()); + + status3 = std::move(status1); + EXPECT_TRUE(status1.empty()); + EXPECT_EQ(12, *status3.get()); +} + +TEST(Status, Take) { + Status<std::unique_ptr<int>> status{std::unique_ptr<int>{new int{123}}}; + EXPECT_FALSE(status.empty()); + EXPECT_NE(nullptr, status.get()); + + auto data = status.take(); + EXPECT_TRUE(status.empty()); + EXPECT_EQ(nullptr, status.get()); + EXPECT_EQ(123, *data); +} diff --git a/libs/vr/libpdx/thread_local_buffer_tests.cpp b/libs/vr/libpdx/thread_local_buffer_tests.cpp new file mode 100644 index 0000000000..6cdaf1071a --- /dev/null +++ b/libs/vr/libpdx/thread_local_buffer_tests.cpp @@ -0,0 +1,117 @@ +#include <memory> +#include <string> +#include <thread> +#include <utility> + +#include <gtest/gtest.h> +#include <pdx/rpc/message_buffer.h> + +namespace android { +namespace pdx { +namespace rpc { + +class ThreadLocalBufferTest { + public: + // Returns the unique address of the thread-local buffer. Used to test the + // correct behavior of the type-based thread local storage slot mapping + // mechanism. + template <typename Slot> + static std::uintptr_t GetSlotAddress() { + return reinterpret_cast<std::uintptr_t>(&MessageBuffer<Slot>::buffer_); + } + + // Returns the raw value of the thread local buffer. Used to test the behavior + // of backing buffer initialization. + template <typename Slot> + static std::uintptr_t GetSlotValue() { + return reinterpret_cast<std::uintptr_t>(MessageBuffer<Slot>::buffer_); + } +}; + +} // namespace rpc +} // namespace pdx +} // namespace android + +using namespace android::pdx::rpc; + +namespace { + +struct TypeTagA; +struct TypeTagB; + +constexpr std::size_t kSendBufferIndex = 0; +constexpr std::size_t kReceiveBufferIndex = 1; + +using SendSlotA = ThreadLocalSlot<TypeTagA, kSendBufferIndex>; +using SendSlotB = ThreadLocalSlot<TypeTagB, kSendBufferIndex>; +using ReceiveSlotA = ThreadLocalSlot<TypeTagA, kReceiveBufferIndex>; +using ReceiveSlotB = ThreadLocalSlot<TypeTagB, kReceiveBufferIndex>; + +} // anonymous namespace + +// Tests that index and type-based thread-local slot addressing works by +// checking that the slot address is the same when the same index/type +// combination is used and different when different combinations are used. +TEST(ThreadLocalBufferTest, TypeSlots) { + auto id1 = ThreadLocalBufferTest::GetSlotAddress<SendSlotA>(); + auto id2 = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotA>(); + auto id3 = ThreadLocalBufferTest::GetSlotAddress<SendSlotB>(); + auto id4 = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotB>(); + + EXPECT_NE(id1, id2); + EXPECT_NE(id3, id4); + EXPECT_NE(id1, id3); + EXPECT_NE(id2, id4); + + auto id1_alias = ThreadLocalBufferTest::GetSlotAddress<SendSlotA>(); + auto id2_alias = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotA>(); + auto id3_alias = ThreadLocalBufferTest::GetSlotAddress<SendSlotB>(); + auto id4_alias = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotB>(); + + EXPECT_EQ(id1, id1_alias); + EXPECT_EQ(id2, id2_alias); + EXPECT_EQ(id3, id3_alias); + EXPECT_EQ(id4, id4_alias); +} + +// Tests that different threads get different buffers for the same slot address. +TEST(ThreadLocalBufferTest, ThreadSlots) { + auto id1 = ThreadLocalBufferTest::GetSlotAddress<SendBuffer>(); + std::uintptr_t id2 = 0U; + + std::thread thread([&id2]() mutable { + id2 = ThreadLocalBufferTest::GetSlotAddress<SendBuffer>(); + }); + thread.join(); + + EXPECT_NE(0U, id1); + EXPECT_NE(0U, id2); + EXPECT_NE(id1, id2); +} + +// Tests that thread-local buffers are allocated at the first buffer request. +TEST(ThreadLocalBufferTest, InitialValue) { + struct TypeTagX; + using SendSlotX = ThreadLocalSlot<TypeTagX, kSendBufferIndex>; + + auto value1 = ThreadLocalBufferTest::GetSlotValue<SendSlotX>(); + MessageBuffer<SendSlotX>::GetBuffer(); + auto value2 = ThreadLocalBufferTest::GetSlotValue<SendSlotX>(); + + EXPECT_EQ(0U, value1); + EXPECT_NE(0U, value2); +} + +// Tests that the underlying buffers are the same for a given index/type pair +// and different across index/type combinations. +TEST(ThreadLocalBufferTest, BackingBuffer) { + auto& buffer1 = MessageBuffer<SendSlotA>::GetBuffer(); + auto& buffer2 = MessageBuffer<SendSlotA>::GetBuffer(); + auto& buffer3 = MessageBuffer<SendSlotB>::GetBuffer(); + auto& buffer4 = MessageBuffer<SendSlotB>::GetBuffer(); + + EXPECT_EQ(buffer1.data(), buffer2.data()); + EXPECT_EQ(buffer3.data(), buffer4.data()); + EXPECT_NE(buffer1.data(), buffer3.data()); + EXPECT_NE(buffer2.data(), buffer4.data()); +} diff --git a/libs/vr/libpdx/variant_tests.cpp b/libs/vr/libpdx/variant_tests.cpp new file mode 100644 index 0000000000..325f33f3c5 --- /dev/null +++ b/libs/vr/libpdx/variant_tests.cpp @@ -0,0 +1,1101 @@ +#include <cstdint> +#include <functional> +#include <memory> +#include <string> +#include <type_traits> + +#include <gtest/gtest.h> +#include <pdx/rpc/variant.h> + +using namespace android::pdx; +using namespace android::pdx::rpc; + +namespace { + +struct BaseType { + BaseType(int value) : value(value) {} + int value; +}; + +struct DerivedType : BaseType { + DerivedType(int value) : BaseType{value} {}; +}; + +template <typename T> +class TestType { + public: + TestType(const T& value) : value_(value) {} + TestType(T&& value) : value_(std::move(value)) {} + TestType(const TestType&) = default; + TestType(TestType&&) = default; + + TestType& operator=(const TestType&) = default; + TestType& operator=(TestType&&) = default; + + const T& get() const { return value_; } + T&& take() { return std::move(value_); } + + private: + T value_; +}; + +template <typename T> +class InstrumentType { + public: + InstrumentType(const T& value) : value_(value) { constructor_count_++; } + InstrumentType(T&& value) : value_(std::move(value)) { constructor_count_++; } + InstrumentType(const InstrumentType& other) : value_(other.value_) { + constructor_count_++; + } + InstrumentType(InstrumentType&& other) : value_(std::move(other.value_)) { + constructor_count_++; + } + InstrumentType(const TestType<T>& other) : value_(other.get()) { + constructor_count_++; + } + InstrumentType(TestType<T>&& other) : value_(other.take()) { + constructor_count_++; + } + ~InstrumentType() { destructor_count_++; } + + InstrumentType& operator=(const InstrumentType& other) { + copy_assignment_count_++; + value_ = other.value_; + return *this; + } + InstrumentType& operator=(InstrumentType&& other) { + move_assignment_count_++; + value_ = std::move(other.value_); + return *this; + } + + InstrumentType& operator=(const TestType<T>& other) { + copy_assignment_count_++; + value_ = other.get(); + return *this; + } + InstrumentType& operator=(TestType<T>&& other) { + move_assignment_count_++; + value_ = other.take(); + return *this; + } + + static std::size_t constructor_count() { return constructor_count_; } + static std::size_t destructor_count() { return destructor_count_; } + static std::size_t move_assignment_count() { return move_assignment_count_; } + static std::size_t copy_assignment_count() { return copy_assignment_count_; } + + const T& get() const { return value_; } + T&& take() { return std::move(value_); } + + static void clear() { + constructor_count_ = 0; + destructor_count_ = 0; + move_assignment_count_ = 0; + copy_assignment_count_ = 0; + } + + private: + T value_; + + static std::size_t constructor_count_; + static std::size_t destructor_count_; + static std::size_t move_assignment_count_; + static std::size_t copy_assignment_count_; +}; + +template <typename T> +std::size_t InstrumentType<T>::constructor_count_ = 0; +template <typename T> +std::size_t InstrumentType<T>::destructor_count_ = 0; +template <typename T> +std::size_t InstrumentType<T>::move_assignment_count_ = 0; +template <typename T> +std::size_t InstrumentType<T>::copy_assignment_count_ = 0; + +} // anonymous namespace + +TEST(Variant, Assignment) { + // Assert basic type properties. + { + Variant<int, bool, float> v; + ASSERT_EQ(-1, v.index()); + ASSERT_FALSE(v.is<int>()); + ASSERT_FALSE(v.is<bool>()); + ASSERT_FALSE(v.is<float>()); + } + + { + Variant<int, bool, float> v; + v = 10; + ASSERT_EQ(0, v.index()); + ASSERT_TRUE(v.is<int>()); + ASSERT_FALSE(v.is<bool>()); + ASSERT_FALSE(v.is<float>()); + EXPECT_EQ(10, std::get<int>(v)); + } + + { + Variant<int, bool, float> v; + v = false; + ASSERT_EQ(1, v.index()); + ASSERT_FALSE(v.is<int>()); + ASSERT_TRUE(v.is<bool>()); + ASSERT_FALSE(v.is<float>()); + EXPECT_EQ(false, std::get<bool>(v)); + } + + { + Variant<int, bool, float> v; + v = 1.0f; + ASSERT_EQ(2, v.index()); + ASSERT_FALSE(v.is<int>()); + ASSERT_FALSE(v.is<bool>()); + ASSERT_TRUE(v.is<float>()); + EXPECT_FLOAT_EQ(1.0f, std::get<float>(v)); + } + + { + Variant<int, bool, float> v; + // ERROR: More than one type is implicitly convertible from double. + // v = 1.0; + v = static_cast<float>(1.0); + } + + { + Variant<int, bool, float> v; + + double x = 1.1; + v = static_cast<float>(x); + ASSERT_EQ(2, v.index()); + ASSERT_FALSE(v.is<int>()); + ASSERT_FALSE(v.is<bool>()); + ASSERT_TRUE(v.is<float>()); + EXPECT_FLOAT_EQ(1.1, std::get<float>(v)); + } + + { + Variant<int, std::string> v; + ASSERT_EQ(-1, v.index()); + ASSERT_FALSE(v.is<int>()); + ASSERT_FALSE(v.is<std::string>()); + } + + { + Variant<int, std::string> v; + v = 20; + ASSERT_EQ(0, v.index()); + ASSERT_TRUE(v.is<int>()); + ASSERT_FALSE(v.is<std::string>()); + EXPECT_EQ(20, std::get<int>(v)); + } + + { + Variant<int, std::string> v; + v = std::string("test"); + ASSERT_EQ(1, v.index()); + ASSERT_FALSE(v.is<int>()); + ASSERT_TRUE(v.is<std::string>()); + EXPECT_EQ("test", std::get<std::string>(v)); + } + + { + Variant<int, std::string> v; + v = "test"; + ASSERT_EQ(1, v.index()); + ASSERT_FALSE(v.is<int>()); + ASSERT_TRUE(v.is<std::string>()); + EXPECT_EQ("test", std::get<std::string>(v)); + } + + { + Variant<const char*> v1; + Variant<std::string> v2; + + v1 = "test"; + ASSERT_TRUE(v1.is<const char*>()); + v2 = v1; + ASSERT_TRUE(v2.is<std::string>()); + EXPECT_EQ("test", std::get<std::string>(v2)); + } + + { + Variant<int> a(1); + Variant<int> b; + ASSERT_TRUE(!a.empty()); + ASSERT_TRUE(b.empty()); + + a = b; + ASSERT_TRUE(a.empty()); + ASSERT_TRUE(b.empty()); + } + + { + Variant<int*, char*> v; + + // ERROR: More than one type is implicitly convertible from nullptr. + // v = nullptr; + + v = static_cast<int*>(nullptr); + EXPECT_TRUE(v.is<int*>()); + + v = static_cast<char*>(nullptr); + EXPECT_TRUE(v.is<char*>()); + } + + { + Variant<int*, char*> v; + int a = 10; + char b = 20; + + v = &b; + ASSERT_TRUE(v.is<char*>()); + EXPECT_EQ(&b, std::get<char*>(v)); + EXPECT_EQ(b, *std::get<char*>(v)); + + v = &a; + ASSERT_TRUE(v.is<int*>()); + EXPECT_EQ(&a, std::get<int*>(v)); + EXPECT_EQ(a, *std::get<int*>(v)); + } + + { + using IntRef = std::reference_wrapper<int>; + Variant<IntRef> v; + int a = 10; + + v = a; + ASSERT_TRUE(v.is<IntRef>()); + EXPECT_EQ(a, std::get<IntRef>(v)); + + a = 20; + EXPECT_EQ(a, std::get<IntRef>(v)); + } +} + +TEST(Variant, MoveAssignment) { + { + Variant<std::string> v; + std::string s = "test"; + v = std::move(s); + + EXPECT_TRUE(s.empty()); + ASSERT_TRUE(v.is<std::string>()); + EXPECT_EQ("test", std::get<std::string>(v)); + } + + { + Variant<std::string> v("test"); + std::string s = "fizz"; + s = std::move(std::get<std::string>(v)); + + ASSERT_TRUE(v.is<std::string>()); + EXPECT_TRUE(std::get<std::string>(v).empty()); + EXPECT_EQ("test", s); + } + + { + Variant<std::string> a("test"); + Variant<std::string> b; + + b = std::move(a); + ASSERT_TRUE(a.is<std::string>()); + ASSERT_TRUE(b.is<std::string>()); + EXPECT_TRUE(std::get<std::string>(a).empty()); + EXPECT_EQ("test", std::get<std::string>(b)); + } + + { + Variant<std::string> a("test"); + Variant<std::string> b("fizz"); + + b = std::move(a); + ASSERT_TRUE(a.is<std::string>()); + ASSERT_TRUE(b.is<std::string>()); + EXPECT_TRUE(std::get<std::string>(a).empty()); + EXPECT_EQ("test", std::get<std::string>(b)); + } + + { + Variant<int, std::string> a("test"); + Variant<int, std::string> b(10); + + b = std::move(a); + ASSERT_TRUE(a.is<std::string>()); + ASSERT_TRUE(b.is<std::string>()); + EXPECT_TRUE(std::get<std::string>(a).empty()); + EXPECT_EQ("test", std::get<std::string>(b)); + } + + { + Variant<int, std::string> a(10); + Variant<int, std::string> b("test"); + + b = std::move(a); + ASSERT_TRUE(a.is<int>()); + ASSERT_TRUE(b.is<int>()); + EXPECT_EQ(10, std::get<int>(a)); + EXPECT_EQ(10, std::get<int>(b)); + } +} + +TEST(Variant, Constructor) { + { + Variant<int, bool, float> v(true); + EXPECT_TRUE(v.is<bool>()); + } + + { + Variant<int, bool, float> v(10); + EXPECT_TRUE(v.is<int>()); + } + + { + Variant<int, bool, float> v(10.1f); + EXPECT_TRUE(v.is<float>()); + } + + { + Variant<float, std::string> v(10.); + EXPECT_TRUE(v.is<float>()); + } + + { + TestType<int> i(1); + Variant<int, bool, float> v(i.take()); + ASSERT_TRUE(v.is<int>()); + EXPECT_EQ(1, std::get<int>(v)); + } + + { + TestType<int> i(1); + Variant<int, bool, float> v(i.get()); + ASSERT_TRUE(v.is<int>()); + EXPECT_EQ(1, std::get<int>(v)); + } + + { + TestType<bool> b(true); + Variant<int, bool, float> v(b.take()); + ASSERT_TRUE(v.is<bool>()); + EXPECT_EQ(true, std::get<bool>(v)); + } + + { + TestType<bool> b(true); + Variant<int, bool, float> v(b.get()); + ASSERT_TRUE(v.is<bool>()); + EXPECT_EQ(true, std::get<bool>(v)); + } + + { + Variant<const char*> c("test"); + Variant<std::string> s(c); + ASSERT_TRUE(s.is<std::string>()); + EXPECT_EQ("test", std::get<std::string>(s)); + } + + { + Variant<int, bool, float> a(true); + Variant<int, bool, float> b(a); + + ASSERT_TRUE(b.is<bool>()); + } + + { + using IntRef = std::reference_wrapper<int>; + int a = 10; + Variant<IntRef> v(a); + TestType<IntRef> t(a); + + ASSERT_TRUE(v.is<IntRef>()); + EXPECT_EQ(a, std::get<IntRef>(v)); + EXPECT_EQ(a, t.get()); + + a = 20; + EXPECT_EQ(a, std::get<IntRef>(v)); + EXPECT_EQ(a, t.get()); + } +} + +// Verify correct ctor/dtor and assignment behavior used an instrumented type. +TEST(Variant, CopyMoveConstructAssign) { + { + InstrumentType<int>::clear(); + + // Default construct to empty, no InstrumentType activity. + Variant<int, InstrumentType<int>> v; + ASSERT_EQ(0u, InstrumentType<int>::constructor_count()); + ASSERT_EQ(0u, InstrumentType<int>::destructor_count()); + ASSERT_EQ(0u, InstrumentType<int>::move_assignment_count()); + ASSERT_EQ(0u, InstrumentType<int>::copy_assignment_count()); + } + + { + InstrumentType<int>::clear(); + + // Construct from int type, no InstrumentType activity. + Variant<int, InstrumentType<int>> v; + v = 10; + EXPECT_EQ(0u, InstrumentType<int>::constructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::destructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count()); + EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count()); + } + + { + InstrumentType<int>::clear(); + + // Construct from int type, no InstrumentType activity. + Variant<int, InstrumentType<int>> v(10); + EXPECT_EQ(0u, InstrumentType<int>::constructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::destructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count()); + EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count()); + } + + { + InstrumentType<int>::clear(); + + // Construct from temporary, temporary ctor/dtor. + Variant<int, InstrumentType<int>> v; + v = InstrumentType<int>(25); + EXPECT_EQ(2u, InstrumentType<int>::constructor_count()); + EXPECT_EQ(1u, InstrumentType<int>::destructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count()); + EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count()); + } + + { + InstrumentType<int>::clear(); + + // Construct from temporary, temporary ctor/dtor. + Variant<int, InstrumentType<int>> v(InstrumentType<int>(25)); + EXPECT_EQ(2u, InstrumentType<int>::constructor_count()); + EXPECT_EQ(1u, InstrumentType<int>::destructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count()); + EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count()); + } + + { + InstrumentType<int>::clear(); + + // Construct from temporary, temporary ctor/dtor. + Variant<int, InstrumentType<int>> v(InstrumentType<int>(25)); + + // Assign from temporary, temporary ctor/dtor. + v = InstrumentType<int>(35); + EXPECT_EQ(3u, InstrumentType<int>::constructor_count()); + EXPECT_EQ(2u, InstrumentType<int>::destructor_count()); + EXPECT_EQ(1u, InstrumentType<int>::move_assignment_count()); + EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count()); + } + + { + InstrumentType<int>::clear(); + + // Construct from temporary, temporary ctor/dtor. + Variant<int, InstrumentType<int>> v(InstrumentType<int>(25)); + + // dtor. + v = 10; + EXPECT_EQ(2u, InstrumentType<int>::constructor_count()); + EXPECT_EQ(2u, InstrumentType<int>::destructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count()); + EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count()); + } + + { + InstrumentType<int>::clear(); + + // Construct from temporary, temporary ctor/dtor. + Variant<int, InstrumentType<int>> v(InstrumentType<int>(25)); + + EXPECT_EQ(2u, InstrumentType<int>::constructor_count()); + EXPECT_EQ(1u, InstrumentType<int>::destructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count()); + EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count()); + } + EXPECT_EQ(2u, InstrumentType<int>::constructor_count()); + EXPECT_EQ(2u, InstrumentType<int>::destructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count()); + EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count()); + + { + InstrumentType<int>::clear(); + + // Construct from other temporary. + Variant<int, InstrumentType<int>> v(TestType<int>(10)); + EXPECT_EQ(1u, InstrumentType<int>::constructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::destructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count()); + EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count()); + } + + { + InstrumentType<int>::clear(); + + // Construct from other temporary. + Variant<int, InstrumentType<int>> v(TestType<int>(10)); + // Assign from other temporary. + v = TestType<int>(11); + EXPECT_EQ(1u, InstrumentType<int>::constructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::destructor_count()); + EXPECT_EQ(1u, InstrumentType<int>::move_assignment_count()); + EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count()); + } + + { + InstrumentType<int>::clear(); + + // Construct from other temporary. + Variant<int, InstrumentType<int>> v(TestType<int>(10)); + // Assign from empty Variant. + v = Variant<int, InstrumentType<int>>(); + EXPECT_EQ(1u, InstrumentType<int>::constructor_count()); + EXPECT_EQ(1u, InstrumentType<int>::destructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count()); + EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count()); + } + + { + InstrumentType<int>::clear(); + + TestType<int> other(10); + // Construct from other. + Variant<int, InstrumentType<int>> v(other); + + EXPECT_EQ(1u, InstrumentType<int>::constructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::destructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count()); + EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count()); + } + + { + InstrumentType<int>::clear(); + + // Construct from other temporary. + Variant<int, InstrumentType<int>> v(TestType<int>(0)); + TestType<int> other(10); + // Assign from other. + v = other; + EXPECT_EQ(1u, InstrumentType<int>::constructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::destructor_count()); + EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count()); + EXPECT_EQ(1u, InstrumentType<int>::copy_assignment_count()); + } +} + +TEST(Variant, MoveConstructor) { + { + std::unique_ptr<int> pointer = std::make_unique<int>(10); + Variant<std::unique_ptr<int>> v(std::move(pointer)); + ASSERT_TRUE(v.is<std::unique_ptr<int>>()); + EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) != nullptr); + EXPECT_TRUE(pointer == nullptr); + } + + { + Variant<std::unique_ptr<int>> a(std::make_unique<int>(10)); + Variant<std::unique_ptr<int>> b(std::move(a)); + + ASSERT_TRUE(a.is<std::unique_ptr<int>>()); + ASSERT_TRUE(b.is<std::unique_ptr<int>>()); + EXPECT_TRUE(std::get<std::unique_ptr<int>>(a) == nullptr); + EXPECT_TRUE(std::get<std::unique_ptr<int>>(b) != nullptr); + } +} + +TEST(Variant, IndexOf) { + Variant<int, bool, float> v1; + + EXPECT_EQ(0, v1.index_of<int>()); + EXPECT_EQ(1, v1.index_of<bool>()); + EXPECT_EQ(2, v1.index_of<float>()); + + Variant<int, bool, float, int> v2; + + EXPECT_EQ(0, v2.index_of<int>()); + EXPECT_EQ(1, v2.index_of<bool>()); + EXPECT_EQ(2, v2.index_of<float>()); +} + +struct Visitor { + int int_value = 0; + bool bool_value = false; + float float_value = 0.0; + bool empty_value = false; + + void Visit(int value) { int_value = value; } + void Visit(bool value) { bool_value = value; } + void Visit(float value) { float_value = value; } + void Visit(EmptyVariant) { empty_value = true; } +}; + +TEST(Variant, Visit) { + { + Variant<int, bool, float> v(10); + EXPECT_TRUE(v.is<int>()); + + Visitor visitor; + v.Visit([&visitor](const auto& value) { visitor.Visit(value); }); + EXPECT_EQ(10, visitor.int_value); + + visitor = {}; + v = true; + v.Visit([&visitor](const auto& value) { visitor.Visit(value); }); + EXPECT_EQ(true, visitor.bool_value); + } + + { + Variant<int, bool, float> v; + EXPECT_EQ(-1, v.index()); + + Visitor visitor; + v.Visit([&visitor](const auto& value) { visitor.Visit(value); }); + EXPECT_TRUE(visitor.empty_value); + } + + { + Variant<std::string> v("test"); + ASSERT_TRUE(v.is<std::string>()); + EXPECT_FALSE(std::get<std::string>(v).empty()); + + v.Visit([](auto&& value) { + std::remove_reference_t<decltype(value)> empty; + std::swap(empty, value); + }); + ASSERT_TRUE(v.is<std::string>()); + EXPECT_TRUE(std::get<std::string>(v).empty()); + } +} + +TEST(Variant, Become) { + { + Variant<int, bool, float> v; + + v.Become(0); + EXPECT_TRUE(v.is<int>()); + + v.Become(1); + EXPECT_TRUE(v.is<bool>()); + + v.Become(2); + EXPECT_TRUE(v.is<float>()); + + v.Become(3); + EXPECT_TRUE(v.empty()); + + v.Become(-1); + EXPECT_TRUE(v.empty()); + + v.Become(-2); + EXPECT_TRUE(v.empty()); + } + + { + Variant<int, bool, float> v; + + v.Become(0, 10); + ASSERT_TRUE(v.is<int>()); + EXPECT_EQ(10, std::get<int>(v)); + + v.Become(1, true); + ASSERT_TRUE(v.is<bool>()); + EXPECT_EQ(true, std::get<bool>(v)); + + v.Become(2, 2.0f); + ASSERT_TRUE(v.is<float>()); + EXPECT_FLOAT_EQ(2.0f, std::get<float>(v)); + + v.Become(3, 10); + EXPECT_TRUE(v.empty()); + + v.Become(-1, 10); + EXPECT_TRUE(v.empty()); + + v.Become(-2, 20); + EXPECT_TRUE(v.empty()); + } + + { + Variant<std::string> v; + + v.Become(0); + ASSERT_TRUE(v.is<std::string>()); + EXPECT_TRUE(std::get<std::string>(v).empty()); + } + + { + Variant<std::string> v; + + v.Become(0, "test"); + ASSERT_TRUE(v.is<std::string>()); + EXPECT_EQ("test", std::get<std::string>(v)); + } + + { + Variant<std::string> v("foo"); + + v.Become(0, "bar"); + ASSERT_TRUE(v.is<std::string>()); + EXPECT_EQ("foo", std::get<std::string>(v)); + } +} + +TEST(Variant, Swap) { + { + Variant<std::string> a; + Variant<std::string> b; + + std::swap(a, b); + EXPECT_TRUE(a.empty()); + EXPECT_TRUE(b.empty()); + } + + { + Variant<std::string> a("1"); + Variant<std::string> b; + + std::swap(a, b); + EXPECT_TRUE(!a.empty()); + EXPECT_TRUE(!b.empty()); + ASSERT_TRUE(b.is<std::string>()); + EXPECT_EQ("1", std::get<std::string>(b)); + } + + { + Variant<std::string> a; + Variant<std::string> b("1"); + + std::swap(a, b); + EXPECT_TRUE(!a.empty()); + EXPECT_TRUE(!b.empty()); + ASSERT_TRUE(a.is<std::string>()); + EXPECT_EQ("1", std::get<std::string>(a)); + } + + { + Variant<std::string> a("1"); + Variant<std::string> b("2"); + + std::swap(a, b); + ASSERT_TRUE(a.is<std::string>()); + ASSERT_TRUE(b.is<std::string>()); + EXPECT_EQ("2", std::get<std::string>(a)); + EXPECT_EQ("1", std::get<std::string>(b)); + } + + { + Variant<int, std::string> a(10); + Variant<int, std::string> b("1"); + + std::swap(a, b); + ASSERT_TRUE(a.is<std::string>()); + ASSERT_TRUE(b.is<int>()); + EXPECT_EQ("1", std::get<std::string>(a)); + EXPECT_EQ(10, std::get<int>(b)); + } + + { + Variant<int, std::string> a("1"); + Variant<int, std::string> b(10); + + std::swap(a, b); + ASSERT_TRUE(a.is<int>()); + ASSERT_TRUE(b.is<std::string>()); + EXPECT_EQ(10, std::get<int>(a)); + EXPECT_EQ("1", std::get<std::string>(b)); + } +} + +TEST(Variant, Get) { + { + Variant<int, bool, float, int> v; + + EXPECT_EQ(nullptr, &std::get<int>(v)); + EXPECT_EQ(nullptr, &std::get<bool>(v)); + EXPECT_EQ(nullptr, &std::get<float>(v)); + EXPECT_EQ(nullptr, &std::get<0>(v)); + EXPECT_EQ(nullptr, &std::get<1>(v)); + EXPECT_EQ(nullptr, &std::get<2>(v)); + EXPECT_EQ(nullptr, &std::get<3>(v)); + } + + { + Variant<int, bool, float, int> v; + v = 9; + ASSERT_TRUE(v.is<int>()) + << "Expected type " << v.index_of<int>() << " got type " << v.index(); + EXPECT_EQ(9, std::get<int>(v)); + EXPECT_EQ(9, std::get<0>(v)); + + std::get<int>(v) = 10; + EXPECT_EQ(10, std::get<int>(v)); + EXPECT_EQ(10, std::get<0>(v)); + + std::get<0>(v) = 11; + EXPECT_EQ(11, std::get<int>(v)); + EXPECT_EQ(11, std::get<0>(v)); + + std::get<3>(v) = 12; + EXPECT_EQ(12, std::get<int>(v)); + EXPECT_EQ(12, std::get<3>(v)); + } + + { + Variant<int, bool, float, int> v; + v = false; + ASSERT_TRUE(v.is<bool>()) + << "Expected type " << v.index_of<bool>() << " got type " << v.index(); + EXPECT_EQ(false, std::get<bool>(v)); + EXPECT_EQ(false, std::get<1>(v)); + + std::get<bool>(v) = true; + EXPECT_EQ(true, std::get<bool>(v)); + EXPECT_EQ(true, std::get<1>(v)); + + std::get<bool>(v) = false; + EXPECT_EQ(false, std::get<bool>(v)); + EXPECT_EQ(false, std::get<1>(v)); + + std::get<1>(v) = true; + EXPECT_EQ(true, std::get<bool>(v)); + EXPECT_EQ(true, std::get<1>(v)); + + std::get<1>(v) = false; + EXPECT_EQ(false, std::get<bool>(v)); + EXPECT_EQ(false, std::get<1>(v)); + } + + { + Variant<int, bool, float, int> v; + v = 1.0f; + ASSERT_TRUE(v.is<float>()) + << "Expected type " << v.index_of<float>() << " got type " << v.index(); + EXPECT_EQ(2, v.index()); + EXPECT_FLOAT_EQ(1.0, std::get<float>(v)); + EXPECT_FLOAT_EQ(1.0, std::get<2>(v)); + + std::get<float>(v) = 1.1; + EXPECT_FLOAT_EQ(1.1, std::get<float>(v)); + EXPECT_FLOAT_EQ(1.1, std::get<2>(v)); + + std::get<float>(v) = -3.0; + EXPECT_FLOAT_EQ(-3.0, std::get<float>(v)); + EXPECT_FLOAT_EQ(-3.0, std::get<2>(v)); + + std::get<2>(v) = 1.1; + EXPECT_FLOAT_EQ(1.1, std::get<float>(v)); + EXPECT_FLOAT_EQ(1.1, std::get<2>(v)); + + std::get<2>(v) = -3.0; + EXPECT_FLOAT_EQ(-3.0, std::get<float>(v)); + EXPECT_FLOAT_EQ(-3.0, std::get<2>(v)); + } + + { + Variant<std::unique_ptr<int>> v(std::make_unique<int>(10)); + std::unique_ptr<int> pointer = std::move(std::get<std::unique_ptr<int>>(v)); + ASSERT_FALSE(v.empty()); + EXPECT_TRUE(pointer != nullptr); + EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) == nullptr); + } + + { + Variant<std::string> v("test"); + std::string s = std::get<std::string>(std::move(v)); + EXPECT_EQ("test", s); + } +} + +TEST(Variant, IfAnyOf) { + { + Variant<int, float> v(10); + ASSERT_TRUE(v.is<int>()); + + bool b = false; + EXPECT_TRUE(IfAnyOf<int>::Get(&v, &b)); + EXPECT_TRUE(b); + + float f = 0.0f; + EXPECT_TRUE((IfAnyOf<int, float>::Get(&v, &f))); + EXPECT_FLOAT_EQ(10.f, f); + } + + { + const Variant<int, float> v(10); + ASSERT_TRUE(v.is<int>()); + + bool b = false; + EXPECT_TRUE(IfAnyOf<int>::Get(&v, &b)); + EXPECT_TRUE(b); + + float f = 0.0f; + EXPECT_TRUE((IfAnyOf<int, float>::Get(&v, &f))); + EXPECT_FLOAT_EQ(10.f, f); + } + + { + Variant<int, float> v(10); + ASSERT_TRUE(v.is<int>()); + + bool b = false; + EXPECT_TRUE(IfAnyOf<int>::Call(&v, [&b](const auto& value) { b = value; })); + EXPECT_TRUE(b); + + float f = 0.0f; + EXPECT_TRUE(( + IfAnyOf<int, float>::Call(&v, [&f](const auto& value) { f = value; }))); + EXPECT_FLOAT_EQ(10.f, f); + } + + { + Variant<std::unique_ptr<int>, int> v(std::make_unique<int>(10)); + ASSERT_TRUE(v.is<std::unique_ptr<int>>()); + const int* original_v = std::get<std::unique_ptr<int>>(v).get(); + + std::unique_ptr<int> u(std::make_unique<int>(20)); + + EXPECT_TRUE(IfAnyOf<std::unique_ptr<int>>::Take(&v, &u)); + ASSERT_TRUE(v.is<std::unique_ptr<int>>()); + EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) == nullptr); + EXPECT_EQ(u.get(), original_v); + } + + { + Variant<std::unique_ptr<DerivedType>, int> v( + std::make_unique<DerivedType>(10)); + ASSERT_TRUE(v.is<std::unique_ptr<DerivedType>>()); + const DerivedType* original_v = + std::get<std::unique_ptr<DerivedType>>(v).get(); + + std::unique_ptr<BaseType> u(std::make_unique<BaseType>(20)); + + EXPECT_TRUE(IfAnyOf<std::unique_ptr<DerivedType>>::Take(&v, &u)); + ASSERT_TRUE(v.is<std::unique_ptr<DerivedType>>()); + EXPECT_TRUE(std::get<std::unique_ptr<DerivedType>>(v) == nullptr); + EXPECT_EQ(u.get(), original_v); + } + + { + Variant<std::unique_ptr<int>, int> v(std::make_unique<int>(10)); + ASSERT_TRUE(v.is<std::unique_ptr<int>>()); + const int* original_v = std::get<std::unique_ptr<int>>(v).get(); + + std::unique_ptr<int> u(std::make_unique<int>(20)); + + EXPECT_TRUE(IfAnyOf<std::unique_ptr<int>>::Call( + &v, [&u](auto&& value) { u = std::move(value); })); + ASSERT_TRUE(v.is<std::unique_ptr<int>>()); + EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) == nullptr); + EXPECT_EQ(u.get(), original_v); + } + + { + Variant<int, bool, float> v(true); + ASSERT_TRUE(v.is<bool>()); + + float f = 0.f; + EXPECT_FALSE((IfAnyOf<int, float>::Get(&v, &f))); + EXPECT_FLOAT_EQ(0.f, f); + } + + { + Variant<std::string, int> v("foo"); + ASSERT_TRUE(v.is<std::string>()); + + std::string s = "bar"; + EXPECT_TRUE(IfAnyOf<std::string>::Swap(&v, &s)); + ASSERT_TRUE(v.is<std::string>()); + EXPECT_EQ("bar", std::get<std::string>(v)); + EXPECT_EQ("foo", s); + } + + { + Variant<std::string, const char*> v(static_cast<const char*>("foo")); + ASSERT_TRUE(v.is<const char*>()); + + std::string s = "bar"; + EXPECT_TRUE((IfAnyOf<std::string, const char*>::Take(&v, &s))); + ASSERT_TRUE(v.is<const char*>()); + EXPECT_EQ("foo", std::get<const char*>(v)); + EXPECT_EQ("foo", s); + + v = std::string("bar"); + ASSERT_TRUE(v.is<std::string>()); + + EXPECT_TRUE((IfAnyOf<std::string, const char*>::Take(&v, &s))); + ASSERT_TRUE(v.is<std::string>()); + EXPECT_EQ("bar", s); + } + + { + Variant<std::string, const char*> v; + ASSERT_TRUE(v.empty()); + + std::string s = "bar"; + EXPECT_FALSE((IfAnyOf<std::string, const char*>::Take(&v, &s))); + EXPECT_EQ("bar", s); + } + + { + Variant<std::string, const char*> v(static_cast<const char*>("test")); + ASSERT_TRUE(v.is<const char*>()); + + std::string s; + EXPECT_FALSE(IfAnyOf<>::Take(&v, &s)); + EXPECT_TRUE(s.empty()); + } +} + +TEST(Variant, ConstVolatile) { + { + Variant<const int> v(10); + ASSERT_TRUE(v.is<const int>()); + EXPECT_EQ(10, std::get<const int>(v)); + } + + { + Variant<const std::string> v("test"); + ASSERT_TRUE(v.is<const std::string>()); + EXPECT_EQ("test", std::get<const std::string>(v)); + } + + { + Variant<volatile int, std::string> v(10); + ASSERT_TRUE(v.is<volatile int>()); + EXPECT_EQ(10, std::get<volatile int>(v)); + } +} + +TEST(Variant, HasType) { + EXPECT_TRUE((detail::HasType<int, int, float, bool>::value)); + EXPECT_FALSE((detail::HasType<char, int, float, bool>::value)); + EXPECT_FALSE(detail::HasType<>::value); + + EXPECT_TRUE((detail::HasType<int&, int, float, bool>::value)); + EXPECT_FALSE((detail::HasType<char&, int, float, bool>::value)); +} + +TEST(Variant, Set) { + EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<int, bool, + float>::value)); + EXPECT_TRUE( + (detail::Set<int, bool, float>::template IsSubset<bool, float>::value)); + EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<float>::value)); + EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<>::value)); + + EXPECT_FALSE( + (detail::Set<int, bool, float>::template IsSubset<int, bool, float, + char>::value)); + EXPECT_FALSE((detail::Set<int, bool, float>::template IsSubset<bool, float, + char>::value)); + EXPECT_FALSE( + (detail::Set<int, bool, float>::template IsSubset<float, char>::value)); + EXPECT_FALSE((detail::Set<int, bool, float>::template IsSubset<char>::value)); + + EXPECT_TRUE(detail::Set<>::template IsSubset<>::value); + EXPECT_FALSE(detail::Set<>::template IsSubset<int>::value); + EXPECT_FALSE((detail::Set<>::template IsSubset<int, float>::value)); +} diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp new file mode 100644 index 0000000000..8cfa86fa44 --- /dev/null +++ b/libs/vr/libpdx_default_transport/Android.bp @@ -0,0 +1,70 @@ +cc_defaults { + name: "pdx_default_transport_compiler_defaults", + clang: true, + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], +} + +cc_defaults { + name: "pdx_default_transport_lib_defaults", + export_include_dirs: ["private"], + whole_static_libs: ["libpdx"], +} + +cc_defaults { + name: "pdx_use_transport_servicefs", + export_include_dirs: ["private/servicefs"], + whole_static_libs: ["libpdx_servicefs", "libservicefs"], +} + +cc_defaults { + name: "pdx_use_transport_uds", + export_include_dirs: ["private/uds"], + whole_static_libs: ["libpdx_uds"], +} + +cc_library_static { + name: "libpdx_default_transport", + defaults: [ + "pdx_default_transport_compiler_defaults", + "pdx_default_transport_lib_defaults", + "pdx_use_transport_uds", + ], +} + +cc_binary { + name: "servicetool", + defaults: ["pdx_default_transport_compiler_defaults"], + srcs: [ + "servicetool.cpp", + ], + shared_libs: [ + "liblog", + ], + static_libs: [ + "libpdx_default_transport", + ], +} + +// Benchmarks. +cc_binary { + name: "pdx_benchmarks", + defaults: ["pdx_default_transport_compiler_defaults"], + srcs: [ + "pdx_benchmarks.cpp", + ], + shared_libs: [ + "libbase", + "libchrome", + "libcutils", + "liblog", + "libutils", + ], + static_libs: [ + "libpdx_default_transport", + ], +} + diff --git a/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp b/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp new file mode 100644 index 0000000000..fa0adf0182 --- /dev/null +++ b/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp @@ -0,0 +1,1090 @@ +// Use ALWAYS at the tag level. Control is performed manually during command +// line processing. +#define ATRACE_TAG ATRACE_TAG_ALWAYS +#include <utils/Trace.h> + +#include <base/files/file_util.h> +#include <base/logging.h> +#include <base/strings/string_split.h> +#include <errno.h> +#include <getopt.h> +#include <pdx/client.h> +#include <pdx/default_transport/client_channel_factory.h> +#include <pdx/default_transport/service_endpoint.h> +#include <pdx/rpc/buffer_wrapper.h> +#include <pdx/rpc/default_initialization_allocator.h> +#include <pdx/rpc/message_buffer.h> +#include <pdx/rpc/remote_method.h> +#include <pdx/rpc/serializable.h> +#include <pdx/service.h> +#include <sys/prctl.h> +#include <time.h> +#include <unistd.h> + +#include <atomic> +#include <cstdlib> +#include <functional> +#include <future> +#include <iomanip> +#include <ios> +#include <iostream> +#include <memory> +#include <numeric> +#include <sstream> +#include <string> +#include <thread> +#include <vector> + +using android::pdx::Channel; +using android::pdx::ClientBase; +using android::pdx::Endpoint; +using android::pdx::ErrorStatus; +using android::pdx::Message; +using android::pdx::Service; +using android::pdx::ServiceBase; +using android::pdx::default_transport::ClientChannelFactory; +using android::pdx::Status; +using android::pdx::Transaction; +using android::pdx::rpc::BufferWrapper; +using android::pdx::rpc::DefaultInitializationAllocator; +using android::pdx::rpc::MessageBuffer; +using android::pdx::rpc::DispatchRemoteMethod; +using android::pdx::rpc::RemoteMethodReturn; +using android::pdx::rpc::ReplyBuffer; +using android::pdx::rpc::Void; +using android::pdx::rpc::WrapBuffer; + +namespace { + +constexpr size_t kMaxMessageSize = 4096 * 1024; + +std::string GetServicePath(const std::string& path, int instance_id) { + return path + std::to_string(instance_id); +} + +void SetThreadName(const std::string& name) { + prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name.c_str()), 0, 0, 0); +} + +constexpr uint64_t kNanosPerSecond = 1000000000llu; + +uint64_t GetClockNs() { + timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + return kNanosPerSecond * t.tv_sec + t.tv_nsec; +} + +template <typename T> +ssize_t ssizeof(const T&) { + return static_cast<ssize_t>(sizeof(T)); +} + +class SchedStats { + public: + SchedStats() : SchedStats(gettid()) {} + SchedStats(pid_t task_id) : task_id_(task_id) {} + SchedStats(const SchedStats&) = default; + SchedStats& operator=(const SchedStats&) = default; + + void Update() { + const std::string stats_path = + "/proc/" + std::to_string(task_id_) + "/schedstat"; + + std::string line; + base::ReadFileToString(base::FilePath{stats_path}, &line); + std::vector<std::string> stats = base::SplitString( + line, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + + CHECK_EQ(3u, stats.size()); + + // Calculate the deltas since the last update. Each value is absolute since + // the task started. + uint64_t current_cpu_time_ns = std::stoull(stats[0]); + uint64_t current_wait_ns = std::stoull(stats[1]); + uint64_t current_timeslices = std::stoull(stats[2]); + cpu_time_ns_ = current_cpu_time_ns - last_cpu_time_ns_; + wait_ns_ = current_wait_ns - last_wait_ns_; + timeslices_ = current_timeslices - last_timeslices_; + last_cpu_time_ns_ = current_cpu_time_ns; + last_wait_ns_ = current_wait_ns; + last_timeslices_ = current_timeslices; + } + + pid_t task_id() const { return task_id_; } + uint64_t cpu_time_ns() const { return cpu_time_ns_; } + uint64_t wait_ns() const { return wait_ns_; } + uint64_t timeslices() const { return timeslices_; } + + double cpu_time_s() const { + return static_cast<double>(cpu_time_ns_) / kNanosPerSecond; + } + double wait_s() const { + return static_cast<double>(wait_ns_) / kNanosPerSecond; + } + + private: + int32_t task_id_; + uint64_t cpu_time_ns_ = 0; + uint64_t last_cpu_time_ns_ = 0; + uint64_t wait_ns_ = 0; + uint64_t last_wait_ns_ = 0; + uint64_t timeslices_ = 0; + uint64_t last_timeslices_ = 0; + + PDX_SERIALIZABLE_MEMBERS(SchedStats, task_id_, cpu_time_ns_, wait_ns_, + timeslices_); +}; + +// Opcodes for client/service protocol. +struct BenchmarkOps { + enum : int { + Nop, + Read, + Write, + Echo, + Stats, + WriteVector, + EchoVector, + Quit, + }; +}; + +struct BenchmarkRPC { + PDX_REMOTE_METHOD(Stats, BenchmarkOps::Stats, + std::tuple<uint64_t, uint64_t, SchedStats>(Void)); + PDX_REMOTE_METHOD(WriteVector, BenchmarkOps::WriteVector, + int(const BufferWrapper<std::vector<uint8_t>> data)); + PDX_REMOTE_METHOD(EchoVector, BenchmarkOps::EchoVector, + BufferWrapper<std::vector<uint8_t>>( + const BufferWrapper<std::vector<uint8_t>> data)); +}; + +struct BenchmarkResult { + int thread_id = 0; + int service_id = 0; + double time_delta_s = 0.0; + uint64_t bytes_sent = 0; + SchedStats sched_stats = {}; +}; + +// Global command line option values. +struct Options { + bool verbose = false; + int threads = 1; + int opcode = BenchmarkOps::Read; + int blocksize = 1; + int count = 1; + int instances = 1; + int timeout = 1; + int warmup = 0; +} ProgramOptions; + +// Command line option names. +const char kOptionService[] = "service"; +const char kOptionClient[] = "client"; +const char kOptionVerbose[] = "verbose"; +const char kOptionOpcode[] = "op"; +const char kOptionBlocksize[] = "bs"; +const char kOptionCount[] = "count"; +const char kOptionThreads[] = "threads"; +const char kOptionInstances[] = "instances"; +const char kOptionTimeout[] = "timeout"; +const char kOptionTrace[] = "trace"; +const char kOptionWarmup[] = "warmup"; + +// getopt() long options. +static option long_options[] = { + {kOptionService, required_argument, 0, 0}, + {kOptionClient, required_argument, 0, 0}, + {kOptionVerbose, no_argument, 0, 0}, + {kOptionOpcode, required_argument, 0, 0}, + {kOptionBlocksize, required_argument, 0, 0}, + {kOptionCount, required_argument, 0, 0}, + {kOptionThreads, required_argument, 0, 0}, + {kOptionInstances, required_argument, 0, 0}, + {kOptionTimeout, required_argument, 0, 0}, + {kOptionTrace, no_argument, 0, 0}, + {kOptionWarmup, required_argument, 0, 0}, + {0, 0, 0, 0}, +}; + +// Parses the argument for kOptionOpcode and sets the value of +// ProgramOptions.opcode. +void ParseOpcodeOption(const std::string& argument) { + if (argument == "read") { + ProgramOptions.opcode = BenchmarkOps::Read; + } else if (argument == "write") { + ProgramOptions.opcode = BenchmarkOps::Write; + } else if (argument == "echo") { + ProgramOptions.opcode = BenchmarkOps::Echo; + } else if (argument == "writevec") { + ProgramOptions.opcode = BenchmarkOps::WriteVector; + } else if (argument == "echovec") { + ProgramOptions.opcode = BenchmarkOps::EchoVector; + } else if (argument == "quit") { + ProgramOptions.opcode = BenchmarkOps::Quit; + } else if (argument == "nop") { + ProgramOptions.opcode = BenchmarkOps::Nop; + } else if (argument == "stats") { + ProgramOptions.opcode = BenchmarkOps::Stats; + } else { + ProgramOptions.opcode = std::stoi(argument); + } +} + +// Implements the service side of the benchmark. +class BenchmarkService : public ServiceBase<BenchmarkService> { + public: + std::shared_ptr<Channel> OnChannelOpen(Message& message) override { + VLOG(1) << "BenchmarkService::OnChannelCreate: cid=" + << message.GetChannelId(); + return nullptr; + } + + void OnChannelClose(Message& message, + const std::shared_ptr<Channel>& /*channel*/) override { + VLOG(1) << "BenchmarkService::OnChannelClose: cid=" + << message.GetChannelId(); + } + + Status<void> HandleMessage(Message& message) override { + ATRACE_NAME("BenchmarkService::HandleMessage"); + + switch (message.GetOp()) { + case BenchmarkOps::Nop: + VLOG(1) << "BenchmarkService::HandleMessage: op=nop"; + { + ATRACE_NAME("Reply"); + CHECK(message.Reply(0)); + } + return {}; + + case BenchmarkOps::Write: { + VLOG(1) << "BenchmarkService::HandleMessage: op=write send_length=" + << message.GetSendLength() + << " receive_length=" << message.GetReceiveLength(); + + Status<void> status; + if (message.GetSendLength()) + status = message.ReadAll(send_buffer.data(), message.GetSendLength()); + + { + ATRACE_NAME("Reply"); + if (!status) + CHECK(message.ReplyError(status.error())); + else + CHECK(message.Reply(message.GetSendLength())); + } + return {}; + } + + case BenchmarkOps::Read: { + VLOG(1) << "BenchmarkService::HandleMessage: op=read send_length=" + << message.GetSendLength() + << " receive_length=" << message.GetReceiveLength(); + + Status<void> status; + if (message.GetReceiveLength()) { + status = message.WriteAll(receive_buffer.data(), + message.GetReceiveLength()); + } + + { + ATRACE_NAME("Reply"); + if (!status) + CHECK(message.ReplyError(status.error())); + else + CHECK(message.Reply(message.GetReceiveLength())); + } + return {}; + } + + case BenchmarkOps::Echo: { + VLOG(1) << "BenchmarkService::HandleMessage: op=echo send_length=" + << message.GetSendLength() + << " receive_length=" << message.GetReceiveLength(); + + Status<void> status; + if (message.GetSendLength()) + status = message.ReadAll(send_buffer.data(), message.GetSendLength()); + + if (!status) { + CHECK(message.ReplyError(status.error())); + return {}; + } + + if (message.GetSendLength()) { + status = + message.WriteAll(send_buffer.data(), message.GetSendLength()); + } + + { + ATRACE_NAME("Reply"); + if (!status) + CHECK(message.ReplyError(status.error())); + else + CHECK(message.Reply(message.GetSendLength())); + } + return {}; + } + + case BenchmarkOps::Stats: { + VLOG(1) << "BenchmarkService::HandleMessage: op=echo send_length=" + << message.GetSendLength() + << " receive_length=" << message.GetReceiveLength(); + + // Snapshot the stats when the message is received. + const uint64_t receive_time_ns = GetClockNs(); + sched_stats_.Update(); + + // Use the RPC system to return the results. + RemoteMethodReturn<BenchmarkRPC::Stats>( + message, BenchmarkRPC::Stats::Return{receive_time_ns, GetClockNs(), + sched_stats_}); + return {}; + } + + case BenchmarkOps::WriteVector: + VLOG(1) << "BenchmarkService::HandleMessage: op=writevec send_length=" + << message.GetSendLength() + << " receive_length=" << message.GetReceiveLength(); + + DispatchRemoteMethod<BenchmarkRPC::WriteVector>( + *this, &BenchmarkService::OnWriteVector, message, kMaxMessageSize); + return {}; + + case BenchmarkOps::EchoVector: + VLOG(1) << "BenchmarkService::HandleMessage: op=echovec send_length=" + << message.GetSendLength() + << " receive_length=" << message.GetReceiveLength(); + + DispatchRemoteMethod<BenchmarkRPC::EchoVector>( + *this, &BenchmarkService::OnEchoVector, message, kMaxMessageSize); + return {}; + + case BenchmarkOps::Quit: + Cancel(); + return ErrorStatus{ESHUTDOWN}; + + default: + VLOG(1) << "BenchmarkService::HandleMessage: default case; op=" + << message.GetOp(); + return Service::DefaultHandleMessage(message); + } + } + + // Updates the scheduler stats from procfs for this thread. + void UpdateSchedStats() { sched_stats_.Update(); } + + private: + friend BASE; + + BenchmarkService(std::unique_ptr<Endpoint> endpoint) + : BASE("BenchmarkService", std::move(endpoint)), + send_buffer(kMaxMessageSize), + receive_buffer(kMaxMessageSize) {} + + std::vector<uint8_t> send_buffer; + std::vector<uint8_t> receive_buffer; + + // Each service thread has its own scheduler stats object. + static thread_local SchedStats sched_stats_; + + using BufferType = BufferWrapper< + std::vector<uint8_t, DefaultInitializationAllocator<uint8_t>>>; + + int OnWriteVector(Message&, const BufferType& data) { return data.size(); } + BufferType OnEchoVector(Message&, BufferType&& data) { + return std::move(data); + } + + BenchmarkService(const BenchmarkService&) = delete; + void operator=(const BenchmarkService&) = delete; +}; + +thread_local SchedStats BenchmarkService::sched_stats_; + +// Implements the client side of the benchmark. +class BenchmarkClient : public ClientBase<BenchmarkClient> { + public: + int Nop() { + ATRACE_NAME("BenchmarkClient::Nop"); + VLOG(1) << "BenchmarkClient::Nop"; + Transaction transaction{*this}; + return ReturnStatusOrError(transaction.Send<int>(BenchmarkOps::Nop)); + } + + int Write(const void* buffer, size_t length) { + ATRACE_NAME("BenchmarkClient::Write"); + VLOG(1) << "BenchmarkClient::Write: buffer=" << buffer + << " length=" << length; + Transaction transaction{*this}; + return ReturnStatusOrError( + transaction.Send<int>(BenchmarkOps::Write, buffer, length, nullptr, 0)); + // return write(endpoint_fd(), buffer, length); + } + + int Read(void* buffer, size_t length) { + ATRACE_NAME("BenchmarkClient::Read"); + VLOG(1) << "BenchmarkClient::Read: buffer=" << buffer + << " length=" << length; + Transaction transaction{*this}; + return ReturnStatusOrError( + transaction.Send<int>(BenchmarkOps::Read, nullptr, 0, buffer, length)); + // return read(endpoint_fd(), buffer, length); + } + + int Echo(const void* send_buffer, size_t send_length, void* receive_buffer, + size_t receive_length) { + ATRACE_NAME("BenchmarkClient::Echo"); + VLOG(1) << "BenchmarkClient::Echo: send_buffer=" << send_buffer + << " send_length=" << send_length + << " receive_buffer=" << receive_buffer + << " receive_length=" << receive_length; + Transaction transaction{*this}; + return ReturnStatusOrError( + transaction.Send<int>(BenchmarkOps::Echo, send_buffer, send_length, + receive_buffer, receive_length)); + } + + int Stats(std::tuple<uint64_t, uint64_t, SchedStats>* stats_out) { + ATRACE_NAME("BenchmarkClient::Stats"); + VLOG(1) << "BenchmarkClient::Stats"; + + auto status = InvokeRemoteMethodInPlace<BenchmarkRPC::Stats>(stats_out); + return status ? 0 : -status.error(); + } + + int WriteVector(const BufferWrapper<std::vector<uint8_t>>& data) { + ATRACE_NAME("BenchmarkClient::Stats"); + VLOG(1) << "BenchmarkClient::Stats"; + + auto status = InvokeRemoteMethod<BenchmarkRPC::WriteVector>(data); + return ReturnStatusOrError(status); + } + + template <typename T> + int WriteVector(const BufferWrapper<T>& data) { + ATRACE_NAME("BenchmarkClient::WriteVector"); + VLOG(1) << "BenchmarkClient::WriteVector"; + + auto status = InvokeRemoteMethod<BenchmarkRPC::WriteVector>(data); + return ReturnStatusOrError(status); + } + + template <typename T, typename U> + int EchoVector(const BufferWrapper<T>& data, BufferWrapper<U>* data_out) { + ATRACE_NAME("BenchmarkClient::EchoVector"); + VLOG(1) << "BenchmarkClient::EchoVector"; + + MessageBuffer<ReplyBuffer>::Reserve(kMaxMessageSize - 1); + auto status = + InvokeRemoteMethodInPlace<BenchmarkRPC::EchoVector>(data_out, data); + return status ? 0 : -status.error(); + } + + int Quit() { + VLOG(1) << "BenchmarkClient::Quit"; + Transaction transaction{*this}; + return ReturnStatusOrError(transaction.Send<int>(BenchmarkOps::Echo)); + } + + private: + friend BASE; + + BenchmarkClient(const std::string& service_path) + : BASE(ClientChannelFactory::Create(service_path), + ProgramOptions.timeout) {} + + BenchmarkClient(const BenchmarkClient&) = delete; + void operator=(const BenchmarkClient&) = delete; +}; + +// Creates a benchmark service at |path| and dispatches messages. +int ServiceCommand(const std::string& path) { + if (path.empty()) + return -EINVAL; + + // Start the requested number of dispatch threads. + std::vector<std::thread> dispatch_threads; + int service_count = ProgramOptions.instances; + int service_id_counter = 0; + int thread_id_counter = 0; + std::atomic<bool> done(false); + + while (service_count--) { + std::cerr << "Starting service instance " << service_id_counter + << std::endl; + auto service = BenchmarkService::Create( + android::pdx::default_transport::Endpoint::CreateAndBindSocket( + GetServicePath(path, service_id_counter), + android::pdx::default_transport::Endpoint::kBlocking)); + if (!service) { + std::cerr << "Failed to create service instance!!" << std::endl; + done = true; + break; + } + + int thread_count = ProgramOptions.threads; + while (thread_count--) { + std::cerr << "Starting dispatch thread " << thread_id_counter + << " service " << service_id_counter << std::endl; + + dispatch_threads.emplace_back( + [&](const int thread_id, const int service_id, + const std::shared_ptr<BenchmarkService>& local_service) { + SetThreadName("service" + std::to_string(service_id)); + + // Read the initial schedstats for this thread from procfs. + local_service->UpdateSchedStats(); + + ATRACE_NAME("BenchmarkService::Dispatch"); + while (!done) { + auto ret = local_service->ReceiveAndDispatch(); + if (!ret) { + if (ret.error() != ESHUTDOWN) { + std::cerr << "Error while dispatching message on thread " + << thread_id << " service " << service_id << ": " + << ret.GetErrorMessage() << std::endl; + } else { + std::cerr << "Quitting thread " << thread_id << " service " + << service_id << std::endl; + } + done = true; + return; + } + } + }, + thread_id_counter++, service_id_counter, service); + } + + service_id_counter++; + } + + // Wait for the dispatch threads to exit. + for (auto& thread : dispatch_threads) { + thread.join(); + } + + return 0; +} + +int ClientCommand(const std::string& path) { + // Start the requested number of client threads. + std::vector<std::thread> client_threads; + std::vector<std::future<BenchmarkResult>> client_results; + int service_count = ProgramOptions.instances; + int thread_id_counter = 0; + int service_id_counter = 0; + + // Aggregate statistics, updated when worker threads exit. + std::atomic<uint64_t> total_bytes(0); + std::atomic<uint64_t> total_time_ns(0); + + // Samples for variance calculation. + std::vector<uint64_t> latency_samples_ns( + ProgramOptions.instances * ProgramOptions.threads * ProgramOptions.count); + const size_t samples_per_thread = ProgramOptions.count; + + std::vector<uint8_t> send_buffer(ProgramOptions.blocksize); + std::vector<uint8_t> receive_buffer(kMaxMessageSize); + + // Barriers for synchronizing thread start. + std::vector<std::future<void>> ready_barrier_futures; + std::promise<void> go_barrier_promise; + std::future<void> go_barrier_future = go_barrier_promise.get_future(); + + // Barrier for synchronizing thread tear down. + std::promise<void> done_barrier_promise; + std::future<void> done_barrier_future = done_barrier_promise.get_future(); + + while (service_count--) { + int thread_count = ProgramOptions.threads; + while (thread_count--) { + std::cerr << "Starting client thread " << thread_id_counter << " service " + << service_id_counter << std::endl; + + std::promise<BenchmarkResult> result_promise; + client_results.push_back(result_promise.get_future()); + + std::promise<void> ready_barrier_promise; + ready_barrier_futures.push_back(ready_barrier_promise.get_future()); + + client_threads.emplace_back( + [&](const int thread_id, const int service_id, + std::promise<BenchmarkResult> result, std::promise<void> ready) { + SetThreadName("client" + std::to_string(thread_id) + "/" + + std::to_string(service_id)); + + ATRACE_NAME("BenchmarkClient::Dispatch"); + + auto client = + BenchmarkClient::Create(GetServicePath(path, service_id)); + if (!client) { + std::cerr << "Failed to create client for service " << service_id + << std::endl; + return -ENOMEM; + } + + uint64_t* thread_samples = + &latency_samples_ns[samples_per_thread * thread_id]; + + // Per-thread statistics. + uint64_t bytes_sent = 0; + uint64_t time_start_ns; + uint64_t time_end_ns; + SchedStats sched_stats; + + // Signal ready and wait for go. + ready.set_value(); + go_barrier_future.wait(); + + // Warmup the scheduler. + int warmup = ProgramOptions.warmup; + while (warmup--) { + for (int i = 0; i < 1000000; i++) + ; + } + + sched_stats.Update(); + time_start_ns = GetClockNs(); + + int count = ProgramOptions.count; + while (count--) { + uint64_t iteration_start_ns = GetClockNs(); + + switch (ProgramOptions.opcode) { + case BenchmarkOps::Nop: { + const int ret = client->Nop(); + if (ret < 0) { + std::cerr << "Failed to send nop: " << strerror(-ret) + << std::endl; + return ret; + } else { + VLOG(1) << "Success"; + } + break; + } + + case BenchmarkOps::Read: { + const int ret = client->Read(receive_buffer.data(), + ProgramOptions.blocksize); + if (ret < 0) { + std::cerr << "Failed to read: " << strerror(-ret) + << std::endl; + return ret; + } else if (ret != ProgramOptions.blocksize) { + std::cerr << "Expected ret=" << ProgramOptions.blocksize + << "; actual ret=" << ret << std::endl; + return -EINVAL; + } else { + VLOG(1) << "Success"; + bytes_sent += ret; + } + break; + } + + case BenchmarkOps::Write: { + const int ret = + client->Write(send_buffer.data(), send_buffer.size()); + if (ret < 0) { + std::cerr << "Failed to write: " << strerror(-ret) + << std::endl; + return ret; + } else if (ret != ProgramOptions.blocksize) { + std::cerr << "Expected ret=" << ProgramOptions.blocksize + << "; actual ret=" << ret << std::endl; + return -EINVAL; + } else { + VLOG(1) << "Success"; + bytes_sent += ret; + } + break; + } + + case BenchmarkOps::Echo: { + const int ret = client->Echo( + send_buffer.data(), send_buffer.size(), + receive_buffer.data(), receive_buffer.size()); + if (ret < 0) { + std::cerr << "Failed to echo: " << strerror(-ret) + << std::endl; + return ret; + } else if (ret != ProgramOptions.blocksize) { + std::cerr << "Expected ret=" << ProgramOptions.blocksize + << "; actual ret=" << ret << std::endl; + return -EINVAL; + } else { + VLOG(1) << "Success"; + bytes_sent += ret * 2; + } + break; + } + + case BenchmarkOps::Stats: { + std::tuple<uint64_t, uint64_t, SchedStats> stats; + const int ret = client->Stats(&stats); + if (ret < 0) { + std::cerr << "Failed to get stats: " << strerror(-ret) + << std::endl; + return ret; + } else { + VLOG(1) << "Success"; + std::cerr + << "Round trip: receive_time_ns=" << std::get<0>(stats) + << " reply_time_ns=" << std::get<1>(stats) + << " cpu_time_s=" << std::get<2>(stats).cpu_time_s() + << " wait_s=" << std::get<2>(stats).wait_s() + << std::endl; + } + break; + } + + case BenchmarkOps::WriteVector: { + const int ret = client->WriteVector( + WrapBuffer(send_buffer.data(), ProgramOptions.blocksize)); + if (ret < 0) { + std::cerr << "Failed to write vector: " << strerror(-ret) + << std::endl; + return ret; + } else { + VLOG(1) << "Success"; + bytes_sent += ret; + } + break; + } + + case BenchmarkOps::EchoVector: { + thread_local BufferWrapper<std::vector< + uint8_t, DefaultInitializationAllocator<uint8_t>>> + response_buffer; + const int ret = client->EchoVector( + WrapBuffer(send_buffer.data(), ProgramOptions.blocksize), + &response_buffer); + if (ret < 0) { + std::cerr << "Failed to echo vector: " << strerror(-ret) + << std::endl; + return ret; + } else { + VLOG(1) << "Success"; + bytes_sent += send_buffer.size() + response_buffer.size(); + } + break; + } + + case BenchmarkOps::Quit: { + const int ret = client->Quit(); + if (ret < 0 && ret != -ESHUTDOWN) { + std::cerr << "Failed to send quit: " << strerror(-ret); + return ret; + } else { + VLOG(1) << "Success"; + } + break; + } + + default: + std::cerr + << "Invalid client operation: " << ProgramOptions.opcode + << std::endl; + return -EINVAL; + } + + uint64_t iteration_end_ns = GetClockNs(); + uint64_t iteration_delta_ns = + iteration_end_ns - iteration_start_ns; + thread_samples[count] = iteration_delta_ns; + + if (iteration_delta_ns > (kNanosPerSecond / 100)) { + SchedStats stats = sched_stats; + stats.Update(); + std::cerr << "Thread " << thread_id << " iteration_delta_s=" + << (static_cast<double>(iteration_delta_ns) / + kNanosPerSecond) + << " " << stats.cpu_time_s() << " " << stats.wait_s() + << std::endl; + } + } + + time_end_ns = GetClockNs(); + sched_stats.Update(); + + const double time_delta_s = + static_cast<double>(time_end_ns - time_start_ns) / + kNanosPerSecond; + + total_bytes += bytes_sent; + total_time_ns += time_end_ns - time_start_ns; + + result.set_value( + {thread_id, service_id, time_delta_s, bytes_sent, sched_stats}); + done_barrier_future.wait(); + + return 0; + }, + thread_id_counter++, service_id_counter, std::move(result_promise), + std::move(ready_barrier_promise)); + } + + service_id_counter++; + } + + // Wait for workers to be ready. + std::cerr << "Waiting for workers to be ready..." << std::endl; + for (auto& ready : ready_barrier_futures) + ready.wait(); + + // Signal workers to go. + std::cerr << "Kicking off benchmark." << std::endl; + go_barrier_promise.set_value(); + + // Wait for all the worker threas to finish. + for (auto& result : client_results) + result.wait(); + + // Report worker thread results. + for (auto& result : client_results) { + BenchmarkResult benchmark_result = result.get(); + std::cerr << std::fixed << "Thread " << benchmark_result.thread_id + << " service " << benchmark_result.service_id << ":" << std::endl; + std::cerr << "\t " << benchmark_result.bytes_sent << " bytes in " + << benchmark_result.time_delta_s << " seconds (" + << std::setprecision(0) << (benchmark_result.bytes_sent / 1024.0 / + benchmark_result.time_delta_s) + << " K/s; " << std::setprecision(3) + << (ProgramOptions.count / benchmark_result.time_delta_s) + << " txn/s; " << std::setprecision(9) + << (benchmark_result.time_delta_s / ProgramOptions.count) + << " s/txn)" << std::endl; + std::cerr << "\tStats: " << benchmark_result.sched_stats.cpu_time_s() << " " + << (benchmark_result.sched_stats.cpu_time_s() / + ProgramOptions.count) + << " " << benchmark_result.sched_stats.wait_s() << " " + << (benchmark_result.sched_stats.wait_s() / ProgramOptions.count) + << " " << benchmark_result.sched_stats.timeslices() << std::endl; + } + + // Signal worker threads to exit. + done_barrier_promise.set_value(); + + // Wait for the worker threads to exit. + for (auto& thread : client_threads) { + thread.join(); + } + + // Report aggregate results. + const int total_threads = ProgramOptions.threads * ProgramOptions.instances; + const int iterations = ProgramOptions.count; + const double total_time_s = + static_cast<double>(total_time_ns) / kNanosPerSecond; + // This is about how much wall time it took to completely transfer all the + // paylaods. + const double average_time_s = total_time_s / total_threads; + + const uint64_t min_sample_time_ns = + *std::min_element(latency_samples_ns.begin(), latency_samples_ns.end()); + const double min_sample_time_s = + static_cast<double>(min_sample_time_ns) / kNanosPerSecond; + + const uint64_t max_sample_time_ns = + *std::max_element(latency_samples_ns.begin(), latency_samples_ns.end()); + const double max_sample_time_s = + static_cast<double>(max_sample_time_ns) / kNanosPerSecond; + + const double total_sample_time_s = + std::accumulate(latency_samples_ns.begin(), latency_samples_ns.end(), 0.0, + [](double s, uint64_t ns) { + return s + static_cast<double>(ns) / kNanosPerSecond; + }); + const double average_sample_time_s = + total_sample_time_s / latency_samples_ns.size(); + + const double sum_of_squared_deviations = std::accumulate( + latency_samples_ns.begin(), latency_samples_ns.end(), 0.0, + [&](double s, uint64_t ns) { + const double delta = + static_cast<double>(ns) / kNanosPerSecond - average_sample_time_s; + return s + delta * delta; + }); + const double variance = sum_of_squared_deviations / latency_samples_ns.size(); + const double standard_deviation = std::sqrt(variance); + + const int num_buckets = 200; + const uint64_t sample_range_ns = max_sample_time_ns - min_sample_time_ns; + const uint64_t ns_per_bucket = sample_range_ns / num_buckets; + std::array<uint64_t, num_buckets> sample_buckets = {{0}}; + + // Count samples in each bucket range. + for (uint64_t sample_ns : latency_samples_ns) { + sample_buckets[(sample_ns - min_sample_time_ns) / (ns_per_bucket + 1)] += 1; + } + + // Calculate population percentiles. + const uint64_t percent_50 = + static_cast<uint64_t>(latency_samples_ns.size() * 0.5); + const uint64_t percent_90 = + static_cast<uint64_t>(latency_samples_ns.size() * 0.9); + const uint64_t percent_95 = + static_cast<uint64_t>(latency_samples_ns.size() * 0.95); + const uint64_t percent_99 = + static_cast<uint64_t>(latency_samples_ns.size() * 0.99); + + uint64_t sample_count = 0; + double latency_50th_percentile_s, latency_90th_percentile_s, + latency_95th_percentile_s, latency_99th_percentile_s; + for (int i = 0; i < num_buckets; i++) { + // Report the midpoint of the bucket range as the value of the + // corresponding + // percentile. + const double bucket_midpoint_time_s = + (ns_per_bucket * i + 0.5 * ns_per_bucket + min_sample_time_ns) / + kNanosPerSecond; + if (sample_count < percent_50 && + (sample_count + sample_buckets[i]) >= percent_50) { + latency_50th_percentile_s = bucket_midpoint_time_s; + } + if (sample_count < percent_90 && + (sample_count + sample_buckets[i]) >= percent_90) { + latency_90th_percentile_s = bucket_midpoint_time_s; + } + if (sample_count < percent_95 && + (sample_count + sample_buckets[i]) >= percent_95) { + latency_95th_percentile_s = bucket_midpoint_time_s; + } + if (sample_count < percent_99 && + (sample_count + sample_buckets[i]) >= percent_99) { + latency_99th_percentile_s = bucket_midpoint_time_s; + } + sample_count += sample_buckets[i]; + } + + std::cerr << std::fixed << "Total throughput over " << total_threads + << " threads:\n\t " << total_bytes << " bytes in " << average_time_s + << " seconds (" << std::setprecision(0) + << (total_bytes / 1024.0 / average_time_s) << " K/s; " + << std::setprecision(3) + << (iterations * total_threads / average_time_s) + << std::setprecision(9) << " txn/s; " + << (average_time_s / (iterations * total_threads)) << " s/txn)" + << std::endl; + std::cerr << "Sample statistics: " << std::endl; + std::cerr << total_sample_time_s << " s total sample time" << std::endl; + std::cerr << average_sample_time_s << " s avg" << std::endl; + std::cerr << standard_deviation << " s std dev" << std::endl; + std::cerr << min_sample_time_s << " s min" << std::endl; + std::cerr << max_sample_time_s << " s max" << std::endl; + std::cerr << "Latency percentiles:" << std::endl; + std::cerr << "50th: " << latency_50th_percentile_s << " s" << std::endl; + std::cerr << "90th: " << latency_90th_percentile_s << " s" << std::endl; + std::cerr << "95th: " << latency_95th_percentile_s << " s" << std::endl; + std::cerr << "99th: " << latency_99th_percentile_s << " s" << std::endl; + + std::cout << total_time_ns << " " << std::fixed << std::setprecision(9) + << average_sample_time_s << " " << std::fixed + << std::setprecision(9) << standard_deviation << std::endl; + return 0; +} + +int Usage(const std::string& command_name) { + // clang-format off + std::cout << "Usage: " << command_name << " [options]" << std::endl; + std::cout << "\t--verbose : Use verbose messages." << std::endl; + std::cout << "\t--service <endpoint path> : Start service at the given path." << std::endl; + std::cout << "\t--client <endpoint path> : Start client to the given path." << std::endl; + std::cout << "\t--op <read | write | echo> : Sepcify client operation mode." << std::endl; + std::cout << "\t--bs <block size bytes> : Sepcify block size to use." << std::endl; + std::cout << "\t--count <count> : Sepcify number of transactions to make." << std::endl; + std::cout << "\t--instances <count> : Specify number of service instances." << std::endl; + std::cout << "\t--threads <count> : Sepcify number of threads per instance." << std::endl; + std::cout << "\t--timeout <timeout ms | -1> : Timeout to wait for services." << std::endl; + std::cout << "\t--trace : Enable systrace logging." << std::endl; + std::cout << "\t--warmup <iterations> : Busy loops before running benchmarks." << std::endl; + // clang-format on + return -1; +} + +} // anonymous namespace + +int main(int argc, char** argv) { + logging::LoggingSettings logging_settings; + logging_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; + logging::InitLogging(logging_settings); + + int getopt_code; + int option_index; + std::string option = ""; + std::string command = ""; + std::string command_argument = ""; + bool tracing_enabled = false; + + // Process command line options. + while ((getopt_code = + getopt_long(argc, argv, "", long_options, &option_index)) != -1) { + option = long_options[option_index].name; + VLOG(1) << "option=" << option; + switch (getopt_code) { + case 0: + if (option == kOptionVerbose) { + ProgramOptions.verbose = true; + logging::SetMinLogLevel(-1); + } else if (option == kOptionOpcode) { + ParseOpcodeOption(optarg); + } else if (option == kOptionBlocksize) { + ProgramOptions.blocksize = std::stoi(optarg); + if (ProgramOptions.blocksize < 0) { + std::cerr << "Invalid blocksize argument: " + << ProgramOptions.blocksize << std::endl; + return -EINVAL; + } + } else if (option == kOptionCount) { + ProgramOptions.count = std::stoi(optarg); + if (ProgramOptions.count < 1) { + std::cerr << "Invalid count argument: " << ProgramOptions.count + << std::endl; + return -EINVAL; + } + } else if (option == kOptionThreads) { + ProgramOptions.threads = std::stoi(optarg); + if (ProgramOptions.threads < 1) { + std::cerr << "Invalid threads argument: " << ProgramOptions.threads + << std::endl; + return -EINVAL; + } + } else if (option == kOptionInstances) { + ProgramOptions.instances = std::stoi(optarg); + if (ProgramOptions.instances < 1) { + std::cerr << "Invalid instances argument: " + << ProgramOptions.instances << std::endl; + return -EINVAL; + } + } else if (option == kOptionTimeout) { + ProgramOptions.timeout = std::stoi(optarg); + } else if (option == kOptionTrace) { + tracing_enabled = true; + } else if (option == kOptionWarmup) { + ProgramOptions.warmup = std::stoi(optarg); + } else { + command = option; + if (optarg) + command_argument = optarg; + } + break; + } + } + + // Setup ATRACE/systrace based on command line. + atrace_setup(); + atrace_set_tracing_enabled(tracing_enabled); + + VLOG(1) << "command=" << command << " command_argument=" << command_argument; + + if (command == "") { + return Usage(argv[0]); + } else if (command == kOptionService) { + return ServiceCommand(command_argument); + } else if (command == kOptionClient) { + return ClientCommand(command_argument); + } else { + return Usage(argv[0]); + } +} diff --git a/libs/vr/libpdx_default_transport/private/pdx/default_transport/service_utility.h b/libs/vr/libpdx_default_transport/private/pdx/default_transport/service_utility.h new file mode 100644 index 0000000000..81bb17bd04 --- /dev/null +++ b/libs/vr/libpdx_default_transport/private/pdx/default_transport/service_utility.h @@ -0,0 +1,91 @@ +#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICE_UTILITY_H_ +#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICE_UTILITY_H_ + +#include <ftw.h> + +#include <pdx/client.h> +#include <pdx/default_transport/client_channel_factory.h> +#include <pdx/service.h> +#include <pdx/status.h> + +namespace android { +namespace pdx { +namespace default_transport { + +class ServiceUtility : public ClientBase<ServiceUtility> { + public: + Status<int> ReloadSystemProperties() { + Transaction transaction{*this}; + return ReturnStatusOrError( + transaction.Send<int>(opcodes::REPORT_SYSPROP_CHANGE)); + } + + static std::string GetRootEndpointPath() { + return ClientChannelFactory::GetRootEndpointPath(); + } + static std::string GetEndpointPath(const std::string& endpoint_path) { + return ClientChannelFactory::GetEndpointPath(endpoint_path); + } + + // Traverses the PDX service path space and sends a message to reload system + // properties to each service endpoint it finds along the way. + // NOTE: This method is used by atrace to poke PDX services. Please avoid + // unnecessary changes to this mechanism to minimize impact on atrace. + static bool PokeServices() { + const int kMaxDepth = 16; + const int result = + nftw(GetRootEndpointPath().c_str(), PokeService, kMaxDepth, FTW_PHYS); + return result == 0 ? true : false; + } + + private: + friend BASE; + + ServiceUtility(const std::string& endpoint_path, int* error = nullptr) + : BASE(ClientChannelFactory::Create(endpoint_path), 0) { + if (error) + *error = Client::error(); + } + + // Sends the sysprop_change message to the service at fpath, so it re-reads + // its system properties. Returns 0 on success or a negated errno code on + // failure. + // NOTE: This method is used by atrace to poke PDX services. Please avoid + // unnecessary changes to this mechanism to minimize impact on atrace. + static int PokeService(const char* fpath, const struct stat* /*sb*/, + int typeflag, struct FTW* /*ftwbuf*/) { + const bool kIgnoreErrors = true; + + if (typeflag == FTW_F) { + int error; + auto utility = ServiceUtility::Create(fpath, &error); + if (!utility) { + if (error != -ECONNREFUSED) { + ALOGE("ServiceUtility::PokeService: Failed to open %s: %s.", fpath, + strerror(-error)); + } + return kIgnoreErrors ? 0 : error; + } + + auto status = utility->ReloadSystemProperties(); + if (!status) { + ALOGE( + "ServiceUtility::PokeService: Failed to send sysprop change to %s: " + "%s", + fpath, status.GetErrorMessage().c_str()); + return kIgnoreErrors ? 0 : -status.error(); + } + } + + return 0; + } + + ServiceUtility(const ServiceUtility&) = delete; + void operator=(const ServiceUtility&) = delete; +}; + +} // namespace default_transport +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICE_UTILITY_H_ diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_manager.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_manager.h new file mode 100644 index 0000000000..11163b3c48 --- /dev/null +++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_manager.h @@ -0,0 +1,17 @@ +#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CHANNEL_MANAGER_H_ +#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CHANNEL_MANAGER_H_ + +#include <servicefs/channel_manager.h> + +namespace android { +namespace pdx { +namespace default_transport { + +using ChannelManager = ::android::pdx::servicefs::ChannelManager; + +} // namespace default_transport +} // namespace pdx +} // namespace android + + +#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CHANNEL_MANAGER_H_ diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel.h new file mode 100644 index 0000000000..d1717801bc --- /dev/null +++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel.h @@ -0,0 +1,17 @@ +#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_H_ +#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_H_ + +#include <servicefs/client_channel.h> + +namespace android { +namespace pdx { +namespace default_transport { + +using ClientChannel = ::android::pdx::servicefs::ClientChannel; + +} // namespace default_transport +} // namespace pdx +} // namespace android + + +#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_H_ diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel_factory.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel_factory.h new file mode 100644 index 0000000000..77b5cac2f5 --- /dev/null +++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel_factory.h @@ -0,0 +1,17 @@ +#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_FACTORY_H_ +#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_FACTORY_H_ + +#include <servicefs/client_channel_factory.h> + +namespace android { +namespace pdx { +namespace default_transport { + +using ClientChannelFactory = ::android::pdx::servicefs::ClientChannelFactory; + +} // namespace default_transport +} // namespace pdx +} // namespace android + + +#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_FACTORY_H_ diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h new file mode 100644 index 0000000000..158871c892 --- /dev/null +++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h @@ -0,0 +1,17 @@ +#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_DISPATCHER_H_ +#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_DISPATCHER_H_ + +#include <servicefs/service_dispatcher.h> + +namespace android { +namespace pdx { +namespace default_transport { + +using ServiceDispatcher = ::android::pdx::servicefs::ServiceDispatcher; + +} // namespace default_transport +} // namespace pdx +} // namespace android + + +#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_DISPATCHER_H_ diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_endpoint.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_endpoint.h new file mode 100644 index 0000000000..8f413c1fa1 --- /dev/null +++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_endpoint.h @@ -0,0 +1,16 @@ +#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_ENDPOINT_H_ +#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_ENDPOINT_H_ + +#include <servicefs/service_endpoint.h> + +namespace android { +namespace pdx { +namespace default_transport { + +using Endpoint = ::android::pdx::servicefs::Endpoint; + +} // namespace default_transport +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_PDX_SERVICE_ENDPOINT_H_ diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_manager.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_manager.h new file mode 100644 index 0000000000..f34636f10a --- /dev/null +++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_manager.h @@ -0,0 +1,17 @@ +#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CHANNEL_MANAGER_H_ +#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CHANNEL_MANAGER_H_ + +#include <uds/channel_manager.h> + +namespace android { +namespace pdx { +namespace default_transport { + +using ChannelManager = ::android::pdx::uds::ChannelManager; + +} // namespace default_transport +} // namespace pdx +} // namespace android + + +#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CHANNEL_MANAGER_H_ diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel.h new file mode 100644 index 0000000000..bf632d7512 --- /dev/null +++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel.h @@ -0,0 +1,17 @@ +#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_H_ +#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_H_ + +#include <uds/client_channel.h> + +namespace android { +namespace pdx { +namespace default_transport { + +using ClientChannel = ::android::pdx::uds::ClientChannel; + +} // namespace default_transport +} // namespace pdx +} // namespace android + + +#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_H_ diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel_factory.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel_factory.h new file mode 100644 index 0000000000..e5c4e30368 --- /dev/null +++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel_factory.h @@ -0,0 +1,17 @@ +#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_FACTORY_H_ +#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_FACTORY_H_ + +#include <uds/client_channel_factory.h> + +namespace android { +namespace pdx { +namespace default_transport { + +using ClientChannelFactory = ::android::pdx::uds::ClientChannelFactory; + +} // namespace default_transport +} // namespace pdx +} // namespace android + + +#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_FACTORY_H_ diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h new file mode 100644 index 0000000000..7cb7a80fe7 --- /dev/null +++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h @@ -0,0 +1,17 @@ +#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_DISPATCHER_H_ +#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_DISPATCHER_H_ + +#include <uds/service_dispatcher.h> + +namespace android { +namespace pdx { +namespace default_transport { + +using ServiceDispatcher = ::android::pdx::uds::ServiceDispatcher; + +} // namespace default_transport +} // namespace pdx +} // namespace android + + +#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_DISPATCHER_H_ diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_endpoint.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_endpoint.h new file mode 100644 index 0000000000..1fd61030f9 --- /dev/null +++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_endpoint.h @@ -0,0 +1,16 @@ +#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_ENDPOINT_H_ +#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_ENDPOINT_H_ + +#include <uds/service_endpoint.h> + +namespace android { +namespace pdx { +namespace default_transport { + +using Endpoint = ::android::pdx::uds::Endpoint; + +} // namespace default_transport +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_PDX_SERVICE_ENDPOINT_H_ diff --git a/libs/vr/libpdx_default_transport/servicetool.cpp b/libs/vr/libpdx_default_transport/servicetool.cpp new file mode 100644 index 0000000000..60eedb3847 --- /dev/null +++ b/libs/vr/libpdx_default_transport/servicetool.cpp @@ -0,0 +1,244 @@ +#include <errno.h> +#include <ftw.h> +#include <getopt.h> +#include <pdx/client.h> +#include <pdx/service.h> +#include <sys/stat.h> + +#include <algorithm> +#include <vector> + +#include <pdx/default_transport/client_channel_factory.h> + +using android::pdx::default_transport::ClientChannelFactory; + +namespace { + +constexpr long kClientTimeoutMs = 0; // Don't wait for non-existent services. +constexpr int kDumpBufferSize = 2 * 4096; // Two pages. + +class ControlClient : public android::pdx::ClientBase<ControlClient> { + public: + explicit ControlClient(const std::string& service_path, long timeout_ms); + + void Reload(); + std::string Dump(); + + private: + friend BASE; + + ControlClient(const ControlClient&) = delete; + void operator=(const ControlClient&) = delete; +}; + +bool option_verbose = false; + +static struct option long_options[] = { + {"reload", required_argument, 0, 0}, + {"dump", required_argument, 0, 0}, + {"verbose", no_argument, 0, 0}, + {0, 0, 0, 0}, +}; + +#define printf_verbose(fmt, ... /*args*/) \ + do { \ + if (option_verbose) \ + printf(fmt, ##__VA_ARGS__); \ + } while (0) + +void HexDump(const void* pointer, size_t length); + +ControlClient::ControlClient(const std::string& service_path, long timeout_ms) + : BASE{ClientChannelFactory::Create(service_path), timeout_ms} {} + +void ControlClient::Reload() { + android::pdx::Transaction trans{*this}; + auto status = trans.Send<void>(android::pdx::opcodes::REPORT_SYSPROP_CHANGE, + nullptr, 0, nullptr, 0); + if (!status) { + fprintf(stderr, "Failed to send reload: %s\n", + status.GetErrorMessage().c_str()); + } +} + +std::string ControlClient::Dump() { + android::pdx::Transaction trans{*this}; + std::vector<char> buffer(kDumpBufferSize); + auto status = trans.Send<int>(android::pdx::opcodes::DUMP_STATE, nullptr, 0, + buffer.data(), buffer.size()); + + printf_verbose("ControlClient::Dump: ret=%d\n", ReturnStatusOrError(status)); + + if (!status) { + fprintf(stderr, "Failed to send dump request: %s\n", + status.GetErrorMessage().c_str()); + return ""; + } else if (status.get() > static_cast<ssize_t>(buffer.capacity())) { + fprintf(stderr, "Service returned a larger size than requested: %d\n", + status.get()); + return ""; + } + + if (option_verbose) + HexDump(buffer.data(), status.get()); + + return std::string(buffer.data(), status.get()); +} + +int Usage(const std::string& command_name) { + printf("Usage: %s [options]\n", command_name.c_str()); + printf("\t--verbose : Use verbose messages.\n"); + printf( + "\t--reload <all | service path> : Ask service(s) to reload system " + "properties.\n"); + printf("\t--dump <all | service path> : Dump service(s) state.\n"); + return -1; +} + +typedef int (*CallbackType)(const char* path, const struct stat* sb, + int type_flag, FTW* ftw_buffer); + +int ReloadCommandCallback(const char* path, const struct stat* sb, + int type_flag, FTW* ftw_buffer); +int DumpCommandCallback(const char* path, const struct stat* sb, int type_flag, + FTW* ftw_buffer); + +void CallOnAllFiles(CallbackType callback, const std::string& base_path) { + const int kMaxDepth = 32; + nftw(base_path.c_str(), callback, kMaxDepth, FTW_PHYS); +} + +int ReloadCommand(const std::string& service_path) { + printf_verbose("ReloadCommand: service_path=%s\n", service_path.c_str()); + + if (service_path == "" || service_path == "all") { + CallOnAllFiles(ReloadCommandCallback, + ClientChannelFactory::GetRootEndpointPath()); + return 0; + } else { + auto client = ControlClient::Create(service_path, kClientTimeoutMs); + if (!client) { + fprintf(stderr, "Failed to open service at \"%s\".\n", + service_path.c_str()); + return -1; + } + + client->Reload(); + return 0; + } +} + +int DumpCommand(const std::string& service_path) { + printf_verbose("DumpCommand: service_path=%s\n", service_path.c_str()); + + if (service_path == "" || service_path == "all") { + CallOnAllFiles(DumpCommandCallback, + ClientChannelFactory::GetRootEndpointPath()); + return 0; + } else { + auto client = ControlClient::Create(service_path, kClientTimeoutMs); + if (!client) { + fprintf(stderr, "Failed to open service at \"%s\".\n", + service_path.c_str()); + return -1; + } + + std::string response = client->Dump(); + if (!response.empty()) { + printf( + "--------------------------------------------------------------------" + "---\n"); + printf("%s:\n", service_path.c_str()); + printf("%s\n", response.c_str()); + } + return 0; + } +} + +int ReloadCommandCallback(const char* path, const struct stat*, int type_flag, + FTW*) { + if (type_flag == FTW_F) + ReloadCommand(path); + return 0; +} + +int DumpCommandCallback(const char* path, const struct stat*, int type_flag, + FTW*) { + if (type_flag == FTW_F) + DumpCommand(path); + return 0; +} + +void HexDump(const void* pointer, size_t length) { + uintptr_t address = reinterpret_cast<uintptr_t>(pointer); + + for (size_t count = 0; count < length; count += 16, address += 16) { + printf("0x%08lx: ", static_cast<unsigned long>(address)); + + for (size_t i = 0; i < 16u; i++) { + if (i < std::min(length - count, static_cast<size_t>(16))) { + printf("%02x ", *reinterpret_cast<const uint8_t*>(address + i)); + } else { + printf(" "); + } + } + + printf("|"); + + for (size_t i = 0; i < 16u; i++) { + if (i < std::min(length - count, static_cast<size_t>(16))) { + char c = *reinterpret_cast<const char*>(address + i); + if (isalnum(c) || c == ' ') { + printf("%c", c); + } else { + printf("."); + } + } else { + printf(" "); + } + } + + printf("|\n"); + } +} + +} // anonymous namespace + +int main(int argc, char** argv) { + int getopt_code; + int option_index; + std::string option = ""; + std::string command = ""; + std::string command_argument = ""; + + // Process command line options. + while ((getopt_code = + getopt_long(argc, argv, "", long_options, &option_index)) != -1) { + option = long_options[option_index].name; + printf_verbose("option=%s\n", option.c_str()); + switch (getopt_code) { + case 0: + if (option == "verbose") { + option_verbose = true; + } else { + command = option; + if (optarg) + command_argument = optarg; + } + break; + } + } + + printf_verbose("command=%s command_argument=%s\n", command.c_str(), + command_argument.c_str()); + + if (command == "") { + return Usage(argv[0]); + } else if (command == "reload") { + return ReloadCommand(command_argument); + } else if (command == "dump") { + return DumpCommand(command_argument); + } else { + return Usage(argv[0]); + } +} diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp new file mode 100644 index 0000000000..82a5ea7526 --- /dev/null +++ b/libs/vr/libpdx_uds/Android.bp @@ -0,0 +1,57 @@ +cc_library_static { + name: "libpdx_uds", + clang: true, + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + "-DLOG_TAG=\"libpdx_uds\"", + "-DTRACE=0", + ], + export_include_dirs: ["private"], + local_include_dirs: ["private"], + srcs: [ + "channel_event_set.cpp", + "channel_manager.cpp", + "client_channel_factory.cpp", + "client_channel.cpp", + "ipc_helper.cpp", + "service_dispatcher.cpp", + "service_endpoint.cpp", + ], + static_libs: [ + "libcutils", + "libbase", + "libpdx", + ], + whole_static_libs: [ + "libselinux", + ], +} + +cc_test { + name: "libpdx_uds_tests", + clang: true, + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + srcs: [ + "client_channel_tests.cpp", + "ipc_helper_tests.cpp", + "remote_method_tests.cpp", + "service_framework_tests.cpp", + ], + static_libs: [ + "libgmock", + "libpdx_uds", + "libpdx", + ], + shared_libs: [ + "libbase", + "libcutils", + "liblog", + "libutils", + ], +} diff --git a/libs/vr/libpdx_uds/channel_event_set.cpp b/libs/vr/libpdx_uds/channel_event_set.cpp new file mode 100644 index 0000000000..ac4dea993b --- /dev/null +++ b/libs/vr/libpdx_uds/channel_event_set.cpp @@ -0,0 +1,115 @@ +#include "private/uds/channel_event_set.h" + +#include <log/log.h> + +#include <uds/ipc_helper.h> + +namespace android { +namespace pdx { +namespace uds { + +ChannelEventSet::ChannelEventSet() { + const int flags = EFD_CLOEXEC | EFD_NONBLOCK; + LocalHandle epoll_fd, event_fd; + + if (!SetupHandle(epoll_create1(EPOLL_CLOEXEC), &epoll_fd, "epoll") || + !SetupHandle(eventfd(0, flags), &event_fd, "event")) { + return; + } + + epoll_event event; + event.events = 0; + event.data.u32 = 0; + if (epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, event_fd.Get(), &event) < 0) { + const int error = errno; + ALOGE("ChannelEventSet::ChannelEventSet: Failed to add event_fd: %s", + strerror(error)); + return; + } + + epoll_fd_ = std::move(epoll_fd); + event_fd_ = std::move(event_fd); +} + +Status<void> ChannelEventSet::AddDataFd(const LocalHandle& data_fd) { + epoll_event event; + event.events = EPOLLHUP | EPOLLRDHUP; + event.data.u32 = event.events; + if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, data_fd.Get(), &event) < 0) { + const int error = errno; + ALOGE("ChannelEventSet::ChannelEventSet: Failed to add event_fd: %s", + strerror(error)); + return ErrorStatus{error}; + } else { + return {}; + } +} + +int ChannelEventSet::ModifyEvents(int clear_mask, int set_mask) { + ALOGD_IF(TRACE, "ChannelEventSet::ModifyEvents: clear_mask=%x set_mask=%x", + clear_mask, set_mask); + const int old_bits = event_bits_; + const int new_bits = (event_bits_ & ~clear_mask) | set_mask; + event_bits_ = new_bits; + + // If anything changed clear the event and update the event mask. + if (old_bits != new_bits) { + eventfd_t value; + eventfd_read(event_fd_.Get(), &value); + + epoll_event event; + event.events = POLLIN; + event.data.u32 = event_bits_; + if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_MOD, event_fd_.Get(), &event) < + 0) { + const int error = errno; + ALOGE("ChannelEventSet::AddEventHandle: Failed to update event: %s", + strerror(error)); + return -error; + } + } + + // If there are any bits set, re-trigger the eventfd. + if (new_bits) + eventfd_write(event_fd_.Get(), 1); + + return 0; +} + +Status<void> ChannelEventSet::SetupHandle(int fd, LocalHandle* handle, + const char* error_name) { + const int error = errno; + handle->Reset(fd); + if (!*handle) { + ALOGE("ChannelEventSet::SetupHandle: Failed to setup %s handle: %s", + error_name, strerror(error)); + return ErrorStatus{error}; + } + return {}; +} + +Status<int> ChannelEventReceiver::GetPendingEvents() const { + constexpr long kTimeoutMs = 0; + epoll_event event; + const int count = + RETRY_EINTR(epoll_wait(epoll_fd_.Get(), &event, 1, kTimeoutMs)); + + Status<int> status; + if (count < 0) { + status.SetError(errno); + ALOGE("ChannelEventReceiver::GetPendingEvents: Failed to get events: %s", + status.GetErrorMessage().c_str()); + return status; + } + + const int mask_out = event.data.u32; + ALOGD_IF(TRACE, "ChannelEventReceiver::GetPendingEvents: mask_out=%x", + mask_out); + + status.SetValue(mask_out); + return status; +} + +} // namespace uds +} // namespace pdx +} // namespace android diff --git a/libs/vr/libpdx_uds/channel_manager.cpp b/libs/vr/libpdx_uds/channel_manager.cpp new file mode 100644 index 0000000000..afc0a4f041 --- /dev/null +++ b/libs/vr/libpdx_uds/channel_manager.cpp @@ -0,0 +1,44 @@ +#include <uds/channel_manager.h> + +#include <log/log.h> + +namespace android { +namespace pdx { +namespace uds { + +ChannelManager& ChannelManager::Get() { + static ChannelManager instance; + return instance; +} + +void ChannelManager::CloseHandle(int32_t handle) { + std::lock_guard<std::mutex> autolock(mutex_); + auto channel = channels_.find(handle); + if (channel == channels_.end()) { + ALOGE("Invalid channel handle: %d", handle); + } else { + channels_.erase(channel); + } +} + +LocalChannelHandle ChannelManager::CreateHandle(LocalHandle data_fd, + LocalHandle event_fd) { + if (data_fd && event_fd) { + std::lock_guard<std::mutex> autolock(mutex_); + int32_t handle = data_fd.Get(); + channels_.emplace(handle, + ChannelData{std::move(data_fd), std::move(event_fd)}); + return LocalChannelHandle(this, handle); + } + return LocalChannelHandle(nullptr, -1); +} + +ChannelManager::ChannelData* ChannelManager::GetChannelData(int32_t handle) { + std::lock_guard<std::mutex> autolock(mutex_); + auto channel = channels_.find(handle); + return channel != channels_.end() ? &channel->second : nullptr; +} + +} // namespace uds +} // namespace pdx +} // namespace android diff --git a/libs/vr/libpdx_uds/client_channel.cpp b/libs/vr/libpdx_uds/client_channel.cpp new file mode 100644 index 0000000000..9d9161784a --- /dev/null +++ b/libs/vr/libpdx_uds/client_channel.cpp @@ -0,0 +1,293 @@ +#include "uds/client_channel.h" + +#include <errno.h> +#include <log/log.h> +#include <sys/epoll.h> +#include <sys/socket.h> + +#include <pdx/client.h> +#include <pdx/service_endpoint.h> +#include <uds/ipc_helper.h> + +namespace android { +namespace pdx { +namespace uds { + +namespace { + +struct TransactionState { + bool GetLocalFileHandle(int index, LocalHandle* handle) { + if (index < 0) { + handle->Reset(index); + } else if (static_cast<size_t>(index) < response.file_descriptors.size()) { + *handle = std::move(response.file_descriptors[index]); + } else { + return false; + } + return true; + } + + bool GetLocalChannelHandle(int index, LocalChannelHandle* handle) { + if (index < 0) { + *handle = LocalChannelHandle{nullptr, index}; + } else if (static_cast<size_t>(index) < response.channels.size()) { + auto& channel_info = response.channels[index]; + *handle = ChannelManager::Get().CreateHandle( + std::move(channel_info.data_fd), std::move(channel_info.event_fd)); + } else { + return false; + } + return true; + } + + FileReference PushFileHandle(BorrowedHandle handle) { + if (!handle) + return handle.Get(); + request.file_descriptors.push_back(std::move(handle)); + return request.file_descriptors.size() - 1; + } + + ChannelReference PushChannelHandle(BorrowedChannelHandle handle) { + if (!handle) + return handle.value(); + + if (auto* channel_data = + ChannelManager::Get().GetChannelData(handle.value())) { + ChannelInfo<BorrowedHandle> channel_info; + channel_info.data_fd.Reset(handle.value()); + channel_info.event_fd = channel_data->event_receiver.event_fd(); + request.channels.push_back(std::move(channel_info)); + return request.channels.size() - 1; + } else { + return -1; + } + } + + RequestHeader<BorrowedHandle> request; + ResponseHeader<LocalHandle> response; +}; + +Status<void> ReadAndDiscardData(const BorrowedHandle& socket_fd, size_t size) { + while (size > 0) { + // If there is more data to read in the message than the buffers provided + // by the caller, read and discard the extra data from the socket. + char buffer[1024]; + size_t size_to_read = std::min(sizeof(buffer), size); + auto status = ReceiveData(socket_fd, buffer, size_to_read); + if (!status) + return status; + size -= size_to_read; + } + // We still want to return EIO error to the caller in case we had unexpected + // data in the socket stream. + return ErrorStatus(EIO); +} + +Status<void> SendRequest(const BorrowedHandle& socket_fd, + TransactionState* transaction_state, int opcode, + const iovec* send_vector, size_t send_count, + size_t max_recv_len) { + size_t send_len = CountVectorSize(send_vector, send_count); + InitRequest(&transaction_state->request, opcode, send_len, max_recv_len, + false); + auto status = SendData(socket_fd, transaction_state->request); + if (status && send_len > 0) + status = SendDataVector(socket_fd, send_vector, send_count); + return status; +} + +Status<void> ReceiveResponse(const BorrowedHandle& socket_fd, + TransactionState* transaction_state, + const iovec* receive_vector, size_t receive_count, + size_t max_recv_len) { + auto status = ReceiveData(socket_fd, &transaction_state->response); + if (!status) + return status; + + if (transaction_state->response.recv_len > 0) { + std::vector<iovec> read_buffers; + size_t size_remaining = 0; + if (transaction_state->response.recv_len != max_recv_len) { + // If the receive buffer not exactly the size of data available, recreate + // the vector list to consume the data exactly since ReceiveDataVector() + // validates that the number of bytes received equals the number of bytes + // requested. + size_remaining = transaction_state->response.recv_len; + for (size_t i = 0; i < receive_count && size_remaining > 0; i++) { + read_buffers.push_back(receive_vector[i]); + iovec& last_vec = read_buffers.back(); + if (last_vec.iov_len > size_remaining) + last_vec.iov_len = size_remaining; + size_remaining -= last_vec.iov_len; + } + receive_vector = read_buffers.data(); + receive_count = read_buffers.size(); + } + status = ReceiveDataVector(socket_fd, receive_vector, receive_count); + if (status && size_remaining > 0) + status = ReadAndDiscardData(socket_fd, size_remaining); + } + return status; +} + +} // anonymous namespace + +ClientChannel::ClientChannel(LocalChannelHandle channel_handle) + : channel_handle_{std::move(channel_handle)} { + channel_data_ = ChannelManager::Get().GetChannelData(channel_handle_.value()); +} + +std::unique_ptr<pdx::ClientChannel> ClientChannel::Create( + LocalChannelHandle channel_handle) { + return std::unique_ptr<pdx::ClientChannel>{ + new ClientChannel{std::move(channel_handle)}}; +} + +ClientChannel::~ClientChannel() { + if (channel_handle_) + shutdown(channel_handle_.value(), SHUT_WR); +} + +void* ClientChannel::AllocateTransactionState() { return new TransactionState; } + +void ClientChannel::FreeTransactionState(void* state) { + delete static_cast<TransactionState*>(state); +} + +Status<void> ClientChannel::SendImpulse(int opcode, const void* buffer, + size_t length) { + std::unique_lock<std::mutex> lock(socket_mutex_); + Status<void> status; + android::pdx::uds::RequestHeader<BorrowedHandle> request; + if (length > request.impulse_payload.size() || + (buffer == nullptr && length != 0)) { + status.SetError(EINVAL); + return status; + } + + InitRequest(&request, opcode, length, 0, true); + memcpy(request.impulse_payload.data(), buffer, length); + return SendData(BorrowedHandle{channel_handle_.value()}, request); +} + +Status<int> ClientChannel::SendAndReceive(void* transaction_state, int opcode, + const iovec* send_vector, + size_t send_count, + const iovec* receive_vector, + size_t receive_count) { + std::unique_lock<std::mutex> lock(socket_mutex_); + Status<int> result; + if ((send_vector == nullptr && send_count != 0) || + (receive_vector == nullptr && receive_count != 0)) { + result.SetError(EINVAL); + return result; + } + + auto* state = static_cast<TransactionState*>(transaction_state); + size_t max_recv_len = CountVectorSize(receive_vector, receive_count); + + auto status = SendRequest(BorrowedHandle{channel_handle_.value()}, state, + opcode, send_vector, send_count, max_recv_len); + if (status) { + status = ReceiveResponse(BorrowedHandle{channel_handle_.value()}, state, + receive_vector, receive_count, max_recv_len); + } + if (!result.PropagateError(status)) { + const int return_code = state->response.ret_code; + if (return_code >= 0) + result.SetValue(return_code); + else + result.SetError(-return_code); + } + return result; +} + +Status<int> ClientChannel::SendWithInt(void* transaction_state, int opcode, + const iovec* send_vector, + size_t send_count, + const iovec* receive_vector, + size_t receive_count) { + return SendAndReceive(transaction_state, opcode, send_vector, send_count, + receive_vector, receive_count); +} + +Status<LocalHandle> ClientChannel::SendWithFileHandle( + void* transaction_state, int opcode, const iovec* send_vector, + size_t send_count, const iovec* receive_vector, size_t receive_count) { + Status<int> int_status = + SendAndReceive(transaction_state, opcode, send_vector, send_count, + receive_vector, receive_count); + Status<LocalHandle> status; + if (status.PropagateError(int_status)) + return status; + + auto* state = static_cast<TransactionState*>(transaction_state); + LocalHandle handle; + if (state->GetLocalFileHandle(int_status.get(), &handle)) { + status.SetValue(std::move(handle)); + } else { + status.SetError(EINVAL); + } + return status; +} + +Status<LocalChannelHandle> ClientChannel::SendWithChannelHandle( + void* transaction_state, int opcode, const iovec* send_vector, + size_t send_count, const iovec* receive_vector, size_t receive_count) { + Status<int> int_status = + SendAndReceive(transaction_state, opcode, send_vector, send_count, + receive_vector, receive_count); + Status<LocalChannelHandle> status; + if (status.PropagateError(int_status)) + return status; + + auto* state = static_cast<TransactionState*>(transaction_state); + LocalChannelHandle handle; + if (state->GetLocalChannelHandle(int_status.get(), &handle)) { + status.SetValue(std::move(handle)); + } else { + status.SetError(EINVAL); + } + return status; +} + +FileReference ClientChannel::PushFileHandle(void* transaction_state, + const LocalHandle& handle) { + auto* state = static_cast<TransactionState*>(transaction_state); + return state->PushFileHandle(handle.Borrow()); +} + +FileReference ClientChannel::PushFileHandle(void* transaction_state, + const BorrowedHandle& handle) { + auto* state = static_cast<TransactionState*>(transaction_state); + return state->PushFileHandle(handle.Duplicate()); +} + +ChannelReference ClientChannel::PushChannelHandle( + void* transaction_state, const LocalChannelHandle& handle) { + auto* state = static_cast<TransactionState*>(transaction_state); + return state->PushChannelHandle(handle.Borrow()); +} + +ChannelReference ClientChannel::PushChannelHandle( + void* transaction_state, const BorrowedChannelHandle& handle) { + auto* state = static_cast<TransactionState*>(transaction_state); + return state->PushChannelHandle(handle.Duplicate()); +} + +bool ClientChannel::GetFileHandle(void* transaction_state, FileReference ref, + LocalHandle* handle) const { + auto* state = static_cast<TransactionState*>(transaction_state); + return state->GetLocalFileHandle(ref, handle); +} + +bool ClientChannel::GetChannelHandle(void* transaction_state, + ChannelReference ref, + LocalChannelHandle* handle) const { + auto* state = static_cast<TransactionState*>(transaction_state); + return state->GetLocalChannelHandle(ref, handle); +} + +} // namespace uds +} // namespace pdx +} // namespace android diff --git a/libs/vr/libpdx_uds/client_channel_factory.cpp b/libs/vr/libpdx_uds/client_channel_factory.cpp new file mode 100644 index 0000000000..433f459769 --- /dev/null +++ b/libs/vr/libpdx_uds/client_channel_factory.cpp @@ -0,0 +1,160 @@ +#include <uds/client_channel_factory.h> + +#include <errno.h> +#include <log/log.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> + +#include <chrono> +#include <thread> + +#include <uds/channel_manager.h> +#include <uds/client_channel.h> +#include <uds/ipc_helper.h> + +using std::chrono::duration_cast; +using std::chrono::steady_clock; + +namespace android { +namespace pdx { +namespace uds { + +std::string ClientChannelFactory::GetRootEndpointPath() { + return "/dev/socket/pdx"; +} + +std::string ClientChannelFactory::GetEndpointPath( + const std::string& endpoint_path) { + std::string path; + if (!endpoint_path.empty()) { + if (endpoint_path.front() == '/') + path = endpoint_path; + else + path = GetRootEndpointPath() + '/' + endpoint_path; + } + return path; +} + +ClientChannelFactory::ClientChannelFactory(const std::string& endpoint_path) + : endpoint_path_{GetEndpointPath(endpoint_path)} {} + +ClientChannelFactory::ClientChannelFactory(LocalHandle socket) + : socket_{std::move(socket)} {} + +std::unique_ptr<pdx::ClientChannelFactory> ClientChannelFactory::Create( + const std::string& endpoint_path) { + return std::unique_ptr<pdx::ClientChannelFactory>{ + new ClientChannelFactory{endpoint_path}}; +} + +std::unique_ptr<pdx::ClientChannelFactory> ClientChannelFactory::Create( + LocalHandle socket) { + return std::unique_ptr<pdx::ClientChannelFactory>{ + new ClientChannelFactory{std::move(socket)}}; +} + +Status<std::unique_ptr<pdx::ClientChannel>> ClientChannelFactory::Connect( + int64_t timeout_ms) const { + Status<void> status; + + bool connected = socket_.IsValid(); + if (!connected) { + socket_.Reset(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)); + LOG_ALWAYS_FATAL_IF( + endpoint_path_.empty(), + "ClientChannelFactory::Connect: unspecified socket path"); + } + + if (!socket_) { + ALOGE("ClientChannelFactory::Connect: socket error: %s", strerror(errno)); + return ErrorStatus(errno); + } + + bool use_timeout = (timeout_ms >= 0); + auto now = steady_clock::now(); + auto time_end = now + std::chrono::milliseconds{timeout_ms}; + + int max_eaccess = 5; // Max number of times to retry when EACCES returned. + while (!connected) { + int64_t timeout = -1; + if (use_timeout) { + auto remaining = time_end - now; + timeout = duration_cast<std::chrono::milliseconds>(remaining).count(); + if (timeout < 0) + return ErrorStatus(ETIMEDOUT); + } + sockaddr_un remote; + remote.sun_family = AF_UNIX; + strncpy(remote.sun_path, endpoint_path_.c_str(), sizeof(remote.sun_path)); + remote.sun_path[sizeof(remote.sun_path) - 1] = '\0'; + ALOGD("ClientChannelFactory: Waiting for endpoint at %s", remote.sun_path); + status = WaitForEndpoint(endpoint_path_, timeout); + if (!status) + return ErrorStatus(status.error()); + + ALOGD("ClientChannelFactory: Connecting to %s", remote.sun_path); + int ret = RETRY_EINTR(connect( + socket_.Get(), reinterpret_cast<sockaddr*>(&remote), sizeof(remote))); + if (ret == -1) { + ALOGD("ClientChannelFactory: Connect error %d: %s", errno, + strerror(errno)); + // if |max_eaccess| below reaches zero when errno is EACCES, the control + // flows into the next "else if" statement and a permanent error is + // returned from this function. + if (errno == ECONNREFUSED || (errno == EACCES && max_eaccess-- > 0)) { + // Connection refused/Permission denied can be the result of connecting + // too early (the service socket is created but its access rights are + // not set or not being listened to yet). + ALOGD("ClientChannelFactory: %s, waiting...", strerror(errno)); + using namespace std::literals::chrono_literals; + std::this_thread::sleep_for(100ms); + } else if (errno != ENOENT && errno != ENOTDIR) { + // ENOENT/ENOTDIR might mean that the socket file/directory containing + // it has been just deleted. Try to wait for its creation and do not + // return an error immediately. + ALOGE( + "ClientChannelFactory::Connect: Failed to initialize connection " + "when connecting: %s", + strerror(errno)); + return ErrorStatus(errno); + } + } else { + connected = true; + ALOGD("ClientChannelFactory: Connected successfully to %s...", + remote.sun_path); + ChannelConnectionInfo<LocalHandle> connection_info; + status = ReceiveData(socket_.Borrow(), &connection_info); + if (!status) + return status.error_status(); + socket_ = std::move(connection_info.channel_fd); + if (!socket_) { + ALOGE("ClientChannelFactory::Connect: Failed to obtain channel socket"); + return ErrorStatus(EIO); + } + } + if (use_timeout) + now = steady_clock::now(); + } // while (!connected) + + RequestHeader<BorrowedHandle> request; + InitRequest(&request, opcodes::CHANNEL_OPEN, 0, 0, false); + status = SendData(socket_.Borrow(), request); + if (!status) + return status.error_status(); + ResponseHeader<LocalHandle> response; + status = ReceiveData(socket_.Borrow(), &response); + if (!status) + return status.error_status(); + int ref = response.ret_code; + if (ref < 0 || static_cast<size_t>(ref) > response.file_descriptors.size()) + return ErrorStatus(EIO); + + LocalHandle event_fd = std::move(response.file_descriptors[ref]); + return ClientChannel::Create(ChannelManager::Get().CreateHandle( + std::move(socket_), std::move(event_fd))); +} + +} // namespace uds +} // namespace pdx +} // namespace android diff --git a/libs/vr/libpdx_uds/client_channel_tests.cpp b/libs/vr/libpdx_uds/client_channel_tests.cpp new file mode 100644 index 0000000000..7c3c68aa31 --- /dev/null +++ b/libs/vr/libpdx_uds/client_channel_tests.cpp @@ -0,0 +1,162 @@ +#include <uds/client_channel.h> + +#include <sys/socket.h> + +#include <algorithm> +#include <limits> +#include <random> +#include <thread> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <pdx/client.h> +#include <pdx/rpc/remote_method.h> +#include <pdx/service.h> + +#include <uds/client_channel_factory.h> +#include <uds/service_endpoint.h> + +using testing::Return; +using testing::_; + +using android::pdx::ClientBase; +using android::pdx::LocalChannelHandle; +using android::pdx::LocalHandle; +using android::pdx::Message; +using android::pdx::ServiceBase; +using android::pdx::ServiceDispatcher; +using android::pdx::Status; +using android::pdx::rpc::DispatchRemoteMethod; +using android::pdx::uds::ClientChannel; +using android::pdx::uds::ClientChannelFactory; +using android::pdx::uds::Endpoint; + +namespace { + +struct TestProtocol { + using DataType = int8_t; + enum { + kOpSum = 0, + }; + PDX_REMOTE_METHOD(Sum, kOpSum, int64_t(const std::vector<DataType>&)); +}; + +class TestService : public ServiceBase<TestService> { + public: + TestService(std::unique_ptr<Endpoint> endpoint) + : ServiceBase{"TestService", std::move(endpoint)} {} + + Status<void> HandleMessage(Message& message) override { + switch (message.GetOp()) { + case TestProtocol::kOpSum: + DispatchRemoteMethod<TestProtocol::Sum>(*this, &TestService::OnSum, + message); + return {}; + + default: + return Service::HandleMessage(message); + } + } + + int64_t OnSum(Message& /*message*/, + const std::vector<TestProtocol::DataType>& data) { + return std::accumulate(data.begin(), data.end(), int64_t{0}); + } +}; + +class TestClient : public ClientBase<TestClient> { + public: + using ClientBase::ClientBase; + + int64_t Sum(const std::vector<TestProtocol::DataType>& data) { + auto status = InvokeRemoteMethod<TestProtocol::Sum>(data); + return status ? status.get() : -1; + } +}; + +class TestServiceRunner { + public: + TestServiceRunner(LocalHandle channel_socket) { + auto endpoint = Endpoint::CreateFromSocketFd(LocalHandle{}); + endpoint->RegisterNewChannelForTests(std::move(channel_socket)); + service_ = TestService::Create(std::move(endpoint)); + dispatcher_ = android::pdx::uds::ServiceDispatcher::Create(); + dispatcher_->AddService(service_); + dispatch_thread_ = std::thread( + std::bind(&ServiceDispatcher::EnterDispatchLoop, dispatcher_.get())); + } + + ~TestServiceRunner() { + dispatcher_->SetCanceled(true); + dispatch_thread_.join(); + dispatcher_->RemoveService(service_); + } + + private: + std::shared_ptr<TestService> service_; + std::unique_ptr<ServiceDispatcher> dispatcher_; + std::thread dispatch_thread_; +}; + +class ClientChannelTest : public testing::Test { + public: + void SetUp() override { + int channel_sockets[2] = {}; + ASSERT_EQ( + 0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, channel_sockets)); + LocalHandle service_channel{channel_sockets[0]}; + LocalHandle client_channel{channel_sockets[1]}; + + service_runner_.reset(new TestServiceRunner{std::move(service_channel)}); + auto factory = ClientChannelFactory::Create(std::move(client_channel)); + auto status = factory->Connect(android::pdx::Client::kInfiniteTimeout); + ASSERT_TRUE(status); + client_ = TestClient::Create(status.take()); + } + + void TearDown() override { + service_runner_.reset(); + client_.reset(); + } + + protected: + std::unique_ptr<TestServiceRunner> service_runner_; + std::shared_ptr<TestClient> client_; +}; + +TEST_F(ClientChannelTest, MultithreadedClient) { + constexpr int kNumTestThreads = 8; + constexpr size_t kDataSize = 1000; // Try to keep RPC buffer size below 4K. + + std::random_device rd; + std::mt19937 gen{rd()}; + std::uniform_int_distribution<TestProtocol::DataType> dist{ + std::numeric_limits<TestProtocol::DataType>::min(), + std::numeric_limits<TestProtocol::DataType>::max()}; + + auto worker = [](std::shared_ptr<TestClient> client, + std::vector<TestProtocol::DataType> data) { + constexpr int kMaxIterations = 500; + int64_t expected = std::accumulate(data.begin(), data.end(), int64_t{0}); + for (int i = 0; i < kMaxIterations; i++) { + ASSERT_EQ(expected, client->Sum(data)); + } + }; + + // Start client threads. + std::vector<TestProtocol::DataType> data; + data.resize(kDataSize); + std::vector<std::thread> threads; + for (int i = 0; i < kNumTestThreads; i++) { + std::generate(data.begin(), data.end(), + [&dist, &gen]() { return dist(gen); }); + threads.emplace_back(worker, client_, data); + } + + // Wait for threads to finish. + for (auto& thread : threads) + thread.join(); +} + +} // namespace diff --git a/libs/vr/libpdx_uds/ipc_helper.cpp b/libs/vr/libpdx_uds/ipc_helper.cpp new file mode 100644 index 0000000000..d75ce86e4b --- /dev/null +++ b/libs/vr/libpdx_uds/ipc_helper.cpp @@ -0,0 +1,534 @@ +#include "uds/ipc_helper.h" + +#include <alloca.h> +#include <errno.h> +#include <log/log.h> +#include <poll.h> +#include <string.h> +#include <sys/inotify.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <algorithm> + +#include <pdx/service.h> +#include <pdx/utility.h> + +namespace android { +namespace pdx { +namespace uds { + +namespace { + +// Default implementations of Send/Receive interfaces to use standard socket +// send/sendmsg/recv/recvmsg functions. +class SocketSender : public SendInterface { + public: + ssize_t Send(int socket_fd, const void* data, size_t size, + int flags) override { + return send(socket_fd, data, size, flags); + } + ssize_t SendMessage(int socket_fd, const msghdr* msg, int flags) override { + return sendmsg(socket_fd, msg, flags); + } +} g_socket_sender; + +class SocketReceiver : public RecvInterface { + public: + ssize_t Receive(int socket_fd, void* data, size_t size, int flags) override { + return recv(socket_fd, data, size, flags); + } + ssize_t ReceiveMessage(int socket_fd, msghdr* msg, int flags) override { + return recvmsg(socket_fd, msg, flags); + } +} g_socket_receiver; + +} // anonymous namespace + +// Helper wrappers around send()/sendmsg() which repeat send() calls on data +// that was not sent with the initial call to send/sendmsg. This is important to +// handle transmissions interrupted by signals. +Status<void> SendAll(SendInterface* sender, const BorrowedHandle& socket_fd, + const void* data, size_t size) { + Status<void> ret; + const uint8_t* ptr = static_cast<const uint8_t*>(data); + while (size > 0) { + ssize_t size_written = + RETRY_EINTR(sender->Send(socket_fd.Get(), ptr, size, MSG_NOSIGNAL)); + if (size_written < 0) { + ret.SetError(errno); + ALOGE("SendAll: Failed to send data over socket: %s", + ret.GetErrorMessage().c_str()); + break; + } + size -= size_written; + ptr += size_written; + } + return ret; +} + +Status<void> SendMsgAll(SendInterface* sender, const BorrowedHandle& socket_fd, + const msghdr* msg) { + Status<void> ret; + ssize_t sent_size = + RETRY_EINTR(sender->SendMessage(socket_fd.Get(), msg, MSG_NOSIGNAL)); + if (sent_size < 0) { + ret.SetError(errno); + ALOGE("SendMsgAll: Failed to send data over socket: %s", + ret.GetErrorMessage().c_str()); + return ret; + } + + ssize_t chunk_start_offset = 0; + for (size_t i = 0; i < msg->msg_iovlen; i++) { + ssize_t chunk_end_offset = chunk_start_offset + msg->msg_iov[i].iov_len; + if (sent_size < chunk_end_offset) { + size_t offset_within_chunk = sent_size - chunk_start_offset; + size_t data_size = msg->msg_iov[i].iov_len - offset_within_chunk; + const uint8_t* chunk_base = + static_cast<const uint8_t*>(msg->msg_iov[i].iov_base); + ret = SendAll(sender, socket_fd, chunk_base + offset_within_chunk, + data_size); + if (!ret) + break; + sent_size += data_size; + } + chunk_start_offset = chunk_end_offset; + } + return ret; +} + +// Helper wrappers around recv()/recvmsg() which repeat recv() calls on data +// that was not received with the initial call to recvmsg(). This is important +// to handle transmissions interrupted by signals as well as the case when +// initial data did not arrive in a single chunk over the socket (e.g. socket +// buffer was full at the time of transmission, and only portion of initial +// message was sent and the rest was blocked until the buffer was cleared by the +// receiving side). +Status<void> RecvMsgAll(RecvInterface* receiver, + const BorrowedHandle& socket_fd, msghdr* msg) { + Status<void> ret; + ssize_t size_read = RETRY_EINTR(receiver->ReceiveMessage( + socket_fd.Get(), msg, MSG_WAITALL | MSG_CMSG_CLOEXEC)); + if (size_read < 0) { + ret.SetError(errno); + ALOGE("RecvMsgAll: Failed to receive data from socket: %s", + ret.GetErrorMessage().c_str()); + return ret; + } else if (size_read == 0) { + ret.SetError(ESHUTDOWN); + ALOGW("RecvMsgAll: Socket has been shut down"); + return ret; + } + + ssize_t chunk_start_offset = 0; + for (size_t i = 0; i < msg->msg_iovlen; i++) { + ssize_t chunk_end_offset = chunk_start_offset + msg->msg_iov[i].iov_len; + if (size_read < chunk_end_offset) { + size_t offset_within_chunk = size_read - chunk_start_offset; + size_t data_size = msg->msg_iov[i].iov_len - offset_within_chunk; + uint8_t* chunk_base = static_cast<uint8_t*>(msg->msg_iov[i].iov_base); + ret = RecvAll(receiver, socket_fd, chunk_base + offset_within_chunk, + data_size); + if (!ret) + break; + size_read += data_size; + } + chunk_start_offset = chunk_end_offset; + } + return ret; +} + +Status<void> RecvAll(RecvInterface* receiver, const BorrowedHandle& socket_fd, + void* data, size_t size) { + Status<void> ret; + uint8_t* ptr = static_cast<uint8_t*>(data); + while (size > 0) { + ssize_t size_read = RETRY_EINTR(receiver->Receive( + socket_fd.Get(), ptr, size, MSG_WAITALL | MSG_CMSG_CLOEXEC)); + if (size_read < 0) { + ret.SetError(errno); + ALOGE("RecvAll: Failed to receive data from socket: %s", + ret.GetErrorMessage().c_str()); + break; + } else if (size_read == 0) { + ret.SetError(ESHUTDOWN); + ALOGW("RecvAll: Socket has been shut down"); + break; + } + size -= size_read; + ptr += size_read; + } + return ret; +} + +uint32_t kMagicPreamble = 0x7564736d; // 'udsm'. + +struct MessagePreamble { + uint32_t magic{0}; + uint32_t data_size{0}; + uint32_t fd_count{0}; +}; + +Status<void> SendPayload::Send(const BorrowedHandle& socket_fd) { + return Send(socket_fd, nullptr); +} + +Status<void> SendPayload::Send(const BorrowedHandle& socket_fd, + const ucred* cred) { + SendInterface* sender = sender_ ? sender_ : &g_socket_sender; + MessagePreamble preamble; + preamble.magic = kMagicPreamble; + preamble.data_size = buffer_.size(); + preamble.fd_count = file_handles_.size(); + Status<void> ret = SendAll(sender, socket_fd, &preamble, sizeof(preamble)); + if (!ret) + return ret; + + msghdr msg = {}; + iovec recv_vect = {buffer_.data(), buffer_.size()}; + msg.msg_iov = &recv_vect; + msg.msg_iovlen = 1; + + if (cred || !file_handles_.empty()) { + const size_t fd_bytes = file_handles_.size() * sizeof(int); + msg.msg_controllen = (cred ? CMSG_SPACE(sizeof(ucred)) : 0) + + (fd_bytes == 0 ? 0 : CMSG_SPACE(fd_bytes)); + msg.msg_control = alloca(msg.msg_controllen); + + cmsghdr* control = CMSG_FIRSTHDR(&msg); + if (cred) { + control->cmsg_level = SOL_SOCKET; + control->cmsg_type = SCM_CREDENTIALS; + control->cmsg_len = CMSG_LEN(sizeof(ucred)); + memcpy(CMSG_DATA(control), cred, sizeof(ucred)); + control = CMSG_NXTHDR(&msg, control); + } + + if (fd_bytes) { + control->cmsg_level = SOL_SOCKET; + control->cmsg_type = SCM_RIGHTS; + control->cmsg_len = CMSG_LEN(fd_bytes); + memcpy(CMSG_DATA(control), file_handles_.data(), fd_bytes); + } + } + + return SendMsgAll(sender, socket_fd, &msg); +} + +// MessageWriter +void* SendPayload::GetNextWriteBufferSection(size_t size) { + return buffer_.grow_by(size); +} + +OutputResourceMapper* SendPayload::GetOutputResourceMapper() { return this; } + +// OutputResourceMapper +Status<FileReference> SendPayload::PushFileHandle(const LocalHandle& handle) { + if (handle) { + const int ref = file_handles_.size(); + file_handles_.push_back(handle.Get()); + return ref; + } else { + return handle.Get(); + } +} + +Status<FileReference> SendPayload::PushFileHandle( + const BorrowedHandle& handle) { + if (handle) { + const int ref = file_handles_.size(); + file_handles_.push_back(handle.Get()); + return ref; + } else { + return handle.Get(); + } +} + +Status<FileReference> SendPayload::PushFileHandle(const RemoteHandle& handle) { + return handle.Get(); +} + +Status<ChannelReference> SendPayload::PushChannelHandle( + const LocalChannelHandle& /*handle*/) { + return ErrorStatus{EOPNOTSUPP}; +} +Status<ChannelReference> SendPayload::PushChannelHandle( + const BorrowedChannelHandle& /*handle*/) { + return ErrorStatus{EOPNOTSUPP}; +} +Status<ChannelReference> SendPayload::PushChannelHandle( + const RemoteChannelHandle& /*handle*/) { + return ErrorStatus{EOPNOTSUPP}; +} + +Status<void> ReceivePayload::Receive(const BorrowedHandle& socket_fd) { + return Receive(socket_fd, nullptr); +} + +Status<void> ReceivePayload::Receive(const BorrowedHandle& socket_fd, + ucred* cred) { + RecvInterface* receiver = receiver_ ? receiver_ : &g_socket_receiver; + MessagePreamble preamble; + Status<void> ret = RecvAll(receiver, socket_fd, &preamble, sizeof(preamble)); + if (!ret) + return ret; + + if (preamble.magic != kMagicPreamble) { + ALOGE("ReceivePayload::Receive: Message header is invalid"); + ret.SetError(EIO); + return ret; + } + + buffer_.resize(preamble.data_size); + file_handles_.clear(); + read_pos_ = 0; + + msghdr msg = {}; + iovec recv_vect = {buffer_.data(), buffer_.size()}; + msg.msg_iov = &recv_vect; + msg.msg_iovlen = 1; + + if (cred || preamble.fd_count) { + const size_t receive_fd_bytes = preamble.fd_count * sizeof(int); + msg.msg_controllen = + (cred ? CMSG_SPACE(sizeof(ucred)) : 0) + + (receive_fd_bytes == 0 ? 0 : CMSG_SPACE(receive_fd_bytes)); + msg.msg_control = alloca(msg.msg_controllen); + } + + ret = RecvMsgAll(receiver, socket_fd, &msg); + if (!ret) + return ret; + + bool cred_available = false; + file_handles_.reserve(preamble.fd_count); + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + while (cmsg) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS && + cred && cmsg->cmsg_len == CMSG_LEN(sizeof(ucred))) { + cred_available = true; + memcpy(cred, CMSG_DATA(cmsg), sizeof(ucred)); + } else if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + socklen_t payload_len = cmsg->cmsg_len - CMSG_LEN(0); + const int* fds = reinterpret_cast<const int*>(CMSG_DATA(cmsg)); + size_t fd_count = payload_len / sizeof(int); + std::transform(fds, fds + fd_count, std::back_inserter(file_handles_), + [](int fd) { return LocalHandle{fd}; }); + } + cmsg = CMSG_NXTHDR(&msg, cmsg); + } + + if (cred && !cred_available) { + ALOGE("ReceivePayload::Receive: Failed to obtain message credentials"); + ret.SetError(EIO); + } + + return ret; +} + +// MessageReader +MessageReader::BufferSection ReceivePayload::GetNextReadBufferSection() { + return {buffer_.data() + read_pos_, &*buffer_.end()}; +} + +void ReceivePayload::ConsumeReadBufferSectionData(const void* new_start) { + read_pos_ = PointerDistance(new_start, buffer_.data()); +} + +InputResourceMapper* ReceivePayload::GetInputResourceMapper() { return this; } + +// InputResourceMapper +bool ReceivePayload::GetFileHandle(FileReference ref, LocalHandle* handle) { + if (ref < 0) { + *handle = LocalHandle{ref}; + return true; + } + if (static_cast<size_t>(ref) > file_handles_.size()) + return false; + *handle = std::move(file_handles_[ref]); + return true; +} + +bool ReceivePayload::GetChannelHandle(ChannelReference /*ref*/, + LocalChannelHandle* /*handle*/) { + return false; +} + +Status<void> SendData(const BorrowedHandle& socket_fd, const void* data, + size_t size) { + return SendAll(&g_socket_sender, socket_fd, data, size); +} + +Status<void> SendDataVector(const BorrowedHandle& socket_fd, const iovec* data, + size_t count) { + msghdr msg = {}; + msg.msg_iov = const_cast<iovec*>(data); + msg.msg_iovlen = count; + return SendMsgAll(&g_socket_sender, socket_fd, &msg); +} + +Status<void> ReceiveData(const BorrowedHandle& socket_fd, void* data, + size_t size) { + return RecvAll(&g_socket_receiver, socket_fd, data, size); +} + +Status<void> ReceiveDataVector(const BorrowedHandle& socket_fd, + const iovec* data, size_t count) { + msghdr msg = {}; + msg.msg_iov = const_cast<iovec*>(data); + msg.msg_iovlen = count; + return RecvMsgAll(&g_socket_receiver, socket_fd, &msg); +} + +size_t CountVectorSize(const iovec* vector, size_t count) { + return std::accumulate( + vector, vector + count, size_t{0}, + [](size_t size, const iovec& vec) { return size + vec.iov_len; }); +} + +void InitRequest(android::pdx::uds::RequestHeader<BorrowedHandle>* request, + int opcode, uint32_t send_len, uint32_t max_recv_len, + bool is_impulse) { + request->op = opcode; + request->cred.pid = getpid(); + request->cred.uid = geteuid(); + request->cred.gid = getegid(); + request->send_len = send_len; + request->max_recv_len = max_recv_len; + request->is_impulse = is_impulse; +} + +Status<void> WaitForEndpoint(const std::string& endpoint_path, + int64_t timeout_ms) { + // Endpoint path must be absolute. + if (endpoint_path.empty() || endpoint_path.front() != '/') + return ErrorStatus(EINVAL); + + // Create inotify fd. + LocalHandle fd{inotify_init()}; + if (!fd) + return ErrorStatus(errno); + + // Set the inotify fd to non-blocking. + int ret = fcntl(fd.Get(), F_GETFL); + fcntl(fd.Get(), F_SETFL, ret | O_NONBLOCK); + + // Setup the pollfd. + pollfd pfd = {fd.Get(), POLLIN, 0}; + + // Find locations of each path separator. + std::vector<size_t> separators{0}; // The path is absolute, so '/' is at #0. + size_t pos = endpoint_path.find('/', 1); + while (pos != std::string::npos) { + separators.push_back(pos); + pos = endpoint_path.find('/', pos + 1); + } + separators.push_back(endpoint_path.size()); + + // Walk down the path, checking for existence and waiting if needed. + pos = 1; + size_t links = 0; + std::string current; + while (pos < separators.size() && links <= MAXSYMLINKS) { + std::string previous = current; + current = endpoint_path.substr(0, separators[pos]); + + // Check for existence; proceed to setup a watch if not. + if (access(current.c_str(), F_OK) < 0) { + if (errno != ENOENT) + return ErrorStatus(errno); + + // Extract the name of the path component to wait for. + std::string next = current.substr( + separators[pos - 1] + 1, separators[pos] - separators[pos - 1] - 1); + + // Add a watch on the last existing directory we reach. + int wd = inotify_add_watch( + fd.Get(), previous.c_str(), + IN_CREATE | IN_DELETE_SELF | IN_MOVE_SELF | IN_MOVED_TO); + if (wd < 0) { + if (errno != ENOENT) + return ErrorStatus(errno); + // Restart at the beginning if previous was deleted. + links = 0; + current.clear(); + pos = 1; + continue; + } + + // Make sure current didn't get created before the watch was added. + ret = access(current.c_str(), F_OK); + if (ret < 0) { + if (errno != ENOENT) + return ErrorStatus(errno); + + bool exit_poll = false; + while (!exit_poll) { + // Wait for an event or timeout. + ret = poll(&pfd, 1, timeout_ms); + if (ret <= 0) + return ErrorStatus(ret == 0 ? ETIMEDOUT : errno); + + // Read events. + char buffer[sizeof(inotify_event) + NAME_MAX + 1]; + + ret = read(fd.Get(), buffer, sizeof(buffer)); + if (ret < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + continue; + else + return ErrorStatus(errno); + } else if (static_cast<size_t>(ret) < sizeof(struct inotify_event)) { + return ErrorStatus(EIO); + } + + auto* event = reinterpret_cast<const inotify_event*>(buffer); + auto* end = reinterpret_cast<const inotify_event*>(buffer + ret); + while (event < end) { + std::string event_for; + if (event->len > 0) + event_for = event->name; + + if (event->mask & (IN_CREATE | IN_MOVED_TO)) { + // See if this is the droid we're looking for. + if (next == event_for) { + exit_poll = true; + break; + } + } else if (event->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) { + // Restart at the beginning if our watch dir is deleted. + links = 0; + current.clear(); + pos = 0; + exit_poll = true; + break; + } + + event = reinterpret_cast<const inotify_event*>(AdvancePointer( + event, sizeof(struct inotify_event) + event->len)); + } // while (event < end) + } // while (!exit_poll) + } // Current dir doesn't exist. + ret = inotify_rm_watch(fd.Get(), wd); + if (ret < 0 && errno != EINVAL) + return ErrorStatus(errno); + } // if (access(current.c_str(), F_OK) < 0) + + // Check for symbolic link and update link count. + struct stat stat_buf; + ret = lstat(current.c_str(), &stat_buf); + if (ret < 0 && errno != ENOENT) + return ErrorStatus(errno); + else if (ret == 0 && S_ISLNK(stat_buf.st_mode)) + links++; + pos++; + } // while (pos < separators.size() && links <= MAXSYMLINKS) + + return {}; +} + +} // namespace uds +} // namespace pdx +} // namespace android diff --git a/libs/vr/libpdx_uds/ipc_helper_tests.cpp b/libs/vr/libpdx_uds/ipc_helper_tests.cpp new file mode 100644 index 0000000000..bfa827e641 --- /dev/null +++ b/libs/vr/libpdx_uds/ipc_helper_tests.cpp @@ -0,0 +1,365 @@ +#include "uds/ipc_helper.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +using testing::Return; +using testing::SetErrnoAndReturn; +using testing::_; + +using android::pdx::BorrowedHandle; +using android::pdx::uds::SendInterface; +using android::pdx::uds::RecvInterface; +using android::pdx::uds::SendAll; +using android::pdx::uds::SendMsgAll; +using android::pdx::uds::RecvAll; +using android::pdx::uds::RecvMsgAll; + +namespace { + +// Useful constants for tests. +static constexpr intptr_t kPtr = 1234; +static constexpr int kSocketFd = 5678; +static const BorrowedHandle kSocket{kSocketFd}; + +// Helper functions to construct test data pointer values. +void* IntToPtr(intptr_t value) { return reinterpret_cast<void*>(value); } +const void* IntToConstPtr(intptr_t value) { + return reinterpret_cast<const void*>(value); +} + +// Mock classes for SendInterface/RecvInterface. +class MockSender : public SendInterface { + public: + MOCK_METHOD4(Send, ssize_t(int socket_fd, const void* data, size_t size, + int flags)); + MOCK_METHOD3(SendMessage, + ssize_t(int socket_fd, const msghdr* msg, int flags)); +}; + +class MockReceiver : public RecvInterface { + public: + MOCK_METHOD4(Receive, + ssize_t(int socket_fd, void* data, size_t size, int flags)); + MOCK_METHOD3(ReceiveMessage, ssize_t(int socket_fd, msghdr* msg, int flags)); +}; + +// Test case classes. +class SendTest : public testing::Test { + public: + SendTest() { + ON_CALL(sender_, Send(_, _, _, _)) + .WillByDefault(SetErrnoAndReturn(EIO, -1)); + ON_CALL(sender_, SendMessage(_, _, _)) + .WillByDefault(SetErrnoAndReturn(EIO, -1)); + } + + protected: + MockSender sender_; +}; + +class RecvTest : public testing::Test { + public: + RecvTest() { + ON_CALL(receiver_, Receive(_, _, _, _)) + .WillByDefault(SetErrnoAndReturn(EIO, -1)); + ON_CALL(receiver_, ReceiveMessage(_, _, _)) + .WillByDefault(SetErrnoAndReturn(EIO, -1)); + } + + protected: + MockReceiver receiver_; +}; + +class MessageTestBase : public testing::Test { + public: + MessageTestBase() { + memset(&msg_, 0, sizeof(msg_)); + msg_.msg_iovlen = data_.size(); + msg_.msg_iov = data_.data(); + } + + protected: + static constexpr intptr_t kPtr1 = kPtr; + static constexpr intptr_t kPtr2 = kPtr + 200; + static constexpr intptr_t kPtr3 = kPtr + 1000; + + MockSender sender_; + msghdr msg_; + std::vector<iovec> data_{ + {IntToPtr(kPtr1), 100}, {IntToPtr(kPtr2), 200}, {IntToPtr(kPtr3), 300}}; +}; + +class SendMessageTest : public MessageTestBase { + public: + SendMessageTest() { + ON_CALL(sender_, Send(_, _, _, _)) + .WillByDefault(SetErrnoAndReturn(EIO, -1)); + ON_CALL(sender_, SendMessage(_, _, _)) + .WillByDefault(SetErrnoAndReturn(EIO, -1)); + } + + protected: + MockSender sender_; +}; + +class RecvMessageTest : public MessageTestBase { + public: + RecvMessageTest() { + ON_CALL(receiver_, Receive(_, _, _, _)) + .WillByDefault(SetErrnoAndReturn(EIO, -1)); + ON_CALL(receiver_, ReceiveMessage(_, _, _)) + .WillByDefault(SetErrnoAndReturn(EIO, -1)); + } + + protected: + MockReceiver receiver_; +}; + +// Actual tests. + +// SendAll +TEST_F(SendTest, Complete) { + EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr), 100, MSG_NOSIGNAL)) + .WillOnce(Return(100)); + + auto status = SendAll(&sender_, kSocket, IntToConstPtr(kPtr), 100); + EXPECT_TRUE(status); +} + +TEST_F(SendTest, Signal) { + EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr), 100, MSG_NOSIGNAL)) + .WillOnce(Return(20)); + EXPECT_CALL(sender_, + Send(kSocketFd, IntToConstPtr(kPtr + 20), 80, MSG_NOSIGNAL)) + .WillOnce(Return(40)); + EXPECT_CALL(sender_, + Send(kSocketFd, IntToConstPtr(kPtr + 60), 40, MSG_NOSIGNAL)) + .WillOnce(Return(40)); + + auto status = SendAll(&sender_, kSocket, IntToConstPtr(kPtr), 100); + EXPECT_TRUE(status); +} + +TEST_F(SendTest, Eintr) { + EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr), 100, MSG_NOSIGNAL)) + .WillOnce(SetErrnoAndReturn(EINTR, -1)) + .WillOnce(Return(100)); + + auto status = SendAll(&sender_, kSocket, IntToConstPtr(kPtr), 100); + EXPECT_TRUE(status); +} + +TEST_F(SendTest, Error) { + EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr), 100, MSG_NOSIGNAL)) + .WillOnce(SetErrnoAndReturn(EIO, -1)); + + auto status = SendAll(&sender_, kSocket, IntToConstPtr(kPtr), 100); + ASSERT_FALSE(status); + EXPECT_EQ(EIO, status.error()); +} + +TEST_F(SendTest, Error2) { + EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr), 100, MSG_NOSIGNAL)) + .WillOnce(Return(50)); + EXPECT_CALL(sender_, + Send(kSocketFd, IntToConstPtr(kPtr + 50), 50, MSG_NOSIGNAL)) + .WillOnce(SetErrnoAndReturn(EIO, -1)); + + auto status = SendAll(&sender_, kSocket, IntToConstPtr(kPtr), 100); + ASSERT_FALSE(status); + EXPECT_EQ(EIO, status.error()); +} + +// RecvAll +TEST_F(RecvTest, Complete) { + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr), 100, + MSG_WAITALL | MSG_CMSG_CLOEXEC)) + .WillOnce(Return(100)); + + auto status = RecvAll(&receiver_, kSocket, IntToPtr(kPtr), 100); + EXPECT_TRUE(status); +} + +TEST_F(RecvTest, Signal) { + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr), 100, _)) + .WillOnce(Return(20)); + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr + 20), 80, _)) + .WillOnce(Return(40)); + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr + 60), 40, _)) + .WillOnce(Return(40)); + + auto status = RecvAll(&receiver_, kSocket, IntToPtr(kPtr), 100); + EXPECT_TRUE(status); +} + +TEST_F(RecvTest, Eintr) { + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr), 100, _)) + .WillOnce(SetErrnoAndReturn(EINTR, -1)) + .WillOnce(Return(100)); + + auto status = RecvAll(&receiver_, kSocket, IntToPtr(kPtr), 100); + EXPECT_TRUE(status); +} + +TEST_F(RecvTest, Error) { + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr), 100, _)) + .WillOnce(SetErrnoAndReturn(EIO, -1)); + + auto status = RecvAll(&receiver_, kSocket, IntToPtr(kPtr), 100); + ASSERT_FALSE(status); + EXPECT_EQ(EIO, status.error()); +} + +TEST_F(RecvTest, Error2) { + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr), 100, _)) + .WillOnce(Return(30)); + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr + 30), 70, _)) + .WillOnce(SetErrnoAndReturn(EIO, -1)); + + auto status = RecvAll(&receiver_, kSocket, IntToPtr(kPtr), 100); + ASSERT_FALSE(status); + EXPECT_EQ(EIO, status.error()); +} + +// SendMsgAll +TEST_F(SendMessageTest, Complete) { + EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, MSG_NOSIGNAL)) + .WillOnce(Return(600)); + + auto status = SendMsgAll(&sender_, kSocket, &msg_); + EXPECT_TRUE(status); +} + +TEST_F(SendMessageTest, Partial) { + EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, _)).WillOnce(Return(70)); + EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr1 + 70), 30, _)) + .WillOnce(Return(30)); + EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr2), 200, _)) + .WillOnce(Return(190)); + EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr2 + 190), 10, _)) + .WillOnce(Return(10)); + EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr3), 300, _)) + .WillOnce(Return(300)); + + auto status = SendMsgAll(&sender_, kSocket, &msg_); + EXPECT_TRUE(status); +} + +TEST_F(SendMessageTest, Partial2) { + EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, _)).WillOnce(Return(310)); + EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr3 + 10), 290, _)) + .WillOnce(Return(290)); + + auto status = SendMsgAll(&sender_, kSocket, &msg_); + EXPECT_TRUE(status); +} + +TEST_F(SendMessageTest, Eintr) { + EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, _)) + .WillOnce(SetErrnoAndReturn(EINTR, -1)) + .WillOnce(Return(70)); + EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr1 + 70), 30, _)) + .WillOnce(SetErrnoAndReturn(EINTR, -1)) + .WillOnce(Return(30)); + EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr2), 200, _)) + .WillOnce(Return(200)); + EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr3), 300, _)) + .WillOnce(Return(300)); + + auto status = SendMsgAll(&sender_, kSocket, &msg_); + EXPECT_TRUE(status); +} + +TEST_F(SendMessageTest, Error) { + EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, _)) + .WillOnce(SetErrnoAndReturn(EBADF, -1)); + + auto status = SendMsgAll(&sender_, kSocket, &msg_); + ASSERT_FALSE(status); + EXPECT_EQ(EBADF, status.error()); +} + +TEST_F(SendMessageTest, Error2) { + EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, _)).WillOnce(Return(20)); + EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr1 + 20), 80, _)) + .WillOnce(SetErrnoAndReturn(EBADF, -1)); + + auto status = SendMsgAll(&sender_, kSocket, &msg_); + ASSERT_FALSE(status); + EXPECT_EQ(EBADF, status.error()); +} + +// RecvMsgAll +TEST_F(RecvMessageTest, Complete) { + EXPECT_CALL(receiver_, + ReceiveMessage(kSocketFd, &msg_, MSG_WAITALL | MSG_CMSG_CLOEXEC)) + .WillOnce(Return(600)); + + auto status = RecvMsgAll(&receiver_, kSocket, &msg_); + EXPECT_TRUE(status); +} + +TEST_F(RecvMessageTest, Partial) { + EXPECT_CALL(receiver_, ReceiveMessage(kSocketFd, &msg_, _)) + .WillOnce(Return(70)); + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr1 + 70), 30, _)) + .WillOnce(Return(30)); + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr2), 200, _)) + .WillOnce(Return(190)); + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr2 + 190), 10, _)) + .WillOnce(Return(10)); + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr3), 300, _)) + .WillOnce(Return(300)); + + auto status = RecvMsgAll(&receiver_, kSocket, &msg_); + EXPECT_TRUE(status); +} + +TEST_F(RecvMessageTest, Partial2) { + EXPECT_CALL(receiver_, ReceiveMessage(kSocketFd, &msg_, _)) + .WillOnce(Return(310)); + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr3 + 10), 290, _)) + .WillOnce(Return(290)); + + auto status = RecvMsgAll(&receiver_, kSocket, &msg_); + EXPECT_TRUE(status); +} + +TEST_F(RecvMessageTest, Eintr) { + EXPECT_CALL(receiver_, ReceiveMessage(kSocketFd, &msg_, _)) + .WillOnce(SetErrnoAndReturn(EINTR, -1)) + .WillOnce(Return(70)); + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr1 + 70), 30, _)) + .WillOnce(SetErrnoAndReturn(EINTR, -1)) + .WillOnce(Return(30)); + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr2), 200, _)) + .WillOnce(Return(200)); + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr3), 300, _)) + .WillOnce(Return(300)); + + auto status = RecvMsgAll(&receiver_, kSocket, &msg_); + EXPECT_TRUE(status); +} + +TEST_F(RecvMessageTest, Error) { + EXPECT_CALL(receiver_, ReceiveMessage(kSocketFd, &msg_, _)) + .WillOnce(SetErrnoAndReturn(EBADF, -1)); + + auto status = RecvMsgAll(&receiver_, kSocket, &msg_); + ASSERT_FALSE(status); + EXPECT_EQ(EBADF, status.error()); +} + +TEST_F(RecvMessageTest, Error2) { + EXPECT_CALL(receiver_, ReceiveMessage(kSocketFd, &msg_, _)) + .WillOnce(Return(20)); + EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr1 + 20), 80, _)) + .WillOnce(SetErrnoAndReturn(EBADF, -1)); + + auto status = RecvMsgAll(&receiver_, kSocket, &msg_); + ASSERT_FALSE(status); + EXPECT_EQ(EBADF, status.error()); +} + +} // namespace diff --git a/libs/vr/libpdx_uds/private/uds/channel_event_set.h b/libs/vr/libpdx_uds/private/uds/channel_event_set.h new file mode 100644 index 0000000000..1f464d5f91 --- /dev/null +++ b/libs/vr/libpdx_uds/private/uds/channel_event_set.h @@ -0,0 +1,62 @@ +#ifndef ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_ +#define ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_ + +#include <errno.h> +#include <poll.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> + +#include <pdx/file_handle.h> +#include <pdx/status.h> + +namespace android { +namespace pdx { +namespace uds { + +class ChannelEventSet { + public: + ChannelEventSet(); + ChannelEventSet(ChannelEventSet&&) = default; + ChannelEventSet& operator=(ChannelEventSet&&) = default; + + BorrowedHandle event_fd() const { return epoll_fd_.Borrow(); } + + explicit operator bool() const { return !!epoll_fd_ && !!event_fd_; } + + Status<void> AddDataFd(const LocalHandle& data_fd); + int ModifyEvents(int clear_mask, int set_mask); + + private: + LocalHandle epoll_fd_; + LocalHandle event_fd_; + uint32_t event_bits_ = 0; + + static Status<void> SetupHandle(int fd, LocalHandle* handle, + const char* error_name); + + ChannelEventSet(const ChannelEventSet&) = delete; + void operator=(const ChannelEventSet&) = delete; +}; + +class ChannelEventReceiver { + public: + ChannelEventReceiver() = default; + ChannelEventReceiver(LocalHandle epoll_fd) : epoll_fd_{std::move(epoll_fd)} {} + ChannelEventReceiver(ChannelEventReceiver&&) = default; + ChannelEventReceiver& operator=(ChannelEventReceiver&&) = default; + + BorrowedHandle event_fd() const { return epoll_fd_.Borrow(); } + Status<int> GetPendingEvents() const; + + private: + LocalHandle epoll_fd_; + + ChannelEventReceiver(const ChannelEventReceiver&) = delete; + void operator=(const ChannelEventReceiver&) = delete; +}; + +} // namespace uds +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_ diff --git a/libs/vr/libpdx_uds/private/uds/channel_manager.h b/libs/vr/libpdx_uds/private/uds/channel_manager.h new file mode 100644 index 0000000000..2aca41421a --- /dev/null +++ b/libs/vr/libpdx_uds/private/uds/channel_manager.h @@ -0,0 +1,40 @@ +#ifndef ANDROID_PDX_UDS_CHANNEL_MANAGER_H_ +#define ANDROID_PDX_UDS_CHANNEL_MANAGER_H_ + +#include <mutex> +#include <unordered_map> + +#include <pdx/channel_handle.h> +#include <pdx/file_handle.h> +#include <uds/channel_event_set.h> + +namespace android { +namespace pdx { +namespace uds { + +class ChannelManager : public ChannelManagerInterface { + public: + static ChannelManager& Get(); + + LocalChannelHandle CreateHandle(LocalHandle data_fd, LocalHandle event_fd); + struct ChannelData { + LocalHandle data_fd; + ChannelEventReceiver event_receiver; + }; + + ChannelData* GetChannelData(int32_t handle); + + private: + ChannelManager() = default; + + void CloseHandle(int32_t handle) override; + + std::mutex mutex_; + std::unordered_map<int32_t, ChannelData> channels_; +}; + +} // namespace uds +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_UDS_CHANNEL_MANAGER_H_ diff --git a/libs/vr/libpdx_uds/private/uds/client_channel.h b/libs/vr/libpdx_uds/private/uds/client_channel.h new file mode 100644 index 0000000000..8f607f56a1 --- /dev/null +++ b/libs/vr/libpdx_uds/private/uds/client_channel.h @@ -0,0 +1,85 @@ +#ifndef ANDROID_PDX_UDS_CLIENT_CHANNEL_H_ +#define ANDROID_PDX_UDS_CLIENT_CHANNEL_H_ + +#include <pdx/client_channel.h> + +#include <mutex> + +#include <uds/channel_event_set.h> +#include <uds/channel_manager.h> +#include <uds/service_endpoint.h> + +namespace android { +namespace pdx { +namespace uds { + +class ClientChannel : public pdx::ClientChannel { + public: + ~ClientChannel() override; + + static std::unique_ptr<pdx::ClientChannel> Create( + LocalChannelHandle channel_handle); + + uint32_t GetIpcTag() const override { return Endpoint::kIpcTag; } + + int event_fd() const override { + return channel_data_ ? channel_data_->event_receiver.event_fd().Get() : -1; + } + Status<int> GetEventMask(int /*events*/) override { + if (channel_data_) + return channel_data_->event_receiver.GetPendingEvents(); + else + return ErrorStatus(EINVAL); + } + + LocalChannelHandle& GetChannelHandle() override { return channel_handle_; } + void* AllocateTransactionState() override; + void FreeTransactionState(void* state) override; + + Status<void> SendImpulse(int opcode, const void* buffer, + size_t length) override; + + Status<int> SendWithInt(void* transaction_state, int opcode, + const iovec* send_vector, size_t send_count, + const iovec* receive_vector, + size_t receive_count) override; + Status<LocalHandle> SendWithFileHandle(void* transaction_state, int opcode, + const iovec* send_vector, + size_t send_count, + const iovec* receive_vector, + size_t receive_count) override; + Status<LocalChannelHandle> SendWithChannelHandle( + void* transaction_state, int opcode, const iovec* send_vector, + size_t send_count, const iovec* receive_vector, + size_t receive_count) override; + + FileReference PushFileHandle(void* transaction_state, + const LocalHandle& handle) override; + FileReference PushFileHandle(void* transaction_state, + const BorrowedHandle& handle) override; + ChannelReference PushChannelHandle(void* transaction_state, + const LocalChannelHandle& handle) override; + ChannelReference PushChannelHandle( + void* transaction_state, const BorrowedChannelHandle& handle) override; + bool GetFileHandle(void* transaction_state, FileReference ref, + LocalHandle* handle) const override; + bool GetChannelHandle(void* transaction_state, ChannelReference ref, + LocalChannelHandle* handle) const override; + + private: + explicit ClientChannel(LocalChannelHandle channel_handle); + + Status<int> SendAndReceive(void* transaction_state, int opcode, + const iovec* send_vector, size_t send_count, + const iovec* receive_vector, size_t receive_count); + + LocalChannelHandle channel_handle_; + ChannelManager::ChannelData* channel_data_; + std::mutex socket_mutex_; +}; + +} // namespace uds +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_UDS_CLIENT_CHANNEL_H_ diff --git a/libs/vr/libpdx_uds/private/uds/client_channel_factory.h b/libs/vr/libpdx_uds/private/uds/client_channel_factory.h new file mode 100644 index 0000000000..c43c5c7c49 --- /dev/null +++ b/libs/vr/libpdx_uds/private/uds/client_channel_factory.h @@ -0,0 +1,36 @@ +#ifndef ANDROID_PDX_UDS_CLIENT_CHANNEL_FACTORY_H_ +#define ANDROID_PDX_UDS_CLIENT_CHANNEL_FACTORY_H_ + +#include <string> + +#include <pdx/client_channel_factory.h> + +namespace android { +namespace pdx { +namespace uds { + +class ClientChannelFactory : public pdx::ClientChannelFactory { + public: + static std::unique_ptr<pdx::ClientChannelFactory> Create( + const std::string& endpoint_path); + static std::unique_ptr<pdx::ClientChannelFactory> Create(LocalHandle socket); + + Status<std::unique_ptr<pdx::ClientChannel>> Connect( + int64_t timeout_ms) const override; + + static std::string GetRootEndpointPath(); + static std::string GetEndpointPath(const std::string& endpoint_path); + + private: + explicit ClientChannelFactory(const std::string& endpoint_path); + explicit ClientChannelFactory(LocalHandle socket); + + mutable LocalHandle socket_; + std::string endpoint_path_; +}; + +} // namespace uds +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_UDS_CLIENT_CHANNEL_FACTORY_H_ diff --git a/libs/vr/libpdx_uds/private/uds/ipc_helper.h b/libs/vr/libpdx_uds/private/uds/ipc_helper.h new file mode 100644 index 0000000000..bde16d3d31 --- /dev/null +++ b/libs/vr/libpdx_uds/private/uds/ipc_helper.h @@ -0,0 +1,214 @@ +#ifndef ANDROID_PDX_UDS_IPC_HELPER_H_ +#define ANDROID_PDX_UDS_IPC_HELPER_H_ + +#include <sys/socket.h> +#include <utility> +#include <vector> + +#include <pdx/rpc/serializable.h> +#include <pdx/rpc/serialization.h> +#include <pdx/status.h> +#include <pdx/utility.h> + +namespace android { +namespace pdx { +namespace uds { + +// Test interfaces used for unit-testing payload sending/receiving over sockets. +class SendInterface { + public: + virtual ssize_t Send(int socket_fd, const void* data, size_t size, + int flags) = 0; + virtual ssize_t SendMessage(int socket_fd, const msghdr* msg, int flags) = 0; + + protected: + virtual ~SendInterface() = default; +}; + +class RecvInterface { + public: + virtual ssize_t Receive(int socket_fd, void* data, size_t size, + int flags) = 0; + virtual ssize_t ReceiveMessage(int socket_fd, msghdr* msg, int flags) = 0; + + protected: + virtual ~RecvInterface() = default; +}; + +// Helper methods that allow to send/receive data through abstract interfaces. +// Useful for mocking out the underlying socket I/O. +Status<void> SendAll(SendInterface* sender, const BorrowedHandle& socket_fd, + const void* data, size_t size); +Status<void> SendMsgAll(SendInterface* sender, const BorrowedHandle& socket_fd, + const msghdr* msg); +Status<void> RecvAll(RecvInterface* receiver, const BorrowedHandle& socket_fd, + void* data, size_t size); +Status<void> RecvMsgAll(RecvInterface* receiver, + const BorrowedHandle& socket_fd, msghdr* msg); + +#define RETRY_EINTR(fnc_call) \ + ([&]() -> decltype(fnc_call) { \ + decltype(fnc_call) result; \ + do { \ + result = (fnc_call); \ + } while (result == -1 && errno == EINTR); \ + return result; \ + })() + +class SendPayload : public MessageWriter, public OutputResourceMapper { + public: + SendPayload(SendInterface* sender = nullptr) : sender_{sender} {} + Status<void> Send(const BorrowedHandle& socket_fd); + Status<void> Send(const BorrowedHandle& socket_fd, const ucred* cred); + + // MessageWriter + void* GetNextWriteBufferSection(size_t size) override; + OutputResourceMapper* GetOutputResourceMapper() override; + + // OutputResourceMapper + Status<FileReference> PushFileHandle(const LocalHandle& handle) override; + Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override; + Status<FileReference> PushFileHandle(const RemoteHandle& handle) override; + Status<ChannelReference> PushChannelHandle( + const LocalChannelHandle& handle) override; + Status<ChannelReference> PushChannelHandle( + const BorrowedChannelHandle& handle) override; + Status<ChannelReference> PushChannelHandle( + const RemoteChannelHandle& handle) override; + + private: + SendInterface* sender_; + ByteBuffer buffer_; + std::vector<int> file_handles_; +}; + +class ReceivePayload : public MessageReader, public InputResourceMapper { + public: + ReceivePayload(RecvInterface* receiver = nullptr) : receiver_{receiver} {} + Status<void> Receive(const BorrowedHandle& socket_fd); + Status<void> Receive(const BorrowedHandle& socket_fd, ucred* cred); + + // MessageReader + BufferSection GetNextReadBufferSection() override; + void ConsumeReadBufferSectionData(const void* new_start) override; + InputResourceMapper* GetInputResourceMapper() override; + + // InputResourceMapper + bool GetFileHandle(FileReference ref, LocalHandle* handle) override; + bool GetChannelHandle(ChannelReference ref, + LocalChannelHandle* handle) override; + + private: + RecvInterface* receiver_; + ByteBuffer buffer_; + std::vector<LocalHandle> file_handles_; + size_t read_pos_{0}; +}; + +template <typename FileHandleType> +class ChannelInfo { + public: + FileHandleType data_fd; + FileHandleType event_fd; + + private: + PDX_SERIALIZABLE_MEMBERS(ChannelInfo, data_fd, event_fd); +}; + +template <typename FileHandleType> +class ChannelConnectionInfo { + public: + FileHandleType channel_fd; + + private: + PDX_SERIALIZABLE_MEMBERS(ChannelConnectionInfo, channel_fd); +}; + +template <typename FileHandleType> +class RequestHeader { + public: + int32_t op{0}; + ucred cred; + uint32_t send_len{0}; + uint32_t max_recv_len{0}; + std::vector<FileHandleType> file_descriptors; + std::vector<ChannelInfo<FileHandleType>> channels; + std::array<uint8_t, 32> impulse_payload; + bool is_impulse{false}; + + private: + PDX_SERIALIZABLE_MEMBERS(RequestHeader, op, send_len, max_recv_len, + file_descriptors, channels, impulse_payload, + is_impulse); +}; + +template <typename FileHandleType> +class ResponseHeader { + public: + int32_t ret_code{0}; + uint32_t recv_len{0}; + std::vector<FileHandleType> file_descriptors; + std::vector<ChannelInfo<FileHandleType>> channels; + + private: + PDX_SERIALIZABLE_MEMBERS(ResponseHeader, ret_code, recv_len, file_descriptors, + channels); +}; + +template <typename T> +inline Status<void> SendData(const BorrowedHandle& socket_fd, const T& data) { + SendPayload payload; + rpc::Serialize(data, &payload); + return payload.Send(socket_fd); +} + +template <typename FileHandleType> +inline Status<void> SendData(const BorrowedHandle& socket_fd, + const RequestHeader<FileHandleType>& request) { + SendPayload payload; + rpc::Serialize(request, &payload); + return payload.Send(socket_fd, &request.cred); +} + +Status<void> SendData(const BorrowedHandle& socket_fd, const void* data, + size_t size); +Status<void> SendDataVector(const BorrowedHandle& socket_fd, const iovec* data, + size_t count); + +template <typename T> +inline Status<void> ReceiveData(const BorrowedHandle& socket_fd, T* data) { + ReceivePayload payload; + Status<void> status = payload.Receive(socket_fd); + if (status && rpc::Deserialize(data, &payload) != rpc::ErrorCode::NO_ERROR) + status.SetError(EIO); + return status; +} + +template <typename FileHandleType> +inline Status<void> ReceiveData(const BorrowedHandle& socket_fd, + RequestHeader<FileHandleType>* request) { + ReceivePayload payload; + Status<void> status = payload.Receive(socket_fd, &request->cred); + if (status && rpc::Deserialize(request, &payload) != rpc::ErrorCode::NO_ERROR) + status.SetError(EIO); + return status; +} + +Status<void> ReceiveData(const BorrowedHandle& socket_fd, void* data, + size_t size); +Status<void> ReceiveDataVector(const BorrowedHandle& socket_fd, + const iovec* data, size_t count); + +size_t CountVectorSize(const iovec* data, size_t count); +void InitRequest(android::pdx::uds::RequestHeader<BorrowedHandle>* request, + int opcode, uint32_t send_len, uint32_t max_recv_len, + bool is_impulse); + +Status<void> WaitForEndpoint(const std::string& endpoint_path, + int64_t timeout_ms); + +} // namespace uds +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_UDS_IPC_HELPER_H_ diff --git a/libs/vr/libpdx_uds/private/uds/service_dispatcher.h b/libs/vr/libpdx_uds/private/uds/service_dispatcher.h new file mode 100644 index 0000000000..23af4f4403 --- /dev/null +++ b/libs/vr/libpdx_uds/private/uds/service_dispatcher.h @@ -0,0 +1,55 @@ +#ifndef ANDROID_PDX_UDS_SERVICE_DISPATCHER_H_ +#define ANDROID_PDX_UDS_SERVICE_DISPATCHER_H_ + +#include <list> +#include <memory> +#include <mutex> +#include <unordered_map> + +#include <pdx/file_handle.h> +#include <pdx/service_dispatcher.h> + +namespace android { +namespace pdx { +namespace uds { + +class ServiceDispatcher : public pdx::ServiceDispatcher { + public: + // Get a new instance of ServiceDispatcher, or return nullptr if init failed. + static std::unique_ptr<pdx::ServiceDispatcher> Create(); + + ~ServiceDispatcher() override; + int AddService(const std::shared_ptr<Service>& service) override; + int RemoveService(const std::shared_ptr<Service>& service) override; + int ReceiveAndDispatch() override; + int ReceiveAndDispatch(int timeout) override; + int EnterDispatchLoop() override; + void SetCanceled(bool cancel) override; + bool IsCanceled() const override; + + private: + ServiceDispatcher(); + + // Internal thread accounting. + int ThreadEnter(); + void ThreadExit(); + + std::mutex mutex_; + std::condition_variable condition_; + std::atomic<bool> canceled_{false}; + + std::list<std::shared_ptr<Service>> services_; + + int thread_count_ = 0; + LocalHandle event_fd_; + LocalHandle epoll_fd_; + + ServiceDispatcher(const ServiceDispatcher&) = delete; + void operator=(const ServiceDispatcher&) = delete; +}; + +} // namespace uds +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_UDS_SERVICE_DISPATCHER_H_ diff --git a/libs/vr/libpdx_uds/private/uds/service_endpoint.h b/libs/vr/libpdx_uds/private/uds/service_endpoint.h new file mode 100644 index 0000000000..368891ce05 --- /dev/null +++ b/libs/vr/libpdx_uds/private/uds/service_endpoint.h @@ -0,0 +1,167 @@ +#ifndef ANDROID_PDX_UDS_SERVICE_ENDPOINT_H_ +#define ANDROID_PDX_UDS_SERVICE_ENDPOINT_H_ + +#include <sys/stat.h> +#include <map> +#include <memory> +#include <mutex> +#include <string> +#include <unordered_map> +#include <vector> + +#include <pdx/service.h> +#include <pdx/service_endpoint.h> +#include <uds/channel_event_set.h> +#include <uds/service_dispatcher.h> + +namespace android { +namespace pdx { +namespace uds { + +class Endpoint : public pdx::Endpoint { + public: + enum { + kIpcTag = 0x00736674, // 'uds' + }; + + // Blocking modes for service endpoint. Controls whether the epoll set is in + // blocking mode or not for message receive. + enum { + kBlocking = true, + kNonBlocking = false, + kDefaultBlocking = kNonBlocking, + }; + + enum : mode_t { + kDefaultMode = 0, + }; + + ~Endpoint() override = default; + + uint32_t GetIpcTag() const override { return kIpcTag; } + Status<void> SetService(Service* service) override; + Status<void> SetChannel(int channel_id, Channel* channel) override; + Status<void> CloseChannel(int channel_id) override; + Status<void> ModifyChannelEvents(int channel_id, int clear_mask, + int set_mask) override; + Status<RemoteChannelHandle> PushChannel(Message* message, int flags, + Channel* channel, + int* channel_id) override; + Status<int> CheckChannel(const Message* message, ChannelReference ref, + Channel** channel) override; + Status<void> MessageReceive(Message* message) override; + Status<void> MessageReply(Message* message, int return_code) override; + Status<void> MessageReplyFd(Message* message, unsigned int push_fd) override; + Status<void> MessageReplyChannelHandle( + Message* message, const LocalChannelHandle& handle) override; + Status<void> MessageReplyChannelHandle( + Message* message, const BorrowedChannelHandle& handle) override; + Status<void> MessageReplyChannelHandle( + Message* message, const RemoteChannelHandle& handle) override; + Status<size_t> ReadMessageData(Message* message, const iovec* vector, + size_t vector_length) override; + Status<size_t> WriteMessageData(Message* message, const iovec* vector, + size_t vector_length) override; + Status<FileReference> PushFileHandle(Message* message, + const LocalHandle& handle) override; + Status<FileReference> PushFileHandle(Message* message, + const BorrowedHandle& handle) override; + Status<FileReference> PushFileHandle(Message* message, + const RemoteHandle& handle) override; + Status<ChannelReference> PushChannelHandle( + Message* message, const LocalChannelHandle& handle) override; + Status<ChannelReference> PushChannelHandle( + Message* message, const BorrowedChannelHandle& handle) override; + Status<ChannelReference> PushChannelHandle( + Message* message, const RemoteChannelHandle& handle) override; + LocalHandle GetFileHandle(Message* message, FileReference ref) const override; + LocalChannelHandle GetChannelHandle(Message* message, + ChannelReference ref) const override; + + void* AllocateMessageState() override; + void FreeMessageState(void* state) override; + + Status<void> Cancel() override; + + // Open an endpoint at the given path. + // Second parameter is unused for UDS, but we have it here for compatibility + // in signature with servicefs::Endpoint::Create(). + // This method uses |endpoint_path| as a relative path to endpoint socket + // created by init process. + static std::unique_ptr<Endpoint> Create(const std::string& endpoint_path, + mode_t /*unused_mode*/ = kDefaultMode, + bool blocking = kDefaultBlocking); + + // Helper method to create an endpoint at the given UDS socket path. This + // method physically creates and binds a socket at that path. + static std::unique_ptr<Endpoint> CreateAndBindSocket( + const std::string& endpoint_path, bool blocking = kDefaultBlocking); + + // Helper method to create an endpoint from an existing socket FD. + // Mostly helpful for tests. + static std::unique_ptr<Endpoint> CreateFromSocketFd(LocalHandle socket_fd); + + // Test helper method to register a new channel identified by |channel_fd| + // socket file descriptor. + Status<void> RegisterNewChannelForTests(LocalHandle channel_fd); + + int epoll_fd() const { return epoll_fd_.Get(); } + + private: + struct ChannelData { + LocalHandle data_fd; + ChannelEventSet event_set; + Channel* channel_state{nullptr}; + }; + + // This class must be instantiated using Create() static methods above. + Endpoint(const std::string& endpoint_path, bool blocking, + bool use_init_socket_fd = true); + Endpoint(LocalHandle socket_fd); + + void Init(LocalHandle socket_fd); + + Endpoint(const Endpoint&) = delete; + void operator=(const Endpoint&) = delete; + + uint32_t GetNextAvailableMessageId() { + return next_message_id_.fetch_add(1, std::memory_order_relaxed); + } + + void BuildCloseMessage(int32_t channel_id, Message* message); + + Status<void> AcceptConnection(Message* message); + Status<void> ReceiveMessageForChannel(const BorrowedHandle& channel_fd, + Message* message); + Status<void> OnNewChannel(LocalHandle channel_fd); + Status<std::pair<int32_t, ChannelData*>> OnNewChannelLocked( + LocalHandle channel_fd, Channel* channel_state); + Status<void> CloseChannelLocked(int32_t channel_id); + Status<void> ReenableEpollEvent(const BorrowedHandle& channel_fd); + Channel* GetChannelState(int32_t channel_id); + BorrowedHandle GetChannelSocketFd(int32_t channel_id); + BorrowedHandle GetChannelEventFd(int32_t channel_id); + int32_t GetChannelId(const BorrowedHandle& channel_fd); + Status<void> CreateChannelSocketPair(LocalHandle* local_socket, + LocalHandle* remote_socket); + + std::string endpoint_path_; + bool is_blocking_; + LocalHandle socket_fd_; + LocalHandle cancel_event_fd_; + LocalHandle epoll_fd_; + + mutable std::mutex channel_mutex_; + std::map<int32_t, ChannelData> channels_; + std::map<int, int32_t> channel_fd_to_id_; + int32_t last_channel_id_{0}; + + Service* service_{nullptr}; + std::atomic<uint32_t> next_message_id_; +}; + +} // namespace uds +} // namespace pdx +} // namespace android + +#endif // ANDROID_PDX_UDS_PDX_SERVICE_ENDPOINT_H_ diff --git a/libs/vr/libpdx_uds/remote_method_tests.cpp b/libs/vr/libpdx_uds/remote_method_tests.cpp new file mode 100644 index 0000000000..3109753dc2 --- /dev/null +++ b/libs/vr/libpdx_uds/remote_method_tests.cpp @@ -0,0 +1,951 @@ +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include <array> +#include <cstdint> +#include <memory> +#include <numeric> +#include <string> +#include <thread> + +#include <gtest/gtest.h> +#include <pdx/channel_handle.h> +#include <pdx/client.h> +#include <pdx/rpc/remote_method.h> +#include <pdx/rpc/serializable.h> +#include <pdx/service.h> +#include <uds/client_channel.h> +#include <uds/client_channel_factory.h> +#include <uds/service_dispatcher.h> +#include <uds/service_endpoint.h> + +using android::pdx::BorrowedHandle; +using android::pdx::Channel; +using android::pdx::ClientBase; +using android::pdx::ErrorStatus; +using android::pdx::LocalChannelHandle; +using android::pdx::LocalHandle; +using android::pdx::Message; +using android::pdx::RemoteChannelHandle; +using android::pdx::RemoteHandle; +using android::pdx::ServiceBase; +using android::pdx::ServiceDispatcher; +using android::pdx::Status; +using android::pdx::uds::Endpoint; +using namespace android::pdx::rpc; + +namespace { + +std::string Rot13(const std::string& s) { + std::string text = s; + std::transform(std::begin(text), std::end(text), std::begin(text), + [](char c) -> char { + if (!std::isalpha(c)) { + return c; + } else { + const char pivot = std::isupper(c) ? 'A' : 'a'; + return (c - pivot + 13) % 26 + pivot; + } + }); + return text; +} + +// Defines a serializable user type that may be transferred between client and +// service. +struct TestType { + int a; + float b; + std::string c; + + TestType() {} + TestType(int a, float b, const std::string& c) : a(a), b(b), c(c) {} + + // Make gtest expressions simpler by defining equality operator. This is not + // needed for serialization. + bool operator==(const TestType& other) const { + return a == other.a && b == other.b && c == other.c; + } + + private: + PDX_SERIALIZABLE_MEMBERS(TestType, a, b, c); +}; + +struct DerivedTestType : public TestType { + DerivedTestType() : TestType() {} + DerivedTestType(int a, float b) : TestType(a, b, "constant") {} +}; + +// Defines a serializable user type with a LocalHandle member. +struct TestFdType { + int a; + LocalHandle fd; + + TestFdType() {} + TestFdType(int a, LocalHandle fd) : a(a), fd(std::move(fd)) {} + + private: + PDX_SERIALIZABLE_MEMBERS(TestFdType, a, fd); +}; + +// Defines a serializable user template type with a FileHandle member. +template <typename FileHandleType> +struct TestTemplateType { + FileHandleType fd; + + TestTemplateType() {} + TestTemplateType(FileHandleType fd) : fd(std::move(fd)) {} + + private: + PDX_SERIALIZABLE_MEMBERS(TestTemplateType<FileHandleType>, fd); +}; + +struct BasicStruct { + int a; + int b; + std::string c; + + private: + PDX_SERIALIZABLE_MEMBERS(BasicStruct, a, b, c); +}; + +using BasicStructTraits = SerializableTraits<BasicStruct>; + +struct NonSerializableType { + int a; + int b; + std::string c; +}; + +struct IncorrectlyDefinedSerializableType { + int a; + int b; + + private: + using SerializableMembers = std::tuple<int, int>; +}; + +// Defines the contract between the client and service, including ServiceFS +// endpoint path, method opcodes, and remote method signatures. +struct TestInterface final { + // Service path. + static constexpr char kClientPath[] = "socket_test"; + + // Op codes. + enum { + kOpAdd = 0, + kOpFoo, + kOpConcatenate, + kOpWriteBuffer, + kOpStringLength, + kOpSendTestType, + kOpSendBasicStruct, + kOpSendVector, + kOpRot13, + kOpNoArgs, + kOpSendFile, + kOpGetFile, + kOpGetTestFdType, + kOpOpenFiles, + kOpReadFile, + kOpPushChannel, + kOpPositive, + }; + + // Methods. + PDX_REMOTE_METHOD(Add, kOpAdd, int(int, int)); + PDX_REMOTE_METHOD(Foo, kOpFoo, int(int, const std::string&)); + PDX_REMOTE_METHOD(Concatenate, kOpConcatenate, + std::string(const std::string&, const std::string&)); + PDX_REMOTE_METHOD(SumVector, kOpWriteBuffer, int(const std::vector<int>&)); + PDX_REMOTE_METHOD(StringLength, kOpStringLength, int(const std::string&)); + PDX_REMOTE_METHOD(SendTestType, kOpSendTestType, TestType(const TestType&)); + PDX_REMOTE_METHOD(SendBasicStruct, kOpSendBasicStruct, + BasicStruct(const BasicStruct&)); + PDX_REMOTE_METHOD(SendVector, kOpSendVector, + std::string(const std::vector<TestType>&)); + PDX_REMOTE_METHOD(Rot13, kOpRot13, std::string(const std::string&)); + PDX_REMOTE_METHOD(NoArgs, kOpNoArgs, int(Void)); + PDX_REMOTE_METHOD(SendFile, kOpSendFile, int(const LocalHandle& fd)); + PDX_REMOTE_METHOD(GetFile, kOpGetFile, LocalHandle(const std::string&, int)); + PDX_REMOTE_METHOD(GetTestFdType, kOpGetTestFdType, + TestFdType(int, const std::string&, int)); + PDX_REMOTE_METHOD(OpenFiles, kOpOpenFiles, + std::vector<LocalHandle>( + const std::vector<std::pair<std::string, int>>&)); + PDX_REMOTE_METHOD(ReadFile, kOpReadFile, + std::pair<int, BufferWrapper<std::uint8_t*>>( + const std::string&, int, std::size_t)); + PDX_REMOTE_METHOD(PushChannel, kOpPushChannel, LocalChannelHandle(Void)); + PDX_REMOTE_METHOD(Positive, kOpPositive, void(int)); + + PDX_REMOTE_API(API, Add, Foo, Concatenate, SumVector, StringLength, + SendTestType, SendVector, Rot13, NoArgs, SendFile, GetFile, + GetTestFdType, OpenFiles, PushChannel, Positive); +}; + +constexpr char TestInterface::kClientPath[]; + +// Test client to send messages to the test service. +class TestClient : public ClientBase<TestClient> { + public: + int Add(int a, int b) { + return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::Add>(a, b)); + } + + int Foo(int a, const std::string& b) { + return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::Foo>(a, b)); + } + + std::string Concatenate(const std::string& a, const std::string& b) { + std::string return_value; + + Status<std::string> status = + InvokeRemoteMethod<TestInterface::Concatenate>(a, b); + if (!status) + return std::string("[Error]"); + else + return status.take(); + } + + int SumVector(const int* buffer, std::size_t size) { + return ReturnStatusOrError( + InvokeRemoteMethod<TestInterface::SumVector>(WrapArray(buffer, size))); + } + + int SumVector(const std::vector<int>& buffer) { + return ReturnStatusOrError( + InvokeRemoteMethod<TestInterface::SumVector>(buffer)); + } + + int StringLength(const char* string, std::size_t size) { + return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::StringLength>( + WrapString(string, size))); + } + + int StringLength(const std::string& string) { + return ReturnStatusOrError( + InvokeRemoteMethod<TestInterface::StringLength>(string)); + } + + TestType SendTestType(const TestType& tt) { + Status<TestType> status = + InvokeRemoteMethod<TestInterface::SendTestType>(tt); + if (!status) + return TestType(0, 0.0, "[Error]"); + else + return status.take(); + } + + BasicStruct SendBasicStruct(const BasicStruct& bs) { + Status<BasicStruct> status = + InvokeRemoteMethod<TestInterface::SendBasicStruct>(bs); + if (!status) + return BasicStruct{0, 0, "[Error]"}; + else + return status.take(); + } + + std::string SendVector(const std::vector<TestType>& v) { + Status<std::string> status = + InvokeRemoteMethod<TestInterface::SendVector>(v); + if (!status) + return "[Error]"; + else + return status.take(); + } + + std::string Rot13(const std::string& string) { + Status<std::string> status = + InvokeRemoteMethod<TestInterface::Rot13>(string); + return status ? status.get() : string; + } + + int NoArgs() { + return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::NoArgs>()); + } + + int SendFile(const LocalHandle& fd) { + return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::SendFile>(fd)); + } + + LocalHandle GetFile(const std::string& path, int mode) { + Status<LocalHandle> status = + InvokeRemoteMethod<TestInterface::GetFile>(path, mode); + if (!status) + return LocalHandle(-status.error()); + else + return status.take(); + } + + int GetFile(const std::string& path, int mode, LocalHandle* fd_out) { + Status<void> status = + InvokeRemoteMethodInPlace<TestInterface::GetFile>(fd_out, path, mode); + return status ? 0 : -status.error(); + } + + TestFdType GetTestFdType(int a, const std::string& path, int mode) { + Status<TestFdType> status = + InvokeRemoteMethod<TestInterface::GetTestFdType>(a, path, mode); + if (!status) + return {}; + else + return status.take(); + } + + std::vector<LocalHandle> OpenFiles( + const std::vector<std::pair<std::string, int>>& file_specs) { + Status<std::vector<LocalHandle>> status = + InvokeRemoteMethod<TestInterface::OpenFiles>(file_specs); + if (!status) + return {}; + else + return status.take(); + } + + int ReadFile(void* buffer, std::size_t size, const std::string& path, + int mode) { + auto buffer_wrapper = WrapBuffer(buffer, size); + auto return_value = std::make_pair(-1, buffer_wrapper); + + Status<void> status = InvokeRemoteMethodInPlace<TestInterface::ReadFile>( + &return_value, path, mode, size); + return status ? return_value.first : -status.error(); + } + + int PushChannel(LocalChannelHandle* fd_out) { + auto status = InvokeRemoteMethodInPlace<TestInterface::PushChannel>(fd_out); + return status ? 0 : -status.error(); + } + + bool Positive(int test_value) { + auto status = InvokeRemoteMethod<TestInterface::Positive>(test_value); + return status.ok(); + } + + int GetFd() const { return event_fd(); } + + private: + friend BASE; + + TestClient(LocalChannelHandle channel_handle) + : BASE{android::pdx::uds::ClientChannel::Create( + std::move(channel_handle))} {} + TestClient() + : BASE{android::pdx::uds::ClientChannelFactory::Create( + TestInterface::kClientPath)} {} + + TestClient(const TestClient&) = delete; + void operator=(const TestClient&) = delete; +}; + +// Test service that encodes/decodes messages from clients. +class TestService : public ServiceBase<TestService> { + public: + Status<void> HandleMessage(Message& message) override { + switch (message.GetOp()) { + case TestInterface::Add::Opcode: + DispatchRemoteMethod<TestInterface::Add>(*this, &TestService::OnAdd, + message); + return {}; + + case TestInterface::Foo::Opcode: + DispatchRemoteMethod<TestInterface::Foo>(*this, &TestService::OnFoo, + message); + return {}; + + case TestInterface::Concatenate::Opcode: + DispatchRemoteMethod<TestInterface::Concatenate>( + *this, &TestService::OnConcatenate, message); + return {}; + + case TestInterface::SumVector::Opcode: + DispatchRemoteMethod<TestInterface::SumVector>( + *this, &TestService::OnSumVector, message); + return {}; + + case TestInterface::StringLength::Opcode: + DispatchRemoteMethod<TestInterface::StringLength>( + *this, &TestService::OnStringLength, message); + return {}; + + case TestInterface::SendTestType::Opcode: + DispatchRemoteMethod<TestInterface::SendTestType>( + *this, &TestService::OnSendTestType, message); + return {}; + + case TestInterface::SendVector::Opcode: + DispatchRemoteMethod<TestInterface::SendVector>( + *this, &TestService::OnSendVector, message); + return {}; + + case TestInterface::Rot13::Opcode: + DispatchRemoteMethod<TestInterface::Rot13>(*this, &TestService::OnRot13, + message); + return {}; + + case TestInterface::NoArgs::Opcode: + DispatchRemoteMethod<TestInterface::NoArgs>( + *this, &TestService::OnNoArgs, message); + return {}; + + case TestInterface::SendFile::Opcode: + DispatchRemoteMethod<TestInterface::SendFile>( + *this, &TestService::OnSendFile, message); + return {}; + + case TestInterface::GetFile::Opcode: + DispatchRemoteMethod<TestInterface::GetFile>( + *this, &TestService::OnGetFile, message); + return {}; + + case TestInterface::GetTestFdType::Opcode: + DispatchRemoteMethod<TestInterface::GetTestFdType>( + *this, &TestService::OnGetTestFdType, message); + return {}; + + case TestInterface::OpenFiles::Opcode: + DispatchRemoteMethod<TestInterface::OpenFiles>( + *this, &TestService::OnOpenFiles, message); + return {}; + + case TestInterface::ReadFile::Opcode: + DispatchRemoteMethod<TestInterface::ReadFile>( + *this, &TestService::OnReadFile, message); + return {}; + + case TestInterface::PushChannel::Opcode: + DispatchRemoteMethod<TestInterface::PushChannel>( + *this, &TestService::OnPushChannel, message); + return {}; + + case TestInterface::Positive::Opcode: + DispatchRemoteMethod<TestInterface::Positive>( + *this, &TestService::OnPositive, message); + return {}; + + default: + return Service::DefaultHandleMessage(message); + } + } + + private: + friend BASE; + + TestService() + : BASE("TestService", + Endpoint::CreateAndBindSocket(TestInterface::kClientPath)) {} + + int OnAdd(Message&, int a, int b) { return a + b; } + + int OnFoo(Message&, int a, const std::string& b) { return a + b.length(); } + + std::string OnConcatenate(Message&, const std::string& a, + const std::string& b) { + return a + b; + } + + int OnSumVector(Message&, const std::vector<int>& vector) { + return std::accumulate(vector.begin(), vector.end(), 0); + } + + int OnStringLength(Message&, const std::string& string) { + return string.length(); + } + + TestType OnSendTestType(Message&, const TestType& tt) { + return TestType(tt.a + 20, tt.b - 2.0, tt.c + "foo"); + } + + std::string OnSendVector(Message&, const std::vector<TestType>& v) { + std::string return_value = ""; + + for (const auto& tt : v) + return_value += tt.c; + + return return_value; + } + + Status<std::string> OnRot13(Message&, const std::string& s) { + return {Rot13(s)}; + } + + int OnNoArgs(Message&) { return 1; } + + int OnSendFile(Message&, const LocalHandle& fd) { return fd.Get(); } + + LocalHandle OnGetFile(Message& message, const std::string& path, int mode) { + LocalHandle fd(path.c_str(), mode); + if (!fd) + message.ReplyError(errno); + return fd; + } + + TestFdType OnGetTestFdType(Message& message, int a, const std::string& path, + int mode) { + TestFdType return_value(a, LocalHandle(path, mode)); + if (!return_value.fd) + message.ReplyError(errno); + return return_value; + } + + std::vector<LocalHandle> OnOpenFiles( + Message&, const std::vector<std::pair<std::string, int>>& file_specs) { + std::vector<LocalHandle> return_value; + for (auto& spec : file_specs) { + LocalHandle fd(spec.first, spec.second); + if (fd) + return_value.emplace_back(std::move(fd)); + else + return_value.emplace_back(-errno); + } + return return_value; + } + + std::pair<int, BufferWrapper<std::vector<std::uint8_t>>> OnReadFile( + Message& message, const std::string& path, int mode, std::size_t length) { + std::pair<int, BufferWrapper<std::vector<std::uint8_t>>> return_value; + LocalHandle fd(path, mode); + if (!fd) { + message.ReplyError(errno); + return return_value; + } + + return_value.second.reserve(length); + const int ret = read(fd.Get(), return_value.second.data(), length); + if (ret < 0) { + message.ReplyError(errno); + return return_value; + } + + return_value.second.resize(ret); + return_value.first = ret; + return return_value; + } + + RemoteChannelHandle OnPushChannel(Message& message) { + auto status = message.PushChannel(0, nullptr, nullptr); + if (!status) { + message.ReplyError(status.error()); + return {}; + } + return status.take(); + } + + Status<void> OnPositive(Message& /*message*/, int test_value) { + if (test_value >= 0) + return {}; + else + return ErrorStatus(EINVAL); + } + + TestService(const TestService&) = delete; + void operator=(const TestService&) = delete; +}; + +} // anonymous namespace + +// Use a test fixture to ensure proper order of cleanup between clients, +// services, and the dispatcher. As these objects are cleaned up in the same +// thread, either the service or client must be destroyed before stopping the +// dispatcher. The reason for this is that clients send blocking "close" +// messages to their respective services on destruction. If this happens after +// stopping the dispatcher the client destructor will get blocked waiting for a +// reply that will never come. In normal use of the service framework this is +// never an issue because clients and the dispatcher for the same service are +// never destructed in the same thread (they live in different processes). +class RemoteMethodTest : public ::testing::Test { + protected: + std::unique_ptr<ServiceDispatcher> dispatcher_; + std::thread dispatch_thread_; + + void SetUp() override { + // Create a dispatcher to handle messages to services. + dispatcher_ = android::pdx::uds::ServiceDispatcher::Create(); + ASSERT_NE(nullptr, dispatcher_); + + // Start the message dispatch loop in a separate thread. + dispatch_thread_ = std::thread( + std::bind(&ServiceDispatcher::EnterDispatchLoop, dispatcher_.get())); + } + + void TearDown() override { + if (dispatcher_) { + // Cancel the dispatcher and wait for the thread to terminate. + // Explicitly + // join the thread so that destruction doesn't deallocate the + // dispatcher + // before the thread finishes. + dispatcher_->SetCanceled(true); + dispatch_thread_.join(); + } + } +}; + +// Test basic operation of TestService/TestClient classes. +TEST_F(RemoteMethodTest, BasicClientService) { + // Create a test service and add it to the dispatcher. + + auto service = TestService::Create(); + ASSERT_NE(nullptr, service); + ASSERT_EQ(0, dispatcher_->AddService(service)); + + // Create a client to service. + auto client = TestClient::Create(); + ASSERT_NE(nullptr, client); + + const int sum = client->Add(10, 25); + EXPECT_GE(35, sum); + + const auto cat = client->Concatenate("This is a string", ", that it is."); + EXPECT_EQ("This is a string, that it is.", cat); + + std::string alphabet = "abcdefghijklmnopqrstuvwxyz"; + const auto rot13_alphabet = client->Rot13(alphabet); + EXPECT_EQ(Rot13(alphabet), rot13_alphabet); + + const auto length = client->Foo(10, "123"); + EXPECT_EQ(13, length); + + const std::vector<int> vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + const int vector_sum = client->SumVector(vector.data(), vector.size()); + const int vector_sum2 = client->SumVector(vector); + EXPECT_EQ(std::accumulate(vector.begin(), vector.end(), 0), vector_sum); + EXPECT_EQ(std::accumulate(vector.begin(), vector.end(), 0), vector_sum2); + + const auto string_length1 = client->StringLength("This is a string"); + EXPECT_EQ(16, string_length1); + + const auto string_length2 = client->StringLength("1234567890"); + EXPECT_EQ(10, string_length2); + + std::string string = "1234567890"; + const auto string_length3 = + client->StringLength(string.c_str(), string.length()); + EXPECT_EQ(10, string_length3); + + TestType tt{10, 0.0, "string"}; + const auto tt_result = client->SendTestType(tt); + EXPECT_EQ(TestType(30, -2.0, "stringfoo"), tt_result); + + std::vector<TestType> ttv = {TestType(0, 0.0, "abc"), + TestType(0, 0.0, "123")}; + const std::string string_result = client->SendVector(ttv); + EXPECT_EQ("abc123", string_result); + + const int int_result = client->NoArgs(); + EXPECT_EQ(1, int_result); +} + +TEST_F(RemoteMethodTest, LocalHandle) { + // Create a test service and add it to the dispatcher. + auto service = TestService::Create(); + ASSERT_NE(nullptr, service); + ASSERT_EQ(0, dispatcher_->AddService(service)); + + // Create a client to service. + auto client = TestClient::Create(); + ASSERT_NE(nullptr, client); + + LocalHandle fd("/dev/zero", O_RDONLY); + ASSERT_TRUE(fd.IsValid()); + + int fd_result = client->SendFile(fd); + EXPECT_LE(0, fd_result); + EXPECT_NE(fd.Get(), fd_result); + fd = LocalHandle(-3); + fd_result = client->SendFile(fd); + EXPECT_EQ(fd.Get(), fd_result); + + fd = client->GetFile("/dev/zero", O_RDONLY); + ASSERT_TRUE(fd.IsValid()) << "Error code: " << fd.Get(); + + std::array<uint8_t, 10> buffer; + buffer.fill(1); + EXPECT_EQ(10, read(fd.Get(), buffer.data(), buffer.size())); + EXPECT_EQ(buffer, decltype(buffer){{0}}); + fd.Close(); + + const int error = client->GetFile("/dev/zero", O_RDONLY, &fd); + EXPECT_EQ(0, error); + EXPECT_TRUE(fd.IsValid()); + + buffer.fill(1); + EXPECT_EQ(10, read(fd.Get(), buffer.data(), buffer.size())); + EXPECT_EQ(buffer, decltype(buffer){{0}}); + + /* + Seg fault. + fd = client->GetFile("/dev/foobar", O_RDONLY); + EXPECT_FALSE(fd.IsValid()); + */ +} + +TEST_F(RemoteMethodTest, PushChannel) { + // Create a test service and add it to the dispatcher. + auto service = TestService::Create(); + ASSERT_NE(nullptr, service); + ASSERT_EQ(0, dispatcher_->AddService(service)); + + // Create a client to service. + auto client = TestClient::Create(); + ASSERT_NE(nullptr, client); + + // Get a new channel as an fd. + LocalChannelHandle channel; + const int ret = client->PushChannel(&channel); + EXPECT_EQ(0, ret); + EXPECT_TRUE(channel.valid()); + + // Create a new client from the channel. + auto client2 = TestClient::Create(std::move(channel)); + ASSERT_NE(nullptr, client2); + + // Test that the new channel works. + const int sum = client2->Add(10, 25); + EXPECT_GE(35, sum); +} + +TEST_F(RemoteMethodTest, Positive) { + // Create a test service and add it to the dispatcher. + auto service = TestService::Create(); + ASSERT_NE(nullptr, service); + ASSERT_EQ(0, dispatcher_->AddService(service)); + + // Create a client to service. + auto client = TestClient::Create(); + ASSERT_NE(nullptr, client); + + ASSERT_TRUE(client->Positive(0)); + ASSERT_TRUE(client->Positive(1)); + ASSERT_FALSE(client->Positive(-1)); +} + +TEST_F(RemoteMethodTest, AggregateLocalHandle) { + // Create a test service and add it to the dispatcher. + auto service = TestService::Create(); + ASSERT_NE(nullptr, service); + ASSERT_EQ(0, dispatcher_->AddService(service)); + + // Create a client to service. + auto client = TestClient::Create(); + ASSERT_NE(nullptr, client); + + TestFdType result = client->GetTestFdType(10, "/dev/zero", O_RDONLY); + EXPECT_TRUE(result.fd.IsValid()); + EXPECT_EQ(10, result.a); + + std::vector<LocalHandle> files = + client->OpenFiles({{{"/dev/zero", O_RDONLY}, + {"/dev/null", O_WRONLY}, + {"/dev/zero", O_RDONLY}}}); + ASSERT_EQ(3u, files.size()); + EXPECT_TRUE(files[0].IsValid()); + EXPECT_TRUE(files[1].IsValid()); + EXPECT_TRUE(files[2].IsValid()); +} + +TEST_F(RemoteMethodTest, BufferWrapper) { + // Create a test service and add it to the dispatcher. + auto service = TestService::Create(); + ASSERT_NE(nullptr, service); + ASSERT_EQ(0, dispatcher_->AddService(service)); + + // Create a client to service. + auto client = TestClient::Create(); + ASSERT_NE(nullptr, client); + + const int buffer_size = 20; + std::vector<std::uint8_t> buffer(buffer_size, 'x'); + std::vector<std::uint8_t> expected(buffer_size, 0); + int ret = + client->ReadFile(buffer.data(), buffer.size(), "/dev/zero", O_RDONLY); + EXPECT_EQ(buffer_size, ret); + EXPECT_EQ(expected, buffer); +} + +// +// RemoteMethodFramework: Tests the type-based framework that remote method +// support is built upon. +// + +// Test logical And template. +TEST(RemoteMethodFramework, And) { + EXPECT_TRUE((And<std::true_type, std::true_type>::value)); + EXPECT_FALSE((And<std::true_type, std::false_type>::value)); + EXPECT_FALSE((And<std::false_type, std::true_type>::value)); + EXPECT_FALSE((And<std::false_type, std::false_type>::value)); + + EXPECT_TRUE((And<std::true_type, std::true_type, std::true_type>::value)); + EXPECT_FALSE((And<std::true_type, std::true_type, std::false_type>::value)); + EXPECT_FALSE((And<std::true_type, std::false_type, std::true_type>::value)); + EXPECT_FALSE((And<std::true_type, std::false_type, std::false_type>::value)); + EXPECT_FALSE((And<std::false_type, std::true_type, std::true_type>::value)); + EXPECT_FALSE((And<std::false_type, std::true_type, std::false_type>::value)); + EXPECT_FALSE((And<std::false_type, std::false_type, std::true_type>::value)); + EXPECT_FALSE((And<std::false_type, std::false_type, std::false_type>::value)); +} + +// Test convertible type constraints. +TEST(RemoteMethodFramework, IsConvertible) { + // std::pair. + EXPECT_TRUE( + (IsConvertible<std::pair<int, float>, std::pair<int, float>>::value)); + EXPECT_FALSE( + (IsConvertible<std::pair<int, float>, std::pair<float, float>>::value)); + EXPECT_FALSE( + (IsConvertible<std::pair<int, float>, std::pair<float, int>>::value)); + + // Nested std::pair. + EXPECT_TRUE((IsConvertible<std::pair<std::pair<int, float>, float>, + std::pair<std::pair<int, float>, float>>::value)); + EXPECT_FALSE((IsConvertible<std::pair<std::pair<int, float>, float>, + std::pair<std::pair<float, int>, float>>::value)); + + // std::tuple and std::pair. + EXPECT_TRUE( + (IsConvertible<std::pair<int, float>, std::tuple<int, float>>::value)); + EXPECT_TRUE( + (IsConvertible<std::tuple<int, float>, std::pair<int, float>>::value)); + EXPECT_FALSE( + (IsConvertible<std::pair<float, float>, std::tuple<int, float>>::value)); + EXPECT_FALSE( + (IsConvertible<std::tuple<float, float>, std::pair<int, float>>::value)); + EXPECT_FALSE( + (IsConvertible<std::pair<int, int>, std::tuple<int, float>>::value)); + EXPECT_FALSE( + (IsConvertible<std::tuple<int, int>, std::pair<int, float>>::value)); + EXPECT_FALSE( + (IsConvertible<std::pair<int, int>, std::tuple<int, int, int>>::value)); + EXPECT_FALSE( + (IsConvertible<std::tuple<int, int, int>, std::pair<int, int>>::value)); + + // std::vector. + EXPECT_TRUE((IsConvertible<std::vector<int>, std::vector<int>>::value)); + EXPECT_FALSE((IsConvertible<std::vector<int>, std::vector<float>>::value)); + + // Nested std::vector. + EXPECT_TRUE((IsConvertible<std::vector<std::pair<int, int>>, + std::vector<std::pair<int, int>>>::value)); + EXPECT_FALSE((IsConvertible<std::vector<std::pair<int, int>>, + std::vector<std::pair<int, float>>>::value)); + EXPECT_FALSE((IsConvertible<std::vector<std::pair<int, int>>, + std::vector<std::pair<float, int>>>::value)); + EXPECT_FALSE((IsConvertible<std::vector<std::pair<int, int>>, + std::vector<std::pair<float, float>>>::value)); + + // std::vector with nested convertible types. + EXPECT_TRUE((IsConvertible<std::vector<StringWrapper<char>>, + std::vector<std::string>>::value)); + + // std::map and std::unordered_map. + EXPECT_TRUE((IsConvertible<std::map<int, float>, + std::unordered_map<int, float>>::value)); + EXPECT_FALSE((IsConvertible<std::map<float, float>, + std::unordered_map<int, float>>::value)); + EXPECT_FALSE((IsConvertible<std::map<float, float>, + std::unordered_map<float, int>>::value)); + EXPECT_FALSE((IsConvertible<std::map<float, float>, + std::unordered_map<int, int>>::value)); + EXPECT_TRUE((IsConvertible<std::unordered_map<int, float>, + std::map<int, float>>::value)); + EXPECT_FALSE((IsConvertible<std::unordered_map<float, float>, + std::map<int, float>>::value)); + EXPECT_FALSE((IsConvertible<std::unordered_map<float, float>, + std::map<float, int>>::value)); + EXPECT_FALSE((IsConvertible<std::unordered_map<float, float>, + std::map<int, int>>::value)); + + // std::map with nested convertible types. + EXPECT_TRUE((IsConvertible<std::map<int, std::string>, + std::map<int, StringWrapper<char>>>::value)); + EXPECT_TRUE( + (IsConvertible<std::map<std::tuple<int, int>, std::string>, + std::map<std::pair<int, int>, std::string>>::value)); + + // std::unordered_map with nested convertible types. + EXPECT_TRUE( + (IsConvertible<std::unordered_map<int, std::string>, + std::unordered_map<int, StringWrapper<char>>>::value)); + EXPECT_TRUE((IsConvertible< + std::unordered_map<std::tuple<int, int>, std::string>, + std::unordered_map<std::pair<int, int>, std::string>>::value)); + + // std::string. + EXPECT_TRUE((IsConvertible<std::string, std::string>::value)); + EXPECT_FALSE((IsConvertible<std::string, int>::value)); + EXPECT_FALSE((IsConvertible<int, std::string>::value)); + + // Nested std::string. + EXPECT_TRUE((IsConvertible<std::pair<std::string, std::string>, + std::pair<std::string, std::string>>::value)); + EXPECT_FALSE((IsConvertible<std::pair<std::string, std::string>, + std::pair<std::string, int>>::value)); + EXPECT_FALSE((IsConvertible<std::pair<std::string, std::string>, + std::pair<int, std::string>>::value)); + EXPECT_FALSE((IsConvertible<std::pair<std::string, std::string>, + std::pair<int, int>>::value)); + + // StringWrapper. + EXPECT_TRUE((IsConvertible<StringWrapper<char>, StringWrapper<char>>::value)); + EXPECT_TRUE((IsConvertible<StringWrapper<char>, std::string>::value)); + EXPECT_TRUE((IsConvertible<std::string, StringWrapper<char>>::value)); + EXPECT_FALSE((IsConvertible<StringWrapper<char>, int>::value)); + EXPECT_FALSE( + (IsConvertible<StringWrapper<char>, BufferWrapper<char*>>::value)); + + // BufferWrapper. + EXPECT_TRUE( + (IsConvertible<BufferWrapper<char*>, BufferWrapper<char*>>::value)); + EXPECT_TRUE( + (IsConvertible<BufferWrapper<char*>, BufferWrapper<const char*>>::value)); + EXPECT_FALSE( + (IsConvertible<BufferWrapper<char*>, BufferWrapper<int*>>::value)); + EXPECT_TRUE((IsConvertible<BufferWrapper<char*>, + BufferWrapper<std::vector<char>>>::value)); + + // RemoteHandle and BorrowedHandle. + EXPECT_TRUE((IsConvertible<LocalHandle, RemoteHandle>::value)); + EXPECT_TRUE((IsConvertible<LocalHandle, BorrowedHandle>::value)); + + // Test rewriting user defined types. + EXPECT_TRUE((IsConvertible<TestTemplateType<LocalHandle>, + TestTemplateType<RemoteHandle>>::value)); + EXPECT_TRUE((IsConvertible<TestTemplateType<LocalHandle>, + TestTemplateType<BorrowedHandle>>::value)); + EXPECT_FALSE((IsConvertible<TestTemplateType<RemoteHandle>, + TestTemplateType<LocalHandle>>::value)); + EXPECT_FALSE((IsConvertible<TestTemplateType<BorrowedHandle>, + TestTemplateType<LocalHandle>>::value)); + + // TODO(eieio): More thorough testing of convertible types. +} + +TEST(RemoteMethodFramework, SerializableMembers) { + EXPECT_TRUE(HasSerializableMembers<TestTemplateType<LocalHandle>>::value); + EXPECT_TRUE(HasSerializableMembers<TestTemplateType<RemoteHandle>>::value); + EXPECT_TRUE(HasSerializableMembers<TestTemplateType<BorrowedHandle>>::value); + + EXPECT_TRUE(std::is_void<EnableIfHasSerializableMembers< + TestTemplateType<LocalHandle>>>::value); + EXPECT_TRUE(std::is_void<EnableIfHasSerializableMembers< + TestTemplateType<RemoteHandle>>>::value); + EXPECT_TRUE(std::is_void<EnableIfHasSerializableMembers< + TestTemplateType<BorrowedHandle>>>::value); + + EXPECT_TRUE(HasSerializableMembers<DerivedTestType>::value); + + EXPECT_TRUE(HasSerializableMembers<BasicStruct>::value); + EXPECT_TRUE(HasSerializableMembers<TestType>::value); + EXPECT_TRUE(HasSerializableMembers<TestTemplateType<LocalHandle>>::value); + EXPECT_TRUE(HasSerializableMembers<TestTemplateType<RemoteHandle>>::value); + EXPECT_TRUE(HasSerializableMembers<TestTemplateType<BorrowedHandle>>::value); + EXPECT_TRUE(HasSerializableMembers<DerivedTestType>::value); + EXPECT_FALSE(HasSerializableMembers<NonSerializableType>::value); + EXPECT_FALSE( + HasSerializableMembers<IncorrectlyDefinedSerializableType>::value); +} + +TEST(RemoteMethodFramework, RemoteAPITypes) { + EXPECT_EQ(0u, TestInterface::API::MethodIndex<TestInterface::Add>()); +} diff --git a/libs/vr/libpdx_uds/service_dispatcher.cpp b/libs/vr/libpdx_uds/service_dispatcher.cpp new file mode 100644 index 0000000000..2c52578d1c --- /dev/null +++ b/libs/vr/libpdx_uds/service_dispatcher.cpp @@ -0,0 +1,202 @@ +#include "uds/service_dispatcher.h" + +#include <errno.h> +#include <log/log.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> + +#include "pdx/service.h" +#include "uds/service_endpoint.h" + +static const int kMaxEventsPerLoop = 128; + +namespace android { +namespace pdx { +namespace uds { + +std::unique_ptr<pdx::ServiceDispatcher> ServiceDispatcher::Create() { + std::unique_ptr<ServiceDispatcher> dispatcher{new ServiceDispatcher()}; + if (!dispatcher->epoll_fd_ || !dispatcher->event_fd_) { + dispatcher.reset(); + } + + return std::move(dispatcher); +} + +ServiceDispatcher::ServiceDispatcher() { + event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); + if (!event_fd_) { + ALOGE("Failed to create event fd because: %s\n", strerror(errno)); + return; + } + + epoll_fd_.Reset(epoll_create1(EPOLL_CLOEXEC)); + if (!epoll_fd_) { + ALOGE("Failed to create epoll fd because: %s\n", strerror(errno)); + return; + } + + // Use "this" as a unique pointer to distinguish the event fd from all + // the other entries that point to instances of Service. + epoll_event event; + event.events = EPOLLIN; + event.data.ptr = this; + + if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd_.Get(), &event) < 0) { + ALOGE("Failed to add event fd to epoll fd because: %s\n", strerror(errno)); + + // Close the fds here and signal failure to the factory method. + event_fd_.Close(); + epoll_fd_.Close(); + } +} + +ServiceDispatcher::~ServiceDispatcher() { SetCanceled(true); } + +int ServiceDispatcher::ThreadEnter() { + std::lock_guard<std::mutex> autolock(mutex_); + + if (canceled_) + return -EBUSY; + + thread_count_++; + return 0; +} + +void ServiceDispatcher::ThreadExit() { + std::lock_guard<std::mutex> autolock(mutex_); + thread_count_--; + condition_.notify_one(); +} + +int ServiceDispatcher::AddService(const std::shared_ptr<Service>& service) { + if (service->endpoint()->GetIpcTag() != Endpoint::kIpcTag) + return -EINVAL; + + std::lock_guard<std::mutex> autolock(mutex_); + + auto* endpoint = static_cast<Endpoint*>(service->endpoint()); + epoll_event event; + event.events = EPOLLIN; + event.data.ptr = service.get(); + + if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, endpoint->epoll_fd(), &event) < + 0) { + ALOGE("Failed to add service to dispatcher because: %s\n", strerror(errno)); + return -errno; + } + + services_.push_back(service); + return 0; +} + +int ServiceDispatcher::RemoveService(const std::shared_ptr<Service>& service) { + if (service->endpoint()->GetIpcTag() != Endpoint::kIpcTag) + return -EINVAL; + + std::lock_guard<std::mutex> autolock(mutex_); + + // It's dangerous to remove a service while other threads may be using it. + if (thread_count_ > 0) + return -EBUSY; + + epoll_event dummy; // See BUGS in man 2 epoll_ctl. + + auto* endpoint = static_cast<Endpoint*>(service->endpoint()); + if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, endpoint->epoll_fd(), &dummy) < + 0) { + ALOGE("Failed to remove service from dispatcher because: %s\n", + strerror(errno)); + return -errno; + } + + services_.remove(service); + return 0; +} + +int ServiceDispatcher::ReceiveAndDispatch() { return ReceiveAndDispatch(-1); } + +int ServiceDispatcher::ReceiveAndDispatch(int timeout) { + int ret = ThreadEnter(); + if (ret < 0) + return ret; + + epoll_event events[kMaxEventsPerLoop]; + + int count = epoll_wait(epoll_fd_.Get(), events, kMaxEventsPerLoop, timeout); + if (count <= 0) { + ALOGE_IF(count < 0, "Failed to wait for epoll events because: %s\n", + strerror(errno)); + ThreadExit(); + return count < 0 ? -errno : -ETIMEDOUT; + } + + for (int i = 0; i < count; i++) { + if (events[i].data.ptr == this) { + ThreadExit(); + return -EBUSY; + } else { + Service* service = static_cast<Service*>(events[i].data.ptr); + + ALOGI_IF(TRACE, "Dispatching message: fd=%d\n", + static_cast<Endpoint*>(service->endpoint())->epoll_fd()); + service->ReceiveAndDispatch(); + } + } + + ThreadExit(); + return 0; +} + +int ServiceDispatcher::EnterDispatchLoop() { + int ret = ThreadEnter(); + if (ret < 0) + return ret; + + epoll_event events[kMaxEventsPerLoop]; + + while (!IsCanceled()) { + int count = epoll_wait(epoll_fd_.Get(), events, kMaxEventsPerLoop, -1); + if (count < 0 && errno != EINTR) { + ALOGE("Failed to wait for epoll events because: %s\n", strerror(errno)); + ThreadExit(); + return -errno; + } + + for (int i = 0; i < count; i++) { + if (events[i].data.ptr == this) { + ThreadExit(); + return -EBUSY; + } else { + Service* service = static_cast<Service*>(events[i].data.ptr); + + ALOGI_IF(TRACE, "Dispatching message: fd=%d\n", + static_cast<Endpoint*>(service->endpoint())->epoll_fd()); + service->ReceiveAndDispatch(); + } + } + } + + ThreadExit(); + return 0; +} + +void ServiceDispatcher::SetCanceled(bool cancel) { + std::unique_lock<std::mutex> lock(mutex_); + canceled_ = cancel; + + if (canceled_ && thread_count_ > 0) { + eventfd_write(event_fd_.Get(), 1); // Signal threads to quit. + + condition_.wait(lock, [this] { return !(canceled_ && thread_count_ > 0); }); + + eventfd_t value; + eventfd_read(event_fd_.Get(), &value); // Unsignal. + } +} + +bool ServiceDispatcher::IsCanceled() const { return canceled_; } + +} // namespace uds +} // namespace pdx +} // namespace android diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp new file mode 100644 index 0000000000..27a56f9fe0 --- /dev/null +++ b/libs/vr/libpdx_uds/service_endpoint.cpp @@ -0,0 +1,772 @@ +#include "uds/service_endpoint.h" + +#include <poll.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <algorithm> // std::min + +#include <android-base/logging.h> +#include <android-base/strings.h> +#include <cutils/sockets.h> +#include <pdx/service.h> +#include <selinux/selinux.h> +#include <uds/channel_manager.h> +#include <uds/client_channel_factory.h> +#include <uds/ipc_helper.h> + +namespace { + +constexpr int kMaxBackLogForSocketListen = 1; + +using android::pdx::BorrowedChannelHandle; +using android::pdx::BorrowedHandle; +using android::pdx::ChannelReference; +using android::pdx::ErrorStatus; +using android::pdx::FileReference; +using android::pdx::LocalChannelHandle; +using android::pdx::LocalHandle; +using android::pdx::Status; +using android::pdx::uds::ChannelInfo; +using android::pdx::uds::ChannelManager; + +struct MessageState { + bool GetLocalFileHandle(int index, LocalHandle* handle) { + if (index < 0) { + handle->Reset(index); + } else if (static_cast<size_t>(index) < request.file_descriptors.size()) { + *handle = std::move(request.file_descriptors[index]); + } else { + return false; + } + return true; + } + + bool GetLocalChannelHandle(int index, LocalChannelHandle* handle) { + if (index < 0) { + *handle = LocalChannelHandle{nullptr, index}; + } else if (static_cast<size_t>(index) < request.channels.size()) { + auto& channel_info = request.channels[index]; + *handle = ChannelManager::Get().CreateHandle( + std::move(channel_info.data_fd), std::move(channel_info.event_fd)); + } else { + return false; + } + return true; + } + + Status<FileReference> PushFileHandle(BorrowedHandle handle) { + if (!handle) + return handle.Get(); + response.file_descriptors.push_back(std::move(handle)); + return response.file_descriptors.size() - 1; + } + + Status<ChannelReference> PushChannelHandle(BorrowedChannelHandle handle) { + if (!handle) + return handle.value(); + + if (auto* channel_data = + ChannelManager::Get().GetChannelData(handle.value())) { + ChannelInfo<BorrowedHandle> channel_info; + channel_info.data_fd.Reset(handle.value()); + channel_info.event_fd = channel_data->event_receiver.event_fd(); + response.channels.push_back(std::move(channel_info)); + return response.channels.size() - 1; + } else { + return ErrorStatus{EINVAL}; + } + } + + Status<ChannelReference> PushChannelHandle(BorrowedHandle data_fd, + BorrowedHandle event_fd) { + if (!data_fd || !event_fd) + return ErrorStatus{EINVAL}; + ChannelInfo<BorrowedHandle> channel_info; + channel_info.data_fd = std::move(data_fd); + channel_info.event_fd = std::move(event_fd); + response.channels.push_back(std::move(channel_info)); + return response.channels.size() - 1; + } + + Status<size_t> WriteData(const iovec* vector, size_t vector_length) { + size_t size = 0; + for (size_t i = 0; i < vector_length; i++) { + const auto* data = reinterpret_cast<const uint8_t*>(vector[i].iov_base); + response_data.insert(response_data.end(), data, data + vector[i].iov_len); + size += vector[i].iov_len; + } + return size; + } + + Status<size_t> ReadData(const iovec* vector, size_t vector_length) { + size_t size_remaining = request_data.size() - request_data_read_pos; + size_t size = 0; + for (size_t i = 0; i < vector_length && size_remaining > 0; i++) { + size_t size_to_copy = std::min(size_remaining, vector[i].iov_len); + memcpy(vector[i].iov_base, request_data.data() + request_data_read_pos, + size_to_copy); + size += size_to_copy; + request_data_read_pos += size_to_copy; + size_remaining -= size_to_copy; + } + return size; + } + + android::pdx::uds::RequestHeader<LocalHandle> request; + android::pdx::uds::ResponseHeader<BorrowedHandle> response; + std::vector<LocalHandle> sockets_to_close; + std::vector<uint8_t> request_data; + size_t request_data_read_pos{0}; + std::vector<uint8_t> response_data; +}; + +} // anonymous namespace + +namespace android { +namespace pdx { +namespace uds { + +Endpoint::Endpoint(const std::string& endpoint_path, bool blocking, + bool use_init_socket_fd) + : endpoint_path_{ClientChannelFactory::GetEndpointPath(endpoint_path)}, + is_blocking_{blocking} { + LocalHandle fd; + if (use_init_socket_fd) { + // Cut off the /dev/socket/ prefix from the full socket path and use the + // resulting "name" to retrieve the file descriptor for the socket created + // by the init process. + constexpr char prefix[] = "/dev/socket/"; + CHECK(android::base::StartsWith(endpoint_path_, prefix)) + << "Endpoint::Endpoint: Socket name '" << endpoint_path_ + << "' must begin with '" << prefix << "'"; + std::string socket_name = endpoint_path_.substr(sizeof(prefix) - 1); + fd.Reset(android_get_control_socket(socket_name.c_str())); + CHECK(fd.IsValid()) + << "Endpoint::Endpoint: Unable to obtain the control socket fd for '" + << socket_name << "'"; + fcntl(fd.Get(), F_SETFD, FD_CLOEXEC); + } else { + fd.Reset(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)); + CHECK(fd.IsValid()) << "Endpoint::Endpoint: Failed to create socket: " + << strerror(errno); + + sockaddr_un local; + local.sun_family = AF_UNIX; + strncpy(local.sun_path, endpoint_path_.c_str(), sizeof(local.sun_path)); + local.sun_path[sizeof(local.sun_path) - 1] = '\0'; + + unlink(local.sun_path); + int ret = + bind(fd.Get(), reinterpret_cast<sockaddr*>(&local), sizeof(local)); + CHECK_EQ(ret, 0) << "Endpoint::Endpoint: bind error: " << strerror(errno); + } + Init(std::move(fd)); +} + +Endpoint::Endpoint(LocalHandle socket_fd) { Init(std::move(socket_fd)); } + +void Endpoint::Init(LocalHandle socket_fd) { + if (socket_fd) { + CHECK_EQ(listen(socket_fd.Get(), kMaxBackLogForSocketListen), 0) + << "Endpoint::Endpoint: listen error: " << strerror(errno); + } + cancel_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); + CHECK(cancel_event_fd_.IsValid()) + << "Endpoint::Endpoint: Failed to create event fd: " << strerror(errno); + + epoll_fd_.Reset(epoll_create1(EPOLL_CLOEXEC)); + CHECK(epoll_fd_.IsValid()) + << "Endpoint::Endpoint: Failed to create epoll fd: " << strerror(errno); + + if (socket_fd) { + epoll_event socket_event; + socket_event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT; + socket_event.data.fd = socket_fd.Get(); + int ret = epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, socket_fd.Get(), + &socket_event); + CHECK_EQ(ret, 0) + << "Endpoint::Endpoint: Failed to add socket fd to epoll fd: " + << strerror(errno); + } + + epoll_event cancel_event; + cancel_event.events = EPOLLIN; + cancel_event.data.fd = cancel_event_fd_.Get(); + + int ret = epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, cancel_event_fd_.Get(), + &cancel_event); + CHECK_EQ(ret, 0) + << "Endpoint::Endpoint: Failed to add cancel event fd to epoll fd: " + << strerror(errno); + socket_fd_ = std::move(socket_fd); +} + +void* Endpoint::AllocateMessageState() { return new MessageState; } + +void Endpoint::FreeMessageState(void* state) { + delete static_cast<MessageState*>(state); +} + +Status<void> Endpoint::AcceptConnection(Message* message) { + if (!socket_fd_) + return ErrorStatus(EBADF); + + sockaddr_un remote; + socklen_t addrlen = sizeof(remote); + LocalHandle connection_fd{accept4(socket_fd_.Get(), + reinterpret_cast<sockaddr*>(&remote), + &addrlen, SOCK_CLOEXEC)}; + if (!connection_fd) { + ALOGE("Endpoint::AcceptConnection: failed to accept connection: %s", + strerror(errno)); + return ErrorStatus(errno); + } + + LocalHandle local_socket; + LocalHandle remote_socket; + auto status = CreateChannelSocketPair(&local_socket, &remote_socket); + if (!status) + return status; + + // Borrow the local channel handle before we move it into OnNewChannel(). + BorrowedHandle channel_handle = local_socket.Borrow(); + status = OnNewChannel(std::move(local_socket)); + if (!status) + return status; + + // Send the channel socket fd to the client. + ChannelConnectionInfo<LocalHandle> connection_info; + connection_info.channel_fd = std::move(remote_socket); + status = SendData(connection_fd.Borrow(), connection_info); + + if (status) { + // Get the CHANNEL_OPEN message from client over the channel socket. + status = ReceiveMessageForChannel(channel_handle, message); + } else { + CloseChannel(GetChannelId(channel_handle)); + } + + // Don't need the connection socket anymore. Further communication should + // happen over the channel socket. + shutdown(connection_fd.Get(), SHUT_WR); + return status; +} + +Status<void> Endpoint::SetService(Service* service) { + service_ = service; + return {}; +} + +Status<void> Endpoint::SetChannel(int channel_id, Channel* channel) { + std::lock_guard<std::mutex> autolock(channel_mutex_); + auto channel_data = channels_.find(channel_id); + if (channel_data == channels_.end()) + return ErrorStatus{EINVAL}; + channel_data->second.channel_state = channel; + return {}; +} + +Status<void> Endpoint::OnNewChannel(LocalHandle channel_fd) { + std::lock_guard<std::mutex> autolock(channel_mutex_); + Status<void> status; + status.PropagateError(OnNewChannelLocked(std::move(channel_fd), nullptr)); + return status; +} + +Status<std::pair<int32_t, Endpoint::ChannelData*>> Endpoint::OnNewChannelLocked( + LocalHandle channel_fd, Channel* channel_state) { + epoll_event event; + event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT; + event.data.fd = channel_fd.Get(); + if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, channel_fd.Get(), &event) < 0) { + ALOGE( + "Endpoint::OnNewChannelLocked: Failed to add channel to endpoint: %s\n", + strerror(errno)); + return ErrorStatus(errno); + } + ChannelData channel_data; + channel_data.event_set.AddDataFd(channel_fd); + channel_data.data_fd = std::move(channel_fd); + channel_data.channel_state = channel_state; + for (;;) { + // Try new channel IDs until we find one which is not already in the map. + if (last_channel_id_++ == std::numeric_limits<int32_t>::max()) + last_channel_id_ = 1; + auto iter = channels_.lower_bound(last_channel_id_); + if (iter == channels_.end() || iter->first != last_channel_id_) { + channel_fd_to_id_.emplace(channel_data.data_fd.Get(), last_channel_id_); + iter = channels_.emplace_hint(iter, last_channel_id_, + std::move(channel_data)); + return std::make_pair(last_channel_id_, &iter->second); + } + } +} + +Status<void> Endpoint::ReenableEpollEvent(const BorrowedHandle& fd) { + epoll_event event; + event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT; + event.data.fd = fd.Get(); + if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_MOD, fd.Get(), &event) < 0) { + ALOGE( + "Endpoint::ReenableEpollEvent: Failed to re-enable channel to " + "endpoint: %s\n", + strerror(errno)); + return ErrorStatus(errno); + } + return {}; +} + +Status<void> Endpoint::CloseChannel(int channel_id) { + std::lock_guard<std::mutex> autolock(channel_mutex_); + return CloseChannelLocked(channel_id); +} + +Status<void> Endpoint::CloseChannelLocked(int32_t channel_id) { + ALOGD_IF(TRACE, "Endpoint::CloseChannelLocked: channel_id=%d", channel_id); + + auto iter = channels_.find(channel_id); + if (iter == channels_.end()) + return ErrorStatus{EINVAL}; + + int channel_fd = iter->second.data_fd.Get(); + Status<void> status; + epoll_event dummy; // See BUGS in man 2 epoll_ctl. + if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, channel_fd, &dummy) < 0) { + status.SetError(errno); + ALOGE( + "Endpoint::CloseChannelLocked: Failed to remove channel from endpoint: " + "%s\n", + strerror(errno)); + } else { + status.SetValue(); + } + + channel_fd_to_id_.erase(channel_fd); + channels_.erase(iter); + return status; +} + +Status<void> Endpoint::ModifyChannelEvents(int channel_id, int clear_mask, + int set_mask) { + std::lock_guard<std::mutex> autolock(channel_mutex_); + + auto search = channels_.find(channel_id); + if (search != channels_.end()) { + auto& channel_data = search->second; + channel_data.event_set.ModifyEvents(clear_mask, set_mask); + return {}; + } + + return ErrorStatus{EINVAL}; +} + +Status<void> Endpoint::CreateChannelSocketPair(LocalHandle* local_socket, + LocalHandle* remote_socket) { + Status<void> status; + char* endpoint_context = nullptr; + // Make sure the channel socket has the correct SELinux label applied. + // Here we get the label from the endpoint file descriptor, which should be + // something like "u:object_r:pdx_service_endpoint_socket:s0" and replace + // "endpoint" with "channel" to produce the channel label such as this: + // "u:object_r:pdx_service_channel_socket:s0". + if (fgetfilecon_raw(socket_fd_.Get(), &endpoint_context) > 0) { + std::string channel_context = endpoint_context; + freecon(endpoint_context); + const std::string suffix = "_endpoint_socket"; + auto pos = channel_context.find(suffix); + if (pos != std::string::npos) { + channel_context.replace(pos, suffix.size(), "_channel_socket"); + } else { + ALOGW( + "Endpoint::CreateChannelSocketPair: Endpoint security context '%s' " + "does not contain expected substring '%s'", + channel_context.c_str(), suffix.c_str()); + } + ALOGE_IF(setsockcreatecon_raw(channel_context.c_str()) == -1, + "Endpoint::CreateChannelSocketPair: Failed to set channel socket " + "security context: %s", + strerror(errno)); + } else { + ALOGE( + "Endpoint::CreateChannelSocketPair: Failed to obtain the endpoint " + "socket's security context: %s", + strerror(errno)); + } + + int channel_pair[2] = {}; + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, channel_pair) == -1) { + ALOGE("Endpoint::CreateChannelSocketPair: Failed to create socket pair: %s", + strerror(errno)); + status.SetError(errno); + return status; + } + + setsockcreatecon_raw(nullptr); + + local_socket->Reset(channel_pair[0]); + remote_socket->Reset(channel_pair[1]); + + int optval = 1; + if (setsockopt(local_socket->Get(), SOL_SOCKET, SO_PASSCRED, &optval, + sizeof(optval)) == -1) { + ALOGE( + "Endpoint::CreateChannelSocketPair: Failed to enable the receiving of " + "the credentials for channel %d: %s", + local_socket->Get(), strerror(errno)); + status.SetError(errno); + } + return status; +} + +Status<RemoteChannelHandle> Endpoint::PushChannel(Message* message, + int /*flags*/, + Channel* channel, + int* channel_id) { + LocalHandle local_socket; + LocalHandle remote_socket; + auto status = CreateChannelSocketPair(&local_socket, &remote_socket); + if (!status) + return status.error_status(); + + std::lock_guard<std::mutex> autolock(channel_mutex_); + auto channel_data = OnNewChannelLocked(std::move(local_socket), channel); + if (!channel_data) + return channel_data.error_status(); + *channel_id = channel_data.get().first; + + // Flags are ignored for now. + // TODO(xiaohuit): Implement those. + + auto* state = static_cast<MessageState*>(message->GetState()); + Status<ChannelReference> ref = state->PushChannelHandle( + remote_socket.Borrow(), + channel_data.get().second->event_set.event_fd().Borrow()); + if (!ref) + return ref.error_status(); + state->sockets_to_close.push_back(std::move(remote_socket)); + return RemoteChannelHandle{ref.get()}; +} + +Status<int> Endpoint::CheckChannel(const Message* /*message*/, + ChannelReference /*ref*/, + Channel** /*channel*/) { + // TODO(xiaohuit): Implement this. + return ErrorStatus(EFAULT); +} + +Channel* Endpoint::GetChannelState(int32_t channel_id) { + std::lock_guard<std::mutex> autolock(channel_mutex_); + auto channel_data = channels_.find(channel_id); + return (channel_data != channels_.end()) ? channel_data->second.channel_state + : nullptr; +} + +BorrowedHandle Endpoint::GetChannelSocketFd(int32_t channel_id) { + std::lock_guard<std::mutex> autolock(channel_mutex_); + BorrowedHandle handle; + auto channel_data = channels_.find(channel_id); + if (channel_data != channels_.end()) + handle = channel_data->second.data_fd.Borrow(); + return handle; +} + +BorrowedHandle Endpoint::GetChannelEventFd(int32_t channel_id) { + std::lock_guard<std::mutex> autolock(channel_mutex_); + BorrowedHandle handle; + auto channel_data = channels_.find(channel_id); + if (channel_data != channels_.end()) + handle = channel_data->second.event_set.event_fd().Borrow(); + return handle; +} + +int32_t Endpoint::GetChannelId(const BorrowedHandle& channel_fd) { + std::lock_guard<std::mutex> autolock(channel_mutex_); + auto iter = channel_fd_to_id_.find(channel_fd.Get()); + return (iter != channel_fd_to_id_.end()) ? iter->second : -1; +} + +Status<void> Endpoint::ReceiveMessageForChannel( + const BorrowedHandle& channel_fd, Message* message) { + RequestHeader<LocalHandle> request; + int32_t channel_id = GetChannelId(channel_fd); + auto status = ReceiveData(channel_fd.Borrow(), &request); + if (!status) { + if (status.error() == ESHUTDOWN) { + BuildCloseMessage(channel_id, message); + return {}; + } else { + CloseChannel(channel_id); + return status; + } + } + + MessageInfo info; + info.pid = request.cred.pid; + info.tid = -1; + info.cid = channel_id; + info.mid = request.is_impulse ? Message::IMPULSE_MESSAGE_ID + : GetNextAvailableMessageId(); + info.euid = request.cred.uid; + info.egid = request.cred.gid; + info.op = request.op; + info.flags = 0; + info.service = service_; + info.channel = GetChannelState(channel_id); + info.send_len = request.send_len; + info.recv_len = request.max_recv_len; + info.fd_count = request.file_descriptors.size(); + static_assert(sizeof(info.impulse) == request.impulse_payload.size(), + "Impulse payload sizes must be the same in RequestHeader and " + "MessageInfo"); + memcpy(info.impulse, request.impulse_payload.data(), + request.impulse_payload.size()); + *message = Message{info}; + auto* state = static_cast<MessageState*>(message->GetState()); + state->request = std::move(request); + if (request.send_len > 0 && !request.is_impulse) { + state->request_data.resize(request.send_len); + status = ReceiveData(channel_fd, state->request_data.data(), + state->request_data.size()); + } + + if (status && request.is_impulse) + status = ReenableEpollEvent(channel_fd); + + if (!status) { + if (status.error() == ESHUTDOWN) { + BuildCloseMessage(channel_id, message); + return {}; + } else { + CloseChannel(channel_id); + return status; + } + } + + return status; +} + +void Endpoint::BuildCloseMessage(int32_t channel_id, Message* message) { + ALOGD_IF(TRACE, "Endpoint::BuildCloseMessage: channel_id=%d", channel_id); + MessageInfo info; + info.pid = -1; + info.tid = -1; + info.cid = channel_id; + info.mid = GetNextAvailableMessageId(); + info.euid = -1; + info.egid = -1; + info.op = opcodes::CHANNEL_CLOSE; + info.flags = 0; + info.service = service_; + info.channel = GetChannelState(channel_id); + info.send_len = 0; + info.recv_len = 0; + info.fd_count = 0; + *message = Message{info}; +} + +Status<void> Endpoint::MessageReceive(Message* message) { + // Receive at most one event from the epoll set. This should prevent multiple + // dispatch threads from attempting to handle messages on the same socket at + // the same time. + epoll_event event; + int count = RETRY_EINTR( + epoll_wait(epoll_fd_.Get(), &event, 1, is_blocking_ ? -1 : 0)); + if (count < 0) { + ALOGE("Endpoint::MessageReceive: Failed to wait for epoll events: %s\n", + strerror(errno)); + return ErrorStatus{errno}; + } else if (count == 0) { + return ErrorStatus{ETIMEDOUT}; + } + + if (event.data.fd == cancel_event_fd_.Get()) { + return ErrorStatus{ESHUTDOWN}; + } + + if (socket_fd_ && event.data.fd == socket_fd_.Get()) { + auto status = AcceptConnection(message); + if (!status) + return status; + return ReenableEpollEvent(socket_fd_.Borrow()); + } + + BorrowedHandle channel_fd{event.data.fd}; + if (event.events & (EPOLLRDHUP | EPOLLHUP)) { + BuildCloseMessage(GetChannelId(channel_fd), message); + return {}; + } + + return ReceiveMessageForChannel(channel_fd, message); +} + +Status<void> Endpoint::MessageReply(Message* message, int return_code) { + const int32_t channel_id = message->GetChannelId(); + auto channel_socket = GetChannelSocketFd(channel_id); + if (!channel_socket) + return ErrorStatus{EBADF}; + + auto* state = static_cast<MessageState*>(message->GetState()); + switch (message->GetOp()) { + case opcodes::CHANNEL_CLOSE: + return CloseChannel(channel_id); + + case opcodes::CHANNEL_OPEN: + if (return_code < 0) { + return CloseChannel(channel_id); + } else { + // Reply with the event fd. + auto push_status = state->PushFileHandle(GetChannelEventFd(channel_id)); + state->response_data.clear(); // Just in case... + if (!push_status) + return push_status.error_status(); + return_code = push_status.get(); + } + break; + } + + state->response.ret_code = return_code; + state->response.recv_len = state->response_data.size(); + auto status = SendData(channel_socket, state->response); + if (status && !state->response_data.empty()) { + status = SendData(channel_socket, state->response_data.data(), + state->response_data.size()); + } + + if (status) + status = ReenableEpollEvent(channel_socket); + + return status; +} + +Status<void> Endpoint::MessageReplyFd(Message* message, unsigned int push_fd) { + auto* state = static_cast<MessageState*>(message->GetState()); + auto ref = state->PushFileHandle(BorrowedHandle{static_cast<int>(push_fd)}); + if (!ref) + return ref.error_status(); + return MessageReply(message, ref.get()); +} + +Status<void> Endpoint::MessageReplyChannelHandle( + Message* message, const LocalChannelHandle& handle) { + auto* state = static_cast<MessageState*>(message->GetState()); + auto ref = state->PushChannelHandle(handle.Borrow()); + if (!ref) + return ref.error_status(); + return MessageReply(message, ref.get()); +} + +Status<void> Endpoint::MessageReplyChannelHandle( + Message* message, const BorrowedChannelHandle& handle) { + auto* state = static_cast<MessageState*>(message->GetState()); + auto ref = state->PushChannelHandle(handle.Duplicate()); + if (!ref) + return ref.error_status(); + return MessageReply(message, ref.get()); +} + +Status<void> Endpoint::MessageReplyChannelHandle( + Message* message, const RemoteChannelHandle& handle) { + return MessageReply(message, handle.value()); +} + +Status<size_t> Endpoint::ReadMessageData(Message* message, const iovec* vector, + size_t vector_length) { + auto* state = static_cast<MessageState*>(message->GetState()); + return state->ReadData(vector, vector_length); +} + +Status<size_t> Endpoint::WriteMessageData(Message* message, const iovec* vector, + size_t vector_length) { + auto* state = static_cast<MessageState*>(message->GetState()); + return state->WriteData(vector, vector_length); +} + +Status<FileReference> Endpoint::PushFileHandle(Message* message, + const LocalHandle& handle) { + auto* state = static_cast<MessageState*>(message->GetState()); + return state->PushFileHandle(handle.Borrow()); +} + +Status<FileReference> Endpoint::PushFileHandle(Message* message, + const BorrowedHandle& handle) { + auto* state = static_cast<MessageState*>(message->GetState()); + return state->PushFileHandle(handle.Duplicate()); +} + +Status<FileReference> Endpoint::PushFileHandle(Message* /*message*/, + const RemoteHandle& handle) { + return handle.Get(); +} + +Status<ChannelReference> Endpoint::PushChannelHandle( + Message* message, const LocalChannelHandle& handle) { + auto* state = static_cast<MessageState*>(message->GetState()); + return state->PushChannelHandle(handle.Borrow()); +} + +Status<ChannelReference> Endpoint::PushChannelHandle( + Message* message, const BorrowedChannelHandle& handle) { + auto* state = static_cast<MessageState*>(message->GetState()); + return state->PushChannelHandle(handle.Duplicate()); +} + +Status<ChannelReference> Endpoint::PushChannelHandle( + Message* /*message*/, const RemoteChannelHandle& handle) { + return handle.value(); +} + +LocalHandle Endpoint::GetFileHandle(Message* message, FileReference ref) const { + LocalHandle handle; + auto* state = static_cast<MessageState*>(message->GetState()); + state->GetLocalFileHandle(ref, &handle); + return handle; +} + +LocalChannelHandle Endpoint::GetChannelHandle(Message* message, + ChannelReference ref) const { + LocalChannelHandle handle; + auto* state = static_cast<MessageState*>(message->GetState()); + state->GetLocalChannelHandle(ref, &handle); + return handle; +} + +Status<void> Endpoint::Cancel() { + if (eventfd_write(cancel_event_fd_.Get(), 1) < 0) + return ErrorStatus{errno}; + return {}; +} + +std::unique_ptr<Endpoint> Endpoint::Create(const std::string& endpoint_path, + mode_t /*unused_mode*/, + bool blocking) { + return std::unique_ptr<Endpoint>(new Endpoint(endpoint_path, blocking)); +} + +std::unique_ptr<Endpoint> Endpoint::CreateAndBindSocket( + const std::string& endpoint_path, bool blocking) { + return std::unique_ptr<Endpoint>( + new Endpoint(endpoint_path, blocking, false)); +} + +std::unique_ptr<Endpoint> Endpoint::CreateFromSocketFd(LocalHandle socket_fd) { + return std::unique_ptr<Endpoint>(new Endpoint(std::move(socket_fd))); +} + +Status<void> Endpoint::RegisterNewChannelForTests(LocalHandle channel_fd) { + int optval = 1; + if (setsockopt(channel_fd.Get(), SOL_SOCKET, SO_PASSCRED, &optval, + sizeof(optval)) == -1) { + ALOGE( + "Endpoint::RegisterNewChannelForTests: Failed to enable the receiving" + "of the credentials for channel %d: %s", + channel_fd.Get(), strerror(errno)); + return ErrorStatus(errno); + } + return OnNewChannel(std::move(channel_fd)); +} + +} // namespace uds +} // namespace pdx +} // namespace android diff --git a/libs/vr/libpdx_uds/service_framework_tests.cpp b/libs/vr/libpdx_uds/service_framework_tests.cpp new file mode 100644 index 0000000000..2943239495 --- /dev/null +++ b/libs/vr/libpdx_uds/service_framework_tests.cpp @@ -0,0 +1,688 @@ +#include <errno.h> +#include <fcntl.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> +#include <unistd.h> + +#include <array> +#include <atomic> +#include <memory> +#include <numeric> +#include <string> +#include <thread> + +#include <gtest/gtest.h> +#include <pdx/channel_handle.h> +#include <pdx/client.h> +#include <pdx/file_handle.h> +#include <pdx/service.h> +#include <private/android_filesystem_config.h> +#include <uds/client_channel.h> +#include <uds/client_channel_factory.h> +#include <uds/service_dispatcher.h> +#include <uds/service_endpoint.h> + +using android::pdx::BorrowedChannelHandle; +using android::pdx::Channel; +using android::pdx::ChannelReference; +using android::pdx::ClientBase; +using android::pdx::ErrorStatus; +using android::pdx::LocalChannelHandle; +using android::pdx::LocalHandle; +using android::pdx::Message; +using android::pdx::MessageInfo; +using android::pdx::RemoteChannelHandle; +using android::pdx::ServiceBase; +using android::pdx::ServiceDispatcher; +using android::pdx::Status; +using android::pdx::Transaction; +using android::pdx::uds::Endpoint; + +namespace { + +const size_t kLargeDataSize = 100000; + +const char kTestServicePath[] = "socket_test"; +const char kTestService1[] = "1"; +const char kTestService2[] = "2"; + +enum test_op_codes { + TEST_OP_GET_SERVICE_ID, + TEST_OP_SET_TEST_CHANNEL, + TEST_OP_GET_THIS_CHANNEL_ID, + TEST_OP_GET_TEST_CHANNEL_ID, + TEST_OP_CHECK_CHANNEL_ID, + TEST_OP_CHECK_CHANNEL_OBJECT, + TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE, + TEST_OP_GET_NEW_CHANNEL, + TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE, + TEST_OP_GET_THIS_PROCESS_ID, + TEST_OP_GET_THIS_THREAD_ID, + TEST_OP_GET_THIS_EUID, + TEST_OP_GET_THIS_EGID, + TEST_OP_IMPULSE, + TEST_OP_POLLHUP_FROM_SERVICE, + TEST_OP_POLLIN_FROM_SERVICE, + TEST_OP_SEND_LARGE_DATA_RETURN_SUM, +}; + +using ImpulsePayload = std::array<std::uint8_t, sizeof(MessageInfo::impulse)>; + +// The test service creates a TestChannel for every client (channel) that +// connects. This represents the service-side context for each client. +class TestChannel : public Channel { + public: + explicit TestChannel(int channel_id) : channel_id_(channel_id) {} + + int channel_id() const { return channel_id_; } + + private: + friend class TestService; + + int channel_id_; + + TestChannel(const TestChannel&) = delete; + void operator=(const TestChannel&) = delete; +}; + +// Test service that creates a TestChannel for each channel and responds to test +// messages. +class TestService : public ServiceBase<TestService> { + public: + std::shared_ptr<Channel> OnChannelOpen(Message& message) override { + return std::make_shared<TestChannel>(message.GetChannelId()); + } + + void OnChannelClose(Message& /*message*/, + const std::shared_ptr<Channel>& channel) override { + if (test_channel_ == channel) + test_channel_ = nullptr; + } + + void HandleImpulse(Message& message) override { + switch (message.GetOp()) { + case TEST_OP_SET_TEST_CHANNEL: + test_channel_ = message.GetChannel<TestChannel>(); + break; + + case TEST_OP_IMPULSE: { + impulse_payload_.fill(0); + std::copy(message.ImpulseBegin(), message.ImpulseEnd(), + impulse_payload_.begin()); + break; + } + + case TEST_OP_POLLHUP_FROM_SERVICE: { + message.ModifyChannelEvents(0, EPOLLHUP); + break; + } + } + } + + Status<void> HandleMessage(Message& message) override { + switch (message.GetOp()) { + case TEST_OP_GET_SERVICE_ID: + REPLY_MESSAGE_RETURN(message, service_id_, {}); + + // Set the test channel to the TestChannel for the current channel. Other + // messages can use this to perform tests. + case TEST_OP_SET_TEST_CHANNEL: + test_channel_ = message.GetChannel<TestChannel>(); + REPLY_MESSAGE_RETURN(message, 0, {}); + + // Return the channel id for the current channel. + case TEST_OP_GET_THIS_CHANNEL_ID: + REPLY_MESSAGE_RETURN(message, message.GetChannelId(), {}); + + // Return the channel id for the test channel. + case TEST_OP_GET_TEST_CHANNEL_ID: + if (test_channel_) + REPLY_MESSAGE_RETURN(message, test_channel_->channel_id(), {}); + else + REPLY_ERROR_RETURN(message, ENOENT, {}); + + // Test check channel feature. + case TEST_OP_CHECK_CHANNEL_ID: { + ChannelReference ref = 0; + if (!message.ReadAll(&ref, sizeof(ref))) + REPLY_ERROR_RETURN(message, EIO, {}); + + const Status<int> ret = message.CheckChannel<TestChannel>(ref, nullptr); + REPLY_MESSAGE_RETURN(message, ret, {}); + } + + case TEST_OP_CHECK_CHANNEL_OBJECT: { + std::shared_ptr<TestChannel> channel; + ChannelReference ref = 0; + if (!message.ReadAll(&ref, sizeof(ref))) + REPLY_ERROR_RETURN(message, EIO, {}); + + const Status<int> ret = + message.CheckChannel<TestChannel>(ref, &channel); + if (!ret) + REPLY_MESSAGE_RETURN(message, ret, {}); + + if (channel != nullptr) + REPLY_MESSAGE_RETURN(message, channel->channel_id(), {}); + else + REPLY_ERROR_RETURN(message, ENODATA, {}); + } + + case TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE: { + ChannelReference ref = 0; + if (!message.ReadAll(&ref, sizeof(ref))) + REPLY_ERROR_RETURN(message, EIO, {}); + + const Status<int> ret = message.CheckChannel<TestChannel>( + other_service_.get(), ref, nullptr); + REPLY_MESSAGE_RETURN(message, ret, {}); + } + + case TEST_OP_GET_NEW_CHANNEL: { + auto channel = std::make_shared<TestChannel>(-1); + Status<RemoteChannelHandle> channel_handle = + message.PushChannel(0, channel, &channel->channel_id_); + REPLY_MESSAGE_RETURN(message, channel_handle, {}); + } + + case TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE: { + if (!other_service_) + REPLY_ERROR_RETURN(message, EINVAL, {}); + + auto channel = std::make_shared<TestChannel>(-1); + Status<RemoteChannelHandle> channel_handle = message.PushChannel( + other_service_.get(), 0, channel, &channel->channel_id_); + REPLY_MESSAGE_RETURN(message, channel_handle, {}); + } + + case TEST_OP_GET_THIS_PROCESS_ID: + REPLY_MESSAGE_RETURN(message, message.GetProcessId(), {}); + + case TEST_OP_GET_THIS_THREAD_ID: + REPLY_MESSAGE_RETURN(message, message.GetThreadId(), {}); + + case TEST_OP_GET_THIS_EUID: + REPLY_MESSAGE_RETURN(message, message.GetEffectiveUserId(), {}); + + case TEST_OP_GET_THIS_EGID: + REPLY_MESSAGE_RETURN(message, message.GetEffectiveGroupId(), {}); + + case TEST_OP_POLLIN_FROM_SERVICE: + REPLY_MESSAGE_RETURN(message, message.ModifyChannelEvents(0, EPOLLIN), + {}); + + case TEST_OP_SEND_LARGE_DATA_RETURN_SUM: { + std::array<int, kLargeDataSize> data_array; + size_t size_to_read = data_array.size() * sizeof(int); + if (!message.ReadAll(data_array.data(), size_to_read)) { + REPLY_ERROR_RETURN(message, EIO, {}); + } + int sum = std::accumulate(data_array.begin(), data_array.end(), 0); + REPLY_MESSAGE_RETURN(message, sum, {}); + } + + default: + return Service::DefaultHandleMessage(message); + } + } + + const ImpulsePayload& GetImpulsePayload() const { return impulse_payload_; } + + private: + friend BASE; + + std::shared_ptr<TestChannel> test_channel_; + std::shared_ptr<TestService> other_service_; + int service_id_; + ImpulsePayload impulse_payload_; + + static std::atomic<int> service_counter_; + + TestService(const std::string& name, + const std::shared_ptr<TestService>& other_service) + : TestService(name, other_service, false) {} + + TestService(const std::string& name, + const std::shared_ptr<TestService>& other_service, bool blocking) + : BASE(std::string("TestService") + name, + Endpoint::CreateAndBindSocket(kTestServicePath + name, blocking)), + other_service_(other_service), + service_id_(service_counter_++) {} + + explicit TestService(const std::string& name) : TestService(name, nullptr) {} + + TestService(const TestService&) = delete; + void operator=(const TestService&) = delete; +}; + +std::atomic<int> TestService::service_counter_; + +// Test client to send messages to the test service. +class TestClient : public ClientBase<TestClient> { + public: + // Requests the service id of the service this channel is connected to. + int GetServiceId() { + Transaction trans{*this}; + return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_SERVICE_ID)); + } + + // Requests the test channel to be set to this client's channel. + int SetTestChannel() { + Transaction trans{*this}; + return ReturnStatusOrError(trans.Send<int>(TEST_OP_SET_TEST_CHANNEL)); + } + + // Request the test channel to be set to this client's channel using an async + // message. + int SetTestChannelAsync() { + return ReturnStatusOrError(SendImpulse(TEST_OP_SET_TEST_CHANNEL)); + } + + // Sends a test async message with payload. + int SendAsync(const void* buffer, size_t length) { + Transaction trans{*this}; + return ReturnStatusOrError(SendImpulse(TEST_OP_IMPULSE, buffer, length)); + } + + // Requests the channel id for this client. + int GetThisChannelId() { + Transaction trans{*this}; + return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_CHANNEL_ID)); + } + + // Requests the channel id of the test channel. + int GetTestChannelId() { + Transaction trans{*this}; + return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_TEST_CHANNEL_ID)); + } + + // Checks whether the fd |channel_id| is a channel to the test service. + // Returns the channel id of the channel. + int CheckChannelIdArgument(BorrowedChannelHandle channel) { + Transaction trans{*this}; + ChannelReference ref = trans.PushChannelHandle(channel).get(); + return ReturnStatusOrError(trans.Send<int>(TEST_OP_CHECK_CHANNEL_ID, &ref, + sizeof(ref), nullptr, 0)); + } + + // Checks whether the fd |channel_id| is a channel to the test service. + // Returns the channel id of the channel exercising the context pointer. + int CheckChannelObjectArgument(BorrowedChannelHandle channel) { + Transaction trans{*this}; + ChannelReference ref = trans.PushChannelHandle(channel).get(); + return ReturnStatusOrError(trans.Send<int>(TEST_OP_CHECK_CHANNEL_OBJECT, + &ref, sizeof(ref), nullptr, 0)); + } + + // Checks whether the fd |channel_fd| is a channel to the other test service. + // Returns 0 on success. + int CheckChannelFromOtherService(BorrowedChannelHandle channel) { + Transaction trans{*this}; + ChannelReference ref = trans.PushChannelHandle(channel).get(); + return ReturnStatusOrError( + trans.Send<int>(TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE, &ref, + sizeof(ref), nullptr, 0)); + } + + // Requests a new channel to the service. + std::unique_ptr<TestClient> GetNewChannel() { + Transaction trans{*this}; + auto status = trans.Send<LocalChannelHandle>(TEST_OP_GET_NEW_CHANNEL); + if (status) + return TestClient::Create(status.take()); + else + return nullptr; + } + + // Requests a new channel to the other service. + std::unique_ptr<TestClient> GetNewChannelFromOtherService() { + Transaction trans{*this}; + auto status = trans.Send<LocalChannelHandle>( + TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE); + if (status) + return TestClient::Create(status.take()); + else + return nullptr; + } + + // Requests an id from the message description. + pid_t GetThisProcessId() { + Transaction trans{*this}; + return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_PROCESS_ID)); + } + pid_t GetThisThreadId() { + Transaction trans{*this}; + return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_THREAD_ID)); + } + uid_t GetThisEffectiveUserId() { + Transaction trans{*this}; + return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_EUID)); + } + gid_t GetThisEffectiveGroupId() { + Transaction trans{*this}; + return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_EGID)); + } + + int SendPollHupEvent() { + return ReturnStatusOrError(SendImpulse(TEST_OP_POLLHUP_FROM_SERVICE)); + } + + int SendPollInEvent() { + Transaction trans{*this}; + return ReturnStatusOrError(trans.Send<int>(TEST_OP_POLLIN_FROM_SERVICE)); + } + + int SendLargeDataReturnSum( + const std::array<int, kLargeDataSize>& data_array) { + Transaction trans{*this}; + return ReturnStatusOrError( + trans.Send<int>(TEST_OP_SEND_LARGE_DATA_RETURN_SUM, data_array.data(), + data_array.size() * sizeof(int), nullptr, 0)); + } + + Status<int> GetEventMask(int events) { + if (auto* client_channel = GetChannel()) { + return client_channel->GetEventMask(events); + } else { + return ErrorStatus(EINVAL); + } + } + + using ClientBase<TestClient>::event_fd; + + enum : size_t { kMaxPayload = MAX_IMPULSE_LENGTH }; + + private: + friend BASE; + + explicit TestClient(const std::string& name) + : BASE{android::pdx::uds::ClientChannelFactory::Create(kTestServicePath + + name)} {} + + explicit TestClient(LocalChannelHandle channel) + : BASE{android::pdx::uds::ClientChannel::Create(std::move(channel))} {} + + TestClient(const TestClient&) = delete; + void operator=(const TestClient&) = delete; +}; + +} // anonymous namespace + +// Use a test fixture to ensure proper order of cleanup between clients, +// services, and the dispatcher. These objects are cleaned up in the same +// thread, order is important; either the service or the client must be +// destroyed before the dispatcher is stopped. The reason for this is that +// clients send blocking "close" messages to their respective services on +// destruction. If this happens after the dispatcher is stopped the client +// destructor will get blocked waiting for a reply that will never come. In +// normal use of the service framework this is never an issue because clients +// and the dispatcher for the same service are never destructed in the same +// thread (they live in different processes). +class ServiceFrameworkTest : public ::testing::Test { + protected: + std::unique_ptr<ServiceDispatcher> dispatcher_; + std::thread dispatch_thread_; + + void SetUp() override { + // Create a dispatcher to handle messages to services. + dispatcher_ = android::pdx::uds::ServiceDispatcher::Create(); + ASSERT_NE(nullptr, dispatcher_); + + // Start the message dispatch loop in a separate thread. + dispatch_thread_ = std::thread( + std::bind(&ServiceDispatcher::EnterDispatchLoop, dispatcher_.get())); + } + + void TearDown() override { + if (dispatcher_) { + // Cancel the dispatcher and wait for the thread to terminate. Explicitly + // join the thread so that destruction doesn't deallocate the dispatcher + // before the thread finishes. + dispatcher_->SetCanceled(true); + dispatch_thread_.join(); + } + } +}; + +// Test basic operation of TestService/TestClient classes. +TEST_F(ServiceFrameworkTest, BasicClientService) { + // Create a test service and add it to the dispatcher. + auto service = TestService::Create(kTestService1); + ASSERT_NE(nullptr, service); + ASSERT_EQ(0, dispatcher_->AddService(service)); + + // Create a client to service. + auto client = TestClient::Create(kTestService1); + ASSERT_NE(nullptr, client); + + // Get the channel id that will be returned by the next tests. + const int channel_id = client->GetThisChannelId(); + EXPECT_LE(0, channel_id); + + // Check return value before test channel is set. + EXPECT_EQ(-ENOENT, client->GetTestChannelId()); + + // Set test channel and perform the test again. + EXPECT_EQ(0, client->SetTestChannel()); + EXPECT_EQ(channel_id, client->GetTestChannelId()); +} + +// Test impulses. +TEST_F(ServiceFrameworkTest, Impulse) { + // Create a test service and add it to the dispatcher. + auto service = TestService::Create(kTestService1); + ASSERT_NE(nullptr, service); + ASSERT_EQ(0, dispatcher_->AddService(service)); + + auto client = TestClient::Create(kTestService1); + ASSERT_NE(nullptr, client); + + // Get the channel id that will be returned by the next tests. + const int channel_id = client->GetThisChannelId(); + EXPECT_LE(0, channel_id); + + // Check return value before test channel is set. + EXPECT_EQ(-ENOENT, client->GetTestChannelId()); + + // Set test channel with an impulse and perform the test again. + EXPECT_EQ(0, client->SetTestChannelAsync()); + EXPECT_EQ(channel_id, client->GetTestChannelId()); + + ImpulsePayload expected_payload = {{'a', 'b', 'c'}}; + EXPECT_EQ(0, client->SendAsync(expected_payload.data(), 3)); + // Send a synchronous message to make sure the async message is handled before + // we check the payload. + client->GetThisChannelId(); + EXPECT_EQ(expected_payload, service->GetImpulsePayload()); + + // Impulse payloads are limited to 4 machine words. + EXPECT_EQ( + 0, client->SendAsync(expected_payload.data(), TestClient::kMaxPayload)); + EXPECT_EQ(-EINVAL, client->SendAsync(expected_payload.data(), + TestClient::kMaxPayload + 1)); + + // Test invalid pointer. + const std::uint8_t* invalid_pointer = nullptr; + EXPECT_EQ(-EINVAL, client->SendAsync(invalid_pointer, sizeof(int))); +} + +// Test Message::PushChannel/Service::PushChannel API. +TEST_F(ServiceFrameworkTest, PushChannel) { + // Create a test service and add it to the dispatcher. + auto other_service = TestService::Create(kTestService1); + ASSERT_NE(nullptr, other_service); + ASSERT_EQ(0, dispatcher_->AddService(other_service)); + + // Create a second test service and add it to the dispatcher. + auto service = TestService::Create(kTestService2, other_service); + ASSERT_NE(nullptr, service); + ASSERT_EQ(0, dispatcher_->AddService(service)); + + // Create a client to the second test service. + auto client1 = TestClient::Create(kTestService2); + ASSERT_NE(nullptr, client1); + + // Test the creation of new channels using the push APIs. + const int channel_id1 = client1->GetThisChannelId(); + EXPECT_LE(0, channel_id1); + + auto client2 = client1->GetNewChannel(); + EXPECT_NE(nullptr, client2); + EXPECT_NE(client1->event_fd(), client2->event_fd()); + + const int channel_id2 = client2->GetThisChannelId(); + EXPECT_LE(0, channel_id2); + EXPECT_NE(channel_id1, channel_id2); + + auto client3 = client1->GetNewChannelFromOtherService(); + EXPECT_NE(nullptr, client3); + EXPECT_NE(client1->event_fd(), client3->event_fd()); + + const int channel_id3 = client3->GetThisChannelId(); + EXPECT_LE(0, channel_id3); + + // Test which services the channels are connected to. + const int service_id1 = client1->GetServiceId(); + EXPECT_LE(0, service_id1); + + const int service_id2 = client2->GetServiceId(); + EXPECT_LE(0, service_id2); + + const int service_id3 = client3->GetServiceId(); + EXPECT_LE(0, service_id3); + + EXPECT_EQ(service_id1, service_id2); + EXPECT_NE(service_id1, service_id3); +} + +// Tests process id, thread id, effective user id, and effective group id +// returned in the message description. +TEST_F(ServiceFrameworkTest, Ids) { + // Create a test service and add it to the dispatcher. + auto service = TestService::Create(kTestService1); + ASSERT_NE(nullptr, service); + ASSERT_EQ(0, dispatcher_->AddService(service)); + + // Create a client to service. + auto client = TestClient::Create(kTestService1); + ASSERT_NE(nullptr, client); + + // Pids 0-2 are defined, no user task should have them. + + const pid_t process_id1 = client->GetThisProcessId(); + EXPECT_LT(2, process_id1); + + pid_t process_id2; + + std::thread thread([&]() { + process_id2 = client->GetThisProcessId(); + }); + thread.join(); + + EXPECT_LT(2, process_id2); + EXPECT_EQ(process_id1, process_id2); + + // This test must run as root for the rest of these tests to work. + const int euid1 = client->GetThisEffectiveUserId(); + ASSERT_EQ(0, euid1); + + const int egid1 = client->GetThisEffectiveGroupId(); + EXPECT_EQ(0, egid1); + + // Set effective uid/gid to system. + ASSERT_EQ(0, setegid(AID_SYSTEM)); + ASSERT_EQ(0, seteuid(AID_SYSTEM)); + + const int euid2 = client->GetThisEffectiveUserId(); + EXPECT_EQ(AID_SYSTEM, euid2); + + const int egid2 = client->GetThisEffectiveGroupId(); + EXPECT_EQ(AID_SYSTEM, egid2); + + // Set the euid/egid back to root. + ASSERT_EQ(0, setegid(0)); + ASSERT_EQ(0, seteuid(0)); +} + +TEST_F(ServiceFrameworkTest, PollIn) { + // Create a test service and add it to the dispatcher. + auto service = TestService::Create(kTestService1); + ASSERT_NE(nullptr, service); + ASSERT_EQ(0, dispatcher_->AddService(service)); + + // Create a client to service. + auto client = TestClient::Create(kTestService1); + ASSERT_NE(nullptr, client); + + epoll_event event; + int count = epoll_wait(client->event_fd(), &event, 1, 0); + ASSERT_EQ(0, count); + + client->SendPollInEvent(); + + count = epoll_wait(client->event_fd(), &event, 1, -1); + ASSERT_EQ(1, count); + ASSERT_TRUE((EPOLLIN & event.events) != 0); +} + +TEST_F(ServiceFrameworkTest, PollHup) { + // Create a test service and add it to the dispatcher. + auto service = TestService::Create(kTestService1); + ASSERT_NE(nullptr, service); + ASSERT_EQ(0, dispatcher_->AddService(service)); + + // Create a client to service. + auto client = TestClient::Create(kTestService1); + ASSERT_NE(nullptr, client); + + epoll_event event; + int count = epoll_wait(client->event_fd(), &event, 1, 0); + ASSERT_EQ(0, count); + + client->SendPollHupEvent(); + + count = epoll_wait(client->event_fd(), &event, 1, -1); + ASSERT_EQ(1, count); + auto event_status = client->GetEventMask(event.events); + ASSERT_TRUE(event_status.ok()); + ASSERT_TRUE((EPOLLHUP & event_status.get()) != 0); +} + +TEST_F(ServiceFrameworkTest, LargeDataSum) { + // Create a test service and add it to the dispatcher. + auto service = TestService::Create(kTestService1); + ASSERT_NE(nullptr, service); + ASSERT_EQ(0, dispatcher_->AddService(service)); + + // Create a client to service. + auto client = TestClient::Create(kTestService1); + ASSERT_NE(nullptr, client); + + std::array<int, kLargeDataSize> data_array; + std::iota(data_array.begin(), data_array.end(), 0); + int expected_sum = std::accumulate(data_array.begin(), data_array.end(), 0); + int sum = client->SendLargeDataReturnSum(data_array); + ASSERT_EQ(expected_sum, sum); +} + +TEST_F(ServiceFrameworkTest, Cancel) { + // Create a test service and add it to the dispatcher. + auto service = TestService::Create(kTestService1, nullptr, true); + ASSERT_NE(nullptr, service); + ASSERT_EQ(0, dispatcher_->AddService(service)); + + // Create a client to service. + auto client = TestClient::Create(kTestService1); + ASSERT_NE(nullptr, client); + + auto previous_time = std::chrono::system_clock::now(); + dispatcher_->ReceiveAndDispatch(100); // 0.1 seconds should block. + auto time = std::chrono::system_clock::now(); + ASSERT_LE(100, std::chrono::duration_cast<std::chrono::milliseconds>( + time - previous_time) + .count()); + service->Cancel(); + // Non-blocking. Return immediately. + dispatcher_->ReceiveAndDispatch(-1); + dispatcher_->ReceiveAndDispatch(-1); +} diff --git a/libs/vr/libperformance/Android.bp b/libs/vr/libperformance/Android.bp new file mode 100644 index 0000000000..364873dae2 --- /dev/null +++ b/libs/vr/libperformance/Android.bp @@ -0,0 +1,41 @@ +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +sourceFiles = [ + "performance_client.cpp", + "performance_rpc.cpp", +] + +includeFiles = [ "include" ] + +staticLibraries = ["libpdx_default_transport"] + +sharedLibraries = [ + "libbase", + "libcutils", + "liblog", + "libutils", +] + +cc_library { + srcs: sourceFiles, + cflags: [ + "-DLOG_TAG=\"libperformance\"", + "-DTRACE=0" + ], + export_include_dirs: includeFiles, + static_libs: staticLibraries, + shared_libs: sharedLibraries, + name: "libperformance", +} diff --git a/libs/vr/libperformance/include/CPPLINT.cfg b/libs/vr/libperformance/include/CPPLINT.cfg new file mode 100644 index 0000000000..2f8a3c018c --- /dev/null +++ b/libs/vr/libperformance/include/CPPLINT.cfg @@ -0,0 +1 @@ +filter=-build/header_guard diff --git a/libs/vr/libperformance/include/dvr/performance_client_api.h b/libs/vr/libperformance/include/dvr/performance_client_api.h new file mode 100644 index 0000000000..2216e3869a --- /dev/null +++ b/libs/vr/libperformance/include/dvr/performance_client_api.h @@ -0,0 +1,59 @@ +#ifndef ANDROID_DVR_PERFORMANCE_CLIENT_API_H_ +#define ANDROID_DVR_PERFORMANCE_CLIENT_API_H_ + +#include <stddef.h> +#include <unistd.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/// Sets the CPU partition for a task. +/// +/// Sets the CPU partition for a task to the partition described by a CPU +/// partition path. +/// +/// TODO(eieio): Describe supported partitions and rules governing assignment. +/// +/// @param task_id The task id of task to attach to a partition. When task_id is +/// 0 the current task id is substituted. +/// @param partition NULL-terminated ASCII string describing the CPU partition +/// to attach the task to. +/// @returns Returns 0 on success or a negative errno error code on error. +int dvrSetCpuPartition(pid_t task_id, const char* partition); + +/// Sets the scheduler class for a task. +/// +/// Sets the scheduler class for a task to the class described by a semantic +/// string. +/// +/// Supported classes for applications are: audio, graphics, normal, and +/// background. Additional options following a ':' to be supported in the +/// future. +/// +/// @param task_id The task id of task to attach to a partition. When task_id is +/// 0 the current task id is substituted. +/// @param scheduler_class NULL-terminated ASCII string containing the desired +/// scheduler class. +/// @returns Returns 0 on success or a negative errno error code on error. +int dvrSetSchedulerClass(pid_t task_id, const char* scheduler_class); + +/// Gets the CPU partition for a task. +/// +/// Gets the CPU partition path for a task as a NULL-terminated ASCII string. If +/// the path is too large to fit in the supplied buffer, -ENOBUFS is returned. +/// +/// @param task_id The task id of the task to retrieve the partition for. When +/// task_id is 0 the current task id is substituted. +/// @param partition Pointer to an ASCII string buffer to store the partition +/// path. +/// @param size Size of the string buffer in bytes, including space for the NULL +/// terminator. +/// @returns Returns 0 on success or a negative errno error code on error. +int dvrGetCpuPartition(pid_t task_id, char* partition, size_t size); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ANDROID_DVR_PERFORMANCE_CLIENT_API_H_ diff --git a/libs/vr/libperformance/include/private/dvr/performance_client.h b/libs/vr/libperformance/include/private/dvr/performance_client.h new file mode 100644 index 0000000000..a61c6b2773 --- /dev/null +++ b/libs/vr/libperformance/include/private/dvr/performance_client.h @@ -0,0 +1,36 @@ +#ifndef ANDROID_DVR_PERFORMANCE_CLIENT_H_ +#define ANDROID_DVR_PERFORMANCE_CLIENT_H_ + +#include <sys/types.h> + +#include <cstddef> +#include <string> +#include <tuple> + +#include <pdx/client.h> + +namespace android { +namespace dvr { + +class PerformanceClient : public pdx::ClientBase<PerformanceClient> { + public: + int SetCpuPartition(pid_t task_id, const std::string& partition); + int SetCpuPartition(pid_t task_id, const char* partition); + int SetSchedulerClass(pid_t task_id, const std::string& scheduler_class); + int SetSchedulerClass(pid_t task_id, const char* scheduler_class); + int GetCpuPartition(pid_t task_id, std::string* partition_out); + int GetCpuPartition(pid_t task_id, char* partition_out, std::size_t size); + + private: + friend BASE; + + explicit PerformanceClient(int* error); + + PerformanceClient(const PerformanceClient&) = delete; + void operator=(const PerformanceClient&) = delete; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_PERFORMANCE_CLIENT_H_ diff --git a/libs/vr/libperformance/include/private/dvr/performance_rpc.h b/libs/vr/libperformance/include/private/dvr/performance_rpc.h new file mode 100644 index 0000000000..73bdaa7d5c --- /dev/null +++ b/libs/vr/libperformance/include/private/dvr/performance_rpc.h @@ -0,0 +1,37 @@ +#ifndef ANDROID_DVR_PERFORMANCE_RPC_H_ +#define ANDROID_DVR_PERFORMANCE_RPC_H_ + +#include <sys/types.h> + +#include <string> + +#include <pdx/rpc/remote_method_type.h> + +namespace android { +namespace dvr { + +// Performance Service RPC interface. Defines the endpoint paths, op codes, and +// method type signatures supported by performanced. +struct PerformanceRPC { + // Service path. + static constexpr char kClientPath[] = "system/performance/client"; + + // Op codes. + enum { + kOpSetCpuPartition = 0, + kOpSetSchedulerClass, + kOpGetCpuPartition, + }; + + // Methods. + PDX_REMOTE_METHOD(SetCpuPartition, kOpSetCpuPartition, + int(pid_t, const std::string&)); + PDX_REMOTE_METHOD(SetSchedulerClass, kOpSetSchedulerClass, + int(pid_t, const std::string&)); + PDX_REMOTE_METHOD(GetCpuPartition, kOpGetCpuPartition, std::string(pid_t)); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_PERFORMANCE_RPC_H_ diff --git a/libs/vr/libperformance/performance_client.cpp b/libs/vr/libperformance/performance_client.cpp new file mode 100644 index 0000000000..2124162bf7 --- /dev/null +++ b/libs/vr/libperformance/performance_client.cpp @@ -0,0 +1,119 @@ +#include "include/private/dvr/performance_client.h" + +#include <sys/types.h> + +#include <pdx/default_transport/client_channel_factory.h> +#include <pdx/rpc/remote_method.h> +#include <pdx/rpc/string_wrapper.h> +#include <private/dvr/performance_rpc.h> + +using android::pdx::rpc::WrapString; + +namespace android { +namespace dvr { + +PerformanceClient::PerformanceClient(int* error) + : BASE(pdx::default_transport::ClientChannelFactory::Create( + PerformanceRPC::kClientPath)) { + if (error) + *error = Client::error(); +} + +int PerformanceClient::SetCpuPartition(pid_t task_id, + const std::string& partition) { + if (task_id == 0) + task_id = gettid(); + + return ReturnStatusOrError( + InvokeRemoteMethod<PerformanceRPC::SetCpuPartition>(task_id, partition)); +} + +int PerformanceClient::SetCpuPartition(pid_t task_id, const char* partition) { + if (task_id == 0) + task_id = gettid(); + + return ReturnStatusOrError( + InvokeRemoteMethod<PerformanceRPC::SetCpuPartition>( + task_id, WrapString(partition))); +} + +int PerformanceClient::SetSchedulerClass(pid_t task_id, + const std::string& scheduler_class) { + if (task_id == 0) + task_id = gettid(); + + return ReturnStatusOrError( + InvokeRemoteMethod<PerformanceRPC::SetSchedulerClass>(task_id, + scheduler_class)); +} + +int PerformanceClient::SetSchedulerClass(pid_t task_id, + const char* scheduler_class) { + if (task_id == 0) + task_id = gettid(); + + return ReturnStatusOrError( + InvokeRemoteMethod<PerformanceRPC::SetSchedulerClass>( + task_id, WrapString(scheduler_class))); +} + +int PerformanceClient::GetCpuPartition(pid_t task_id, + std::string* partition_out) { + if (partition_out == nullptr) + return -EINVAL; + + if (task_id == 0) + task_id = gettid(); + + auto status = InvokeRemoteMethodInPlace<PerformanceRPC::GetCpuPartition>( + partition_out, task_id); + return status ? 0 : -status.error(); +} + +int PerformanceClient::GetCpuPartition(pid_t task_id, char* partition_out, + std::size_t size) { + if (partition_out == nullptr) + return -EINVAL; + + if (task_id == 0) + task_id = gettid(); + + auto wrapper = WrapString(partition_out, size); + auto status = InvokeRemoteMethodInPlace<PerformanceRPC::GetCpuPartition>( + &wrapper, task_id); + if (!status) + return -status.error(); + + if (wrapper.size() < size) + partition_out[wrapper.size()] = '\0'; + + return 0; +} + +} // namespace dvr +} // namespace android + +extern "C" int dvrSetCpuPartition(pid_t task_id, const char* partition) { + int error; + if (auto client = android::dvr::PerformanceClient::Create(&error)) + return client->SetCpuPartition(task_id, partition); + else + return error; +} + +extern "C" int dvrSetSchedulerClass(pid_t task_id, + const char* scheduler_class) { + int error; + if (auto client = android::dvr::PerformanceClient::Create(&error)) + return client->SetSchedulerClass(task_id, scheduler_class); + else + return error; +} + +extern "C" int dvrGetCpuPartition(pid_t task_id, char* partition, size_t size) { + int error; + if (auto client = android::dvr::PerformanceClient::Create(&error)) + return client->GetCpuPartition(task_id, partition, size); + else + return error; +} diff --git a/libs/vr/libperformance/performance_rpc.cpp b/libs/vr/libperformance/performance_rpc.cpp new file mode 100644 index 0000000000..9135349ca2 --- /dev/null +++ b/libs/vr/libperformance/performance_rpc.cpp @@ -0,0 +1,9 @@ +#include "include/private/dvr/performance_rpc.h" + +namespace android { +namespace dvr { + +constexpr char PerformanceRPC::kClientPath[]; + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libvr_manager/Android.bp b/libs/vr/libvr_manager/Android.bp new file mode 100644 index 0000000000..87848779f2 --- /dev/null +++ b/libs/vr/libvr_manager/Android.bp @@ -0,0 +1,36 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +exported_include_dirs = [ "include" ] + +include_dirs = ["frameworks/native/include/vr/vr_manager"] + +src_files = [ + "vr_manager.cpp", + "trusted_uids.cpp", +] + +static_libs = [ + "libutils", + "libbinder", +] + +cc_library_static { + srcs: src_files, + include_dirs: include_dirs, + export_include_dirs: exported_include_dirs, + cflags: ["-Wall", "-Werror", "-Wunused", "-Wunreachable-code"], + static_libs: static_libs, + name: "libvr_manager", +} diff --git a/libs/vr/libvr_manager/include/private/dvr/trusted_uids.h b/libs/vr/libvr_manager/include/private/dvr/trusted_uids.h new file mode 100644 index 0000000000..4496fbff7a --- /dev/null +++ b/libs/vr/libvr_manager/include/private/dvr/trusted_uids.h @@ -0,0 +1,33 @@ +#ifndef ANDROID_DVR_TRUSTED_UIDS_H_ +#define ANDROID_DVR_TRUSTED_UIDS_H_ + +#include <sys/types.h> + +namespace android { +namespace dvr { + +/** + * Tells if a provided UID can be trusted to access restricted VR APIs. + * + * UID trust is based on the android.permission.RESTRICTED_VR_ACCESS permission. + * AID_SYSTEM and AID_ROOT are automatically trusted by Android. + * + * UIDs are guaranteed not to be reused until the next reboot even in case + * of package reinstall. For performance reasons this method caches results by + * default, as otherwise every check would trigger a Java call. + * + * This function is thread-safe. + * + * @param uid The uid to check. + * @param use_cache If true any cached result for the provided uid will be + * reused. If false this call will reach the Application Manager Service + * in Java to get updated values. Any updates will be stored in the cache. + * @return true if the uid is trusted, false if not or if the VR Manager Service + * could not be reached to verify the uid. + */ +bool IsTrustedUid(uid_t uid, bool use_cache = true); + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_TRUSTED_UIDS_H_ diff --git a/libs/vr/libvr_manager/trusted_uids.cpp b/libs/vr/libvr_manager/trusted_uids.cpp new file mode 100644 index 0000000000..4228a050db --- /dev/null +++ b/libs/vr/libvr_manager/trusted_uids.cpp @@ -0,0 +1,51 @@ +#include "private/dvr/trusted_uids.h" + +#include <mutex> +#include <unordered_map> + +#include <binder/IPermissionController.h> +#include <binder/IServiceManager.h> +#include <private/android_filesystem_config.h> +#include <utils/String16.h> +#include <vr/vr_manager/vr_manager.h> + +namespace android { +namespace dvr { + +bool IsTrustedUid(uid_t uid, bool use_cache) { + static std::unordered_map<uid_t, bool> uid_cache; + static std::mutex uid_cache_mutex; + + // Whitelist requests from the system UID. + // These are already whitelisted by the permission service, but it might not + // be available if the ActivityManagerService is up during boot. + // This ensures the correct result for system services while booting up. + if (uid == AID_SYSTEM) + return true; + + std::lock_guard<std::mutex> lock(uid_cache_mutex); + + if (use_cache) { + auto it = uid_cache.find(uid); + if (it != uid_cache.end()) + return it->second; + } + + sp<IBinder> binder = defaultServiceManager()->getService(String16("permission")); + if (binder == 0) { + ALOGW("Could not access permission service"); + return false; + } + + // Note: we ignore the pid because it's only used to automatically reply + // true if the caller is the Activity Manager Service. + bool trusted = interface_cast<IPermissionController>(binder)->checkPermission( + String16("android.permission.RESTRICTED_VR_ACCESS"), -1, uid); + + // Cache the information for this uid to avoid future Java calls. + uid_cache[uid] = trusted; + return trusted; +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libvr_manager/vr_manager.cpp b/libs/vr/libvr_manager/vr_manager.cpp new file mode 100644 index 0000000000..5cfc22eec3 --- /dev/null +++ b/libs/vr/libvr_manager/vr_manager.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "VrManager" +#include <utils/Log.h> + +#include <vr/vr_manager/vr_manager.h> +#include <stdint.h> +#include <sys/types.h> +#include <binder/Parcel.h> + +namespace android { + +// Must be kept in sync with interface defined in IVrStateCallbacks.aidl. + +class BpVrStateCallbacks : public BpInterface<IVrStateCallbacks> { + public: + explicit BpVrStateCallbacks(const sp<IBinder>& impl) + : BpInterface<IVrStateCallbacks>(impl) {} + + void onVrStateChanged(bool enabled) { + Parcel data, reply; + data.writeInterfaceToken(IVrStateCallbacks::getInterfaceDescriptor()); + data.writeBool(enabled); + remote()->transact(ON_VR_STATE_CHANGED, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(VrStateCallbacks, "android.service.vr.IVrStateCallbacks"); + +status_t BnVrStateCallbacks::onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags) { + switch(code) { + case ON_VR_STATE_CHANGED: { + CHECK_INTERFACE(IVrStateCallbacks, data, reply); + onVrStateChanged(data.readBool()); + return OK; + } + } + return BBinder::onTransact(code, data, reply, flags); +} + +// Must be kept in sync with interface defined in +// IPersistentVrStateCallbacks.aidl. + +class BpPersistentVrStateCallbacks + : public BpInterface<IPersistentVrStateCallbacks> { + public: + explicit BpPersistentVrStateCallbacks(const sp<IBinder>& impl) + : BpInterface<IPersistentVrStateCallbacks>(impl) {} + + void onPersistentVrStateChanged(bool enabled) { + Parcel data, reply; + data.writeInterfaceToken( + IPersistentVrStateCallbacks::getInterfaceDescriptor()); + data.writeBool(enabled); + remote()->transact(ON_PERSISTENT_VR_STATE_CHANGED, + data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(PersistentVrStateCallbacks, + "android.service.vr.IPersistentVrStateCallbacks"); + +status_t BnPersistentVrStateCallbacks::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + switch(code) { + case ON_PERSISTENT_VR_STATE_CHANGED: { + CHECK_INTERFACE(IPersistentVrStateCallbacks, data, reply); + onPersistentVrStateChanged(data.readBool()); + return OK; + } + } + return BBinder::onTransact(code, data, reply, flags); +} + +// Must be kept in sync with interface defined in IVrManager.aidl. + +class BpVrManager : public BpInterface<IVrManager> { + public: + explicit BpVrManager(const sp<IBinder>& impl) + : BpInterface<IVrManager>(impl) {} + + void registerListener(const sp<IVrStateCallbacks>& cb) override { + Parcel data; + data.writeInterfaceToken(IVrManager::getInterfaceDescriptor()); + data.writeStrongBinder(IInterface::asBinder(cb)); + remote()->transact(REGISTER_LISTENER, data, NULL); + } + + void unregisterListener(const sp<IVrStateCallbacks>& cb) override { + Parcel data; + data.writeInterfaceToken(IVrManager::getInterfaceDescriptor()); + data.writeStrongBinder(IInterface::asBinder(cb)); + remote()->transact(UNREGISTER_LISTENER, data, NULL); + } + + void registerPersistentVrStateListener( + const sp<IPersistentVrStateCallbacks>& cb) override { + Parcel data; + data.writeInterfaceToken(IVrManager::getInterfaceDescriptor()); + data.writeStrongBinder(IInterface::asBinder(cb)); + remote()->transact(REGISTER_PERSISTENT_VR_STATE_LISTENER, data, NULL); + } + + void unregisterPersistentVrStateListener( + const sp<IPersistentVrStateCallbacks>& cb) override { + Parcel data; + data.writeInterfaceToken(IVrManager::getInterfaceDescriptor()); + data.writeStrongBinder(IInterface::asBinder(cb)); + remote()->transact(UNREGISTER_PERSISTENT_VR_STATE_LISTENER, data, NULL); + } + + bool getVrModeState() override { + Parcel data, reply; + data.writeInterfaceToken(IVrManager::getInterfaceDescriptor()); + remote()->transact(GET_VR_MODE_STATE, data, &reply); + int32_t ret = reply.readExceptionCode(); + if (ret != 0) { + return false; + } + return reply.readBool(); + } +}; + +IMPLEMENT_META_INTERFACE(VrManager, "android.service.vr.IVrManager"); + +} // namespace android diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp new file mode 100644 index 0000000000..de26a74f48 --- /dev/null +++ b/libs/vr/libvrflinger/Android.bp @@ -0,0 +1,78 @@ +// Copyright (C) 2008 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +sourceFiles = [ + "acquired_buffer.cpp", + "epoll_event_dispatcher.cpp", + "display_manager_service.cpp", + "display_service.cpp", + "display_surface.cpp", + "hardware_composer.cpp", + "vr_flinger.cpp", + "vsync_service.cpp", +] + +includeFiles = [ "include" ] + +staticLibraries = [ + "libsurfaceflingerincludes", + "libhwcomposer-command-buffer", + "libbufferhub", + "libbufferhubqueue", + "libdisplay", + "libdvrcommon", + "libperformance", + "libvrsensor", + "libpdx_default_transport", + "libvr_manager", +] + +sharedLibraries = [ + "android.frameworks.vr.composer@1.0", + "android.hardware.graphics.allocator@2.0", + "android.hardware.graphics.composer@2.1", + "libbinder", + "libbase", + "libcutils", + "liblog", + "libhardware", + "libnativewindow", + "libutils", + "libEGL", + "libGLESv1_CM", + "libGLESv2", + "libvulkan", + "libui", + "libgui", + "libsync", + "libhidlbase", + "libhidltransport", + "libfmq", +] + +cc_library_static { + srcs: sourceFiles, + export_include_dirs: includeFiles, + + cflags: [ + "-DLOG_TAG=\"vr_flinger\"", + "-DTRACE=0", + "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", + "-DGL_GLEXT_PROTOTYPES", + "-DEGL_EGLEXT_PROTOTYPES", + ], + shared_libs: sharedLibraries, + whole_static_libs: staticLibraries, + name: "libvrflinger", +} diff --git a/libs/vr/libvrflinger/acquired_buffer.cpp b/libs/vr/libvrflinger/acquired_buffer.cpp new file mode 100644 index 0000000000..7932a9c9d3 --- /dev/null +++ b/libs/vr/libvrflinger/acquired_buffer.cpp @@ -0,0 +1,100 @@ +#include "acquired_buffer.h" + +#include <log/log.h> +#include <sync/sync.h> + +using android::pdx::LocalHandle; + +namespace android { +namespace dvr { + +AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer, + LocalHandle acquire_fence) + : buffer_(buffer), acquire_fence_(std::move(acquire_fence)) {} + +AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer, + int* error) { + LocalHandle fence; + const int ret = buffer->Acquire(&fence); + + if (error) + *error = ret; + + if (ret < 0) { + ALOGW("AcquiredBuffer::AcquiredBuffer: Failed to acquire buffer: %s", + strerror(-ret)); + buffer_ = nullptr; + // Default construct sets acquire_fence_ to empty. + } else { + buffer_ = buffer; + acquire_fence_ = std::move(fence); + } +} + +AcquiredBuffer::AcquiredBuffer(AcquiredBuffer&& other) + : buffer_(std::move(other.buffer_)), + acquire_fence_(std::move(other.acquire_fence_)) {} + +AcquiredBuffer::~AcquiredBuffer() { Release(LocalHandle(kEmptyFence)); } + +AcquiredBuffer& AcquiredBuffer::operator=(AcquiredBuffer&& other) { + if (this != &other) { + Release(LocalHandle(kEmptyFence)); + + buffer_ = std::move(other.buffer_); + acquire_fence_ = std::move(other.acquire_fence_); + } + return *this; +} + +bool AcquiredBuffer::IsAvailable() const { + if (IsEmpty()) + return false; + + // Only check the fence if the acquire fence is not empty. + if (acquire_fence_) { + const int ret = sync_wait(acquire_fence_.Get(), 0); + ALOGD_IF(TRACE || (ret < 0 && errno != ETIME), + "AcquiredBuffer::IsAvailable: acquire_fence_=%d sync_wait()=%d " + "errno=%d.", + acquire_fence_.Get(), ret, ret < 0 ? errno : 0); + if (ret == 0) { + // The fence is completed, so to avoid further calls to sync_wait we close + // it here. + acquire_fence_.Close(); + } + return ret == 0; + } else { + return true; + } +} + +LocalHandle AcquiredBuffer::ClaimAcquireFence() { + return std::move(acquire_fence_); +} + +std::shared_ptr<BufferConsumer> AcquiredBuffer::ClaimBuffer() { + return std::move(buffer_); +} + +int AcquiredBuffer::Release(LocalHandle release_fence) { + if (buffer_) { + // Close the release fence since we can't transfer it with an async release. + release_fence.Close(); + const int ret = buffer_->ReleaseAsync(); + if (ret < 0) { + ALOGE("AcquiredBuffer::Release: Failed to release buffer %d: %s", + buffer_->id(), strerror(-ret)); + if (ret != -ESHUTDOWN) + return ret; + } + + buffer_ = nullptr; + acquire_fence_.Close(); + } + + return 0; +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libvrflinger/acquired_buffer.h b/libs/vr/libvrflinger/acquired_buffer.h new file mode 100644 index 0000000000..dd4fcc5e73 --- /dev/null +++ b/libs/vr/libvrflinger/acquired_buffer.h @@ -0,0 +1,82 @@ +#ifndef ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_ +#define ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_ + +#include <pdx/file_handle.h> +#include <private/dvr/buffer_hub_client.h> + +#include <memory> + +namespace android { +namespace dvr { + +// Manages the ACQUIRE/RELEASE ownership cycle of a BufferConsumer. +class AcquiredBuffer { + public: + static constexpr int kEmptyFence = pdx::LocalHandle::kEmptyFileHandle; + + AcquiredBuffer() : buffer_(nullptr), acquire_fence_(kEmptyFence) {} + + // Constructs an AcquiredBuffer from a BufferConsumer pointer and an acquire + // fence. The BufferConsumer MUST be in the ACQUIRED state prior to calling + // this constructor; the constructor does not attempt to ACQUIRE the buffer + // itself. + AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer, + pdx::LocalHandle acquire_fence); + + // Constructs an AcquiredBuffer from a BufferConsumer. The BufferConsumer MUST + // be in the POSTED state prior to calling this constructor, as this + // constructor attempts to ACQUIRE the buffer. If ACQUIRING the buffer fails + // this instance is left in the empty state. An optional error code is + // returned in |error|, which may be nullptr if not needed. + AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer, int* error); + + // Move constructor. Behaves similarly to the move assignment operator below. + AcquiredBuffer(AcquiredBuffer&& other); + + ~AcquiredBuffer(); + + // Move assignment operator. Moves the BufferConsumer and acquire fence from + // |other| into this instance after RELEASING the current BufferConsumer and + // closing the acquire fence. After the move |other| is left in the empty + // state. + AcquiredBuffer& operator=(AcquiredBuffer&& other); + + // Accessors for the underlying BufferConsumer, the acquire fence, and the + // use-case specific sequence value from the acquisition (see + // private/dvr/buffer_hub_client.h). + std::shared_ptr<BufferConsumer> buffer() const { return buffer_; } + int acquire_fence() const { return acquire_fence_.Get(); } + + // When non-empty, returns true if the acquired fence was signaled (or if the + // fence is empty). Returns false when empty or if the fence is not signaled. + bool IsAvailable() const; + + bool IsEmpty() const { return buffer_ == nullptr; } + + // Returns the acquire fence, passing ownership to the caller. + pdx::LocalHandle ClaimAcquireFence(); + + // Returns the buffer, passing ownership to the caller. Caller is responsible + // for calling Release on the returned buffer. + std::shared_ptr<BufferConsumer> ClaimBuffer(); + + // Releases the BufferConsumer, passing the release fence in |release_fence| + // to the producer. On success, the BufferConsumer and acquire fence are set + // to empty state; if release fails, the BufferConsumer and acquire fence are + // left in place and a negative error code is returned. + int Release(pdx::LocalHandle release_fence); + + private: + AcquiredBuffer(const AcquiredBuffer&) = delete; + void operator=(const AcquiredBuffer&) = delete; + + std::shared_ptr<BufferConsumer> buffer_; + // Mutable so that the fence can be closed when it is determined to be + // signaled during IsAvailable(). + mutable pdx::LocalHandle acquire_fence_; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_ diff --git a/libs/vr/libvrflinger/display_manager_service.cpp b/libs/vr/libvrflinger/display_manager_service.cpp new file mode 100644 index 0000000000..a0b3efea7e --- /dev/null +++ b/libs/vr/libvrflinger/display_manager_service.cpp @@ -0,0 +1,155 @@ +#include "display_manager_service.h" + +#include <pdx/channel_handle.h> +#include <pdx/default_transport/service_endpoint.h> +#include <private/android_filesystem_config.h> +#include <private/dvr/display_protocol.h> +#include <private/dvr/trusted_uids.h> +#include <sys/poll.h> + +#include <array> + +using android::dvr::display::DisplayManagerProtocol; +using android::pdx::Channel; +using android::pdx::LocalChannelHandle; +using android::pdx::Message; +using android::pdx::default_transport::Endpoint; +using android::pdx::ErrorStatus; +using android::pdx::rpc::DispatchRemoteMethod; +using android::pdx::rpc::IfAnyOf; +using android::pdx::rpc::RemoteMethodError; + +namespace android { +namespace dvr { + +void DisplayManager::SetNotificationsPending(bool pending) { + auto status = service_->ModifyChannelEvents(channel_id_, pending ? 0 : POLLIN, + pending ? POLLIN : 0); + ALOGE_IF(!status, + "DisplayManager::SetNotificationPending: Failed to modify channel " + "events: %s", + status.GetErrorMessage().c_str()); +} + +DisplayManagerService::DisplayManagerService( + const std::shared_ptr<DisplayService>& display_service) + : BASE("DisplayManagerService", + Endpoint::Create(DisplayManagerProtocol::kClientPath)), + display_service_(display_service) { + display_service_->SetDisplayConfigurationUpdateNotifier( + std::bind(&DisplayManagerService::OnDisplaySurfaceChange, this)); +} + +std::shared_ptr<pdx::Channel> DisplayManagerService::OnChannelOpen( + pdx::Message& message) { + const int user_id = message.GetEffectiveUserId(); + const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id); + + // Prevent more than one display manager from registering at a time or + // untrusted UIDs from connecting. + if (display_manager_ || !trusted) { + RemoteMethodError(message, EPERM); + return nullptr; + } + + display_manager_ = + std::make_shared<DisplayManager>(this, message.GetChannelId()); + return display_manager_; +} + +void DisplayManagerService::OnChannelClose( + pdx::Message& /*message*/, const std::shared_ptr<pdx::Channel>& channel) { + // Unregister the display manager when the channel closes. + if (display_manager_ == channel) + display_manager_ = nullptr; +} + +pdx::Status<void> DisplayManagerService::HandleMessage(pdx::Message& message) { + auto channel = std::static_pointer_cast<DisplayManager>(message.GetChannel()); + + switch (message.GetOp()) { + case DisplayManagerProtocol::GetSurfaceState::Opcode: + DispatchRemoteMethod<DisplayManagerProtocol::GetSurfaceState>( + *this, &DisplayManagerService::OnGetSurfaceState, message); + return {}; + + case DisplayManagerProtocol::GetSurfaceQueue::Opcode: + DispatchRemoteMethod<DisplayManagerProtocol::GetSurfaceQueue>( + *this, &DisplayManagerService::OnGetSurfaceQueue, message); + return {}; + + case DisplayManagerProtocol::SetupNamedBuffer::Opcode: + DispatchRemoteMethod<DisplayManagerProtocol::SetupNamedBuffer>( + *this, &DisplayManagerService::OnSetupNamedBuffer, message); + return {}; + + default: + return Service::DefaultHandleMessage(message); + } +} + +pdx::Status<std::vector<display::SurfaceState>> +DisplayManagerService::OnGetSurfaceState(pdx::Message& /*message*/) { + std::vector<display::SurfaceState> items; + + display_service_->ForEachDisplaySurface( + SurfaceType::Application, + [&items](const std::shared_ptr<DisplaySurface>& surface) mutable { + items.push_back({surface->surface_id(), surface->process_id(), + surface->user_id(), surface->attributes(), + surface->update_flags(), surface->GetQueueIds()}); + surface->ClearUpdate(); + }); + + // The fact that we're in the message handler implies that display_manager_ is + // not nullptr. No check required, unless this service becomes multi-threaded. + display_manager_->SetNotificationsPending(false); + return items; +} + +pdx::Status<pdx::LocalChannelHandle> DisplayManagerService::OnGetSurfaceQueue( + pdx::Message& /*message*/, int surface_id, int queue_id) { + auto surface = display_service_->GetDisplaySurface(surface_id); + if (!surface || surface->surface_type() != SurfaceType::Application) + return ErrorStatus(EINVAL); + + auto queue = + std::static_pointer_cast<ApplicationDisplaySurface>(surface)->GetQueue( + queue_id); + if (!queue) + return ErrorStatus(EINVAL); + + auto status = queue->CreateConsumerQueueHandle(); + ALOGE_IF( + !status, + "DisplayManagerService::OnGetSurfaceQueue: Failed to create consumer " + "queue for queue_id=%d: %s", + queue->id(), status.GetErrorMessage().c_str()); + + return status; +} + +pdx::Status<BorrowedNativeBufferHandle> +DisplayManagerService::OnSetupNamedBuffer(pdx::Message& message, + const std::string& name, size_t size, + uint64_t usage) { + const int user_id = message.GetEffectiveUserId(); + const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id); + + if (!trusted) { + ALOGE( + "DisplayService::SetupNamedBuffer: Named buffers may only be created " + "by trusted UIDs: user_id=%d", + user_id); + return ErrorStatus(EPERM); + } + return display_service_->SetupNamedBuffer(name, size, usage); +} + +void DisplayManagerService::OnDisplaySurfaceChange() { + if (display_manager_) + display_manager_->SetNotificationsPending(true); +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libvrflinger/display_manager_service.h b/libs/vr/libvrflinger/display_manager_service.h new file mode 100644 index 0000000000..0857eb5298 --- /dev/null +++ b/libs/vr/libvrflinger/display_manager_service.h @@ -0,0 +1,77 @@ +#ifndef ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_ +#define ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_ + +#include <pdx/service.h> +#include <pdx/status.h> +#include <private/dvr/display_protocol.h> + +#include "display_service.h" + +namespace android { +namespace dvr { + +class DisplayManagerService; + +// The display manager is a client of the display manager service. This class +// represents the connected client that the display manager service sends +// notifications to. +class DisplayManager : public pdx::Channel { + public: + DisplayManager(DisplayManagerService* service, int channel_id) + : service_(service), channel_id_(channel_id) {} + + int channel_id() const { return channel_id_; } + + // Sets or clears the channel event mask to indicate pending events that the + // display manager on the other end of the channel should read and handle. + // When |pending| is true the POLLIN bit is set in the event mask; when + // |pending| is false the POLLIN bit is cleared in the event mask. + void SetNotificationsPending(bool pending); + + private: + DisplayManager(const DisplayManager&) = delete; + void operator=(const DisplayManager&) = delete; + + DisplayManagerService* service_; + int channel_id_; +}; + +// The display manager service marshalls state and events from the display +// service to the display manager. +class DisplayManagerService : public pdx::ServiceBase<DisplayManagerService> { + public: + std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override; + void OnChannelClose(pdx::Message& message, + const std::shared_ptr<pdx::Channel>& channel) override; + pdx::Status<void> HandleMessage(pdx::Message& message) override; + + private: + friend BASE; + + explicit DisplayManagerService( + const std::shared_ptr<DisplayService>& display_service); + + pdx::Status<std::vector<display::SurfaceState>> OnGetSurfaceState( + pdx::Message& message); + pdx::Status<pdx::LocalChannelHandle> OnGetSurfaceQueue(pdx::Message& message, + int surface_id, + int queue_id); + pdx::Status<BorrowedNativeBufferHandle> OnSetupNamedBuffer( + pdx::Message& message, const std::string& name, size_t size, + uint64_t usage); + + // Called by the display service to indicate changes to display surfaces that + // the display manager should evaluate. + void OnDisplaySurfaceChange(); + + DisplayManagerService(const DisplayManagerService&) = delete; + void operator=(const DisplayManagerService&) = delete; + + std::shared_ptr<DisplayService> display_service_; + std::shared_ptr<DisplayManager> display_manager_; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_ diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp new file mode 100644 index 0000000000..47efa7653d --- /dev/null +++ b/libs/vr/libvrflinger/display_service.cpp @@ -0,0 +1,265 @@ +#include "display_service.h" + +#include <unistd.h> +#include <vector> + +#include <dvr/dvr_display_types.h> +#include <pdx/default_transport/service_endpoint.h> +#include <pdx/rpc/remote_method.h> +#include <private/dvr/display_protocol.h> +#include <private/dvr/numeric.h> +#include <private/dvr/types.h> + +using android::dvr::display::DisplayProtocol; +using android::pdx::Channel; +using android::pdx::ErrorStatus; +using android::pdx::Message; +using android::pdx::Status; +using android::pdx::default_transport::Endpoint; +using android::pdx::rpc::DispatchRemoteMethod; + +namespace android { +namespace dvr { + +DisplayService::DisplayService(Hwc2::Composer* hidl, + RequestDisplayCallback request_display_callback) + : BASE("DisplayService", + Endpoint::Create(display::DisplayProtocol::kClientPath)), + hardware_composer_(hidl, request_display_callback), + request_display_callback_(request_display_callback) { + hardware_composer_.Initialize(); +} + +bool DisplayService::IsInitialized() const { + return BASE::IsInitialized() && hardware_composer_.IsInitialized(); +} + +std::string DisplayService::DumpState(size_t /*max_length*/) { + return hardware_composer_.Dump(); +} + +void DisplayService::OnChannelClose(pdx::Message& message, + const std::shared_ptr<Channel>& channel) { + if (auto surface = std::static_pointer_cast<DisplaySurface>(channel)) { + surface->OnSetAttributes(message, + {{display::SurfaceAttribute::Visible, + display::SurfaceAttributeValue{false}}}); + SurfaceUpdated(surface->surface_type(), + display::SurfaceUpdateFlags::VisibilityChanged); + } +} + +// First-level dispatch for display service messages. Directly handles messages +// that are independent of the display surface (metrics, creation) and routes +// surface-specific messages to the per-instance handlers. +Status<void> DisplayService::HandleMessage(pdx::Message& message) { + ALOGD_IF(TRACE, "DisplayService::HandleMessage: opcode=%d", message.GetOp()); + switch (message.GetOp()) { + case DisplayProtocol::GetMetrics::Opcode: + DispatchRemoteMethod<DisplayProtocol::GetMetrics>( + *this, &DisplayService::OnGetMetrics, message); + return {}; + + case DisplayProtocol::CreateSurface::Opcode: + DispatchRemoteMethod<DisplayProtocol::CreateSurface>( + *this, &DisplayService::OnCreateSurface, message); + return {}; + + case DisplayProtocol::GetNamedBuffer::Opcode: + DispatchRemoteMethod<DisplayProtocol::GetNamedBuffer>( + *this, &DisplayService::OnGetNamedBuffer, message); + return {}; + + case DisplayProtocol::IsVrAppRunning::Opcode: + DispatchRemoteMethod<DisplayProtocol::IsVrAppRunning>( + *this, &DisplayService::IsVrAppRunning, message); + return {}; + + // Direct the surface specific messages to the surface instance. + case DisplayProtocol::SetAttributes::Opcode: + case DisplayProtocol::CreateQueue::Opcode: + case DisplayProtocol::GetSurfaceInfo::Opcode: + return HandleSurfaceMessage(message); + + default: + return Service::HandleMessage(message); + } +} + +Status<display::Metrics> DisplayService::OnGetMetrics( + pdx::Message& /*message*/) { + return {{static_cast<uint32_t>(GetDisplayMetrics().width), + static_cast<uint32_t>(GetDisplayMetrics().height), + static_cast<uint32_t>(GetDisplayMetrics().dpi.x), + static_cast<uint32_t>(GetDisplayMetrics().dpi.y), + static_cast<uint32_t>( + hardware_composer_.native_display_metrics().vsync_period_ns), + 0, + 0, + 0, + 0.0, + {}, + {}}}; +} + +// Creates a new DisplaySurface and associates it with this channel. This may +// only be done once per channel. +Status<display::SurfaceInfo> DisplayService::OnCreateSurface( + pdx::Message& message, const display::SurfaceAttributes& attributes) { + // A surface may only be created once per channel. + if (message.GetChannel()) + return ErrorStatus(EINVAL); + + ALOGI_IF(TRACE, "DisplayService::OnCreateSurface: cid=%d", + message.GetChannelId()); + + // Use the channel id as the unique surface id. + const int surface_id = message.GetChannelId(); + const int process_id = message.GetProcessId(); + const int user_id = message.GetEffectiveUserId(); + + ALOGI_IF(TRACE, + "DisplayService::OnCreateSurface: surface_id=%d process_id=%d", + surface_id, process_id); + + auto surface_status = + DisplaySurface::Create(this, surface_id, process_id, user_id, attributes); + if (!surface_status) { + ALOGE("DisplayService::OnCreateSurface: Failed to create surface: %s", + surface_status.GetErrorMessage().c_str()); + return ErrorStatus(surface_status.error()); + } + + SurfaceType surface_type = surface_status.get()->surface_type(); + display::SurfaceUpdateFlags update_flags = + surface_status.get()->update_flags(); + display::SurfaceInfo surface_info{surface_status.get()->surface_id(), + surface_status.get()->visible(), + surface_status.get()->z_order()}; + + message.SetChannel(surface_status.take()); + + SurfaceUpdated(surface_type, update_flags); + return {surface_info}; +} + +void DisplayService::SurfaceUpdated(SurfaceType surface_type, + display::SurfaceUpdateFlags update_flags) { + ALOGD_IF(TRACE, "DisplayService::SurfaceUpdated: update_flags=%x", + update_flags.value()); + if (update_flags.value() != 0) { + if (surface_type == SurfaceType::Application) + NotifyDisplayConfigurationUpdate(); + else + UpdateActiveDisplaySurfaces(); + } +} + +pdx::Status<BorrowedNativeBufferHandle> DisplayService::OnGetNamedBuffer( + pdx::Message& /* message */, const std::string& name) { + ALOGD_IF(TRACE, "displayService::OnGetNamedBuffer: name=%s", name.c_str()); + auto named_buffer = named_buffers_.find(name); + if (named_buffer != named_buffers_.end()) + return {BorrowedNativeBufferHandle(*named_buffer->second, 0)}; + else + return pdx::ErrorStatus(EINVAL); +} + +// Calls the message handler for the DisplaySurface associated with this +// channel. +Status<void> DisplayService::HandleSurfaceMessage(pdx::Message& message) { + auto surface = std::static_pointer_cast<DisplaySurface>(message.GetChannel()); + ALOGW_IF(!surface, + "DisplayService::HandleSurfaceMessage: surface is nullptr!"); + + if (surface) + return surface->HandleMessage(message); + else + return ErrorStatus(EINVAL); +} + +std::shared_ptr<DisplaySurface> DisplayService::GetDisplaySurface( + int surface_id) const { + return std::static_pointer_cast<DisplaySurface>(GetChannel(surface_id)); +} + +std::vector<std::shared_ptr<DisplaySurface>> +DisplayService::GetDisplaySurfaces() const { + return GetChannels<DisplaySurface>(); +} + +std::vector<std::shared_ptr<DirectDisplaySurface>> +DisplayService::GetVisibleDisplaySurfaces() const { + std::vector<std::shared_ptr<DirectDisplaySurface>> visible_surfaces; + + ForEachDisplaySurface( + SurfaceType::Direct, + [&](const std::shared_ptr<DisplaySurface>& surface) mutable { + if (surface->visible()) { + visible_surfaces.push_back( + std::static_pointer_cast<DirectDisplaySurface>(surface)); + surface->ClearUpdate(); + } + }); + + return visible_surfaces; +} + +void DisplayService::UpdateActiveDisplaySurfaces() { + auto visible_surfaces = GetVisibleDisplaySurfaces(); + + std::sort(visible_surfaces.begin(), visible_surfaces.end(), + [](const std::shared_ptr<DisplaySurface>& a, + const std::shared_ptr<DisplaySurface>& b) { + return a->z_order() < b->z_order(); + }); + + ALOGD_IF(TRACE, + "DisplayService::UpdateActiveDisplaySurfaces: %zd visible surfaces", + visible_surfaces.size()); + + hardware_composer_.SetDisplaySurfaces(std::move(visible_surfaces)); +} + +pdx::Status<BorrowedNativeBufferHandle> DisplayService::SetupNamedBuffer( + const std::string& name, size_t size, uint64_t usage) { + auto named_buffer = named_buffers_.find(name); + if (named_buffer == named_buffers_.end()) { + auto ion_buffer = std::make_unique<IonBuffer>(static_cast<int>(size), 1, + HAL_PIXEL_FORMAT_BLOB, usage); + named_buffer = + named_buffers_.insert(std::make_pair(name, std::move(ion_buffer))) + .first; + } + + return {BorrowedNativeBufferHandle(*named_buffer->second, 0)}; +} + +void DisplayService::OnHardwareComposerRefresh() { + hardware_composer_.OnHardwareComposerRefresh(); +} + +void DisplayService::SetDisplayConfigurationUpdateNotifier( + DisplayConfigurationUpdateNotifier update_notifier) { + update_notifier_ = update_notifier; +} + +void DisplayService::NotifyDisplayConfigurationUpdate() { + if (update_notifier_) + update_notifier_(); +} + +Status<bool> DisplayService::IsVrAppRunning(pdx::Message& /*message*/) { + bool visible = false; + ForEachDisplaySurface( + SurfaceType::Application, + [&visible](const std::shared_ptr<DisplaySurface>& surface) { + if (surface->visible()) + visible = true; + }); + + return {visible}; +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h new file mode 100644 index 0000000000..bb4eeef1c6 --- /dev/null +++ b/libs/vr/libvrflinger/display_service.h @@ -0,0 +1,125 @@ +#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_ +#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_ + +#include <pdx/service.h> +#include <pdx/status.h> +#include <private/dvr/buffer_hub_client.h> +#include <private/dvr/bufferhub_rpc.h> +#include <private/dvr/display_protocol.h> + +#include <functional> +#include <iterator> +#include <memory> +#include <string> +#include <vector> + +#include "acquired_buffer.h" +#include "display_surface.h" +#include "epoll_event_dispatcher.h" +#include "hardware_composer.h" + +namespace android { +namespace dvr { + +// DisplayService implements the display service component of VrFlinger. +class DisplayService : public pdx::ServiceBase<DisplayService> { + public: + bool IsInitialized() const override; + std::string DumpState(size_t max_length) override; + + void OnChannelClose(pdx::Message& message, + const std::shared_ptr<pdx::Channel>& channel) override; + pdx::Status<void> HandleMessage(pdx::Message& message) override; + + std::shared_ptr<DisplaySurface> GetDisplaySurface(int surface_id) const; + std::vector<std::shared_ptr<DisplaySurface>> GetDisplaySurfaces() const; + std::vector<std::shared_ptr<DirectDisplaySurface>> GetVisibleDisplaySurfaces() + const; + + // Updates the list of actively displayed surfaces. This must be called after + // any change to client/manager attributes that affect visibility or z order. + void UpdateActiveDisplaySurfaces(); + + pdx::Status<BorrowedNativeBufferHandle> SetupNamedBuffer( + const std::string& name, size_t size, uint64_t usage); + + template <class A> + void ForEachDisplaySurface(SurfaceType surface_type, A action) const { + ForEachChannel([surface_type, + action](const ChannelIterator::value_type& pair) mutable { + auto surface = std::static_pointer_cast<DisplaySurface>(pair.second); + if (surface->surface_type() == surface_type) + action(surface); + }); + } + + using DisplayConfigurationUpdateNotifier = std::function<void(void)>; + void SetDisplayConfigurationUpdateNotifier( + DisplayConfigurationUpdateNotifier notifier); + + using VSyncCallback = HardwareComposer::VSyncCallback; + void SetVSyncCallback(VSyncCallback callback) { + hardware_composer_.SetVSyncCallback(callback); + } + + HWCDisplayMetrics GetDisplayMetrics() { + return hardware_composer_.display_metrics(); + } + + void GrantDisplayOwnership() { hardware_composer_.Enable(); } + void SeizeDisplayOwnership() { hardware_composer_.Disable(); } + + void OnHardwareComposerRefresh(); + + private: + friend BASE; + friend DisplaySurface; + + friend class VrDisplayStateService; + + using RequestDisplayCallback = std::function<void(bool)>; + + DisplayService(android::Hwc2::Composer* hidl, + RequestDisplayCallback request_display_callback); + + pdx::Status<BorrowedNativeBufferHandle> OnGetNamedBuffer( + pdx::Message& message, const std::string& name); + pdx::Status<display::Metrics> OnGetMetrics(pdx::Message& message); + pdx::Status<display::SurfaceInfo> OnCreateSurface( + pdx::Message& message, const display::SurfaceAttributes& attributes); + + // Temporary query for current VR status. Will be removed later. + pdx::Status<bool> IsVrAppRunning(pdx::Message& message); + + pdx::Status<void> AddEventHandler(int fd, int events, + EpollEventDispatcher::Handler handler) { + return dispatcher_.AddEventHandler(fd, events, handler); + } + pdx::Status<void> RemoveEventHandler(int fd) { + return dispatcher_.RemoveEventHandler(fd); + } + + void SurfaceUpdated(SurfaceType surface_type, + display::SurfaceUpdateFlags update_flags); + + // Called by DisplaySurface to signal that a surface property has changed and + // the display manager should be notified. + void NotifyDisplayConfigurationUpdate(); + + pdx::Status<void> HandleSurfaceMessage(pdx::Message& message); + + HardwareComposer hardware_composer_; + RequestDisplayCallback request_display_callback_; + EpollEventDispatcher dispatcher_; + DisplayConfigurationUpdateNotifier update_notifier_; + + std::unordered_map<std::string, std::unique_ptr<IonBuffer>> named_buffers_; + + DisplayService(const DisplayService&) = delete; + void operator=(const DisplayService&) = delete; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_ diff --git a/libs/vr/libvrflinger/display_surface.cpp b/libs/vr/libvrflinger/display_surface.cpp new file mode 100644 index 0000000000..fb2751b77d --- /dev/null +++ b/libs/vr/libvrflinger/display_surface.cpp @@ -0,0 +1,457 @@ +#include "display_surface.h" + +#include <private/android_filesystem_config.h> +#include <utils/Trace.h> + +#include <private/dvr/trusted_uids.h> + +#include "display_service.h" +#include "hardware_composer.h" + +#define LOCAL_TRACE 1 + +using android::dvr::display::DisplayProtocol; +using android::pdx::BorrowedChannelHandle; +using android::pdx::ErrorStatus; +using android::pdx::LocalChannelHandle; +using android::pdx::LocalHandle; +using android::pdx::Message; +using android::pdx::RemoteChannelHandle; +using android::pdx::Status; +using android::pdx::rpc::DispatchRemoteMethod; +using android::pdx::rpc::IfAnyOf; + +namespace android { +namespace dvr { + +DisplaySurface::DisplaySurface(DisplayService* service, + SurfaceType surface_type, int surface_id, + int process_id, int user_id, + const display::SurfaceAttributes& attributes) + : service_(service), + surface_type_(surface_type), + surface_id_(surface_id), + process_id_(process_id), + user_id_(user_id), + attributes_(attributes), + update_flags_(display::SurfaceUpdateFlags::NewSurface) {} + +DisplaySurface::~DisplaySurface() { + ALOGD_IF(LOCAL_TRACE, + "DisplaySurface::~DisplaySurface: surface_id=%d process_id=%d", + surface_id(), process_id()); +} + +Status<void> DisplaySurface::HandleMessage(pdx::Message& message) { + switch (message.GetOp()) { + case DisplayProtocol::SetAttributes::Opcode: + DispatchRemoteMethod<DisplayProtocol::SetAttributes>( + *this, &DisplaySurface::OnSetAttributes, message); + break; + + case DisplayProtocol::GetSurfaceInfo::Opcode: + DispatchRemoteMethod<DisplayProtocol::GetSurfaceInfo>( + *this, &DisplaySurface::OnGetSurfaceInfo, message); + break; + + case DisplayProtocol::CreateQueue::Opcode: + DispatchRemoteMethod<DisplayProtocol::CreateQueue>( + *this, &DisplaySurface::OnCreateQueue, message); + break; + } + + return {}; +} + +Status<void> DisplaySurface::OnSetAttributes( + pdx::Message& /*message*/, const display::SurfaceAttributes& attributes) { + display::SurfaceUpdateFlags update_flags; + + for (const auto& attribute : attributes) { + const auto& key = attribute.first; + const auto* variant = &attribute.second; + bool invalid_value = false; + bool visibility_changed = false; + + // Catch attributes that have significance to the display service. + switch (key) { + case display::SurfaceAttribute::ZOrder: + invalid_value = !IfAnyOf<int32_t, int64_t, float>::Call( + variant, [&](const auto& value) { + if (z_order_ != value) { + visibility_changed = true; + z_order_ = value; + } + }); + break; + case display::SurfaceAttribute::Visible: + invalid_value = !IfAnyOf<int32_t, int64_t, bool>::Call( + variant, [&](const auto& value) { + if (visible_ != value) { + visibility_changed = true; + visible_ = value; + } + }); + break; + } + + if (invalid_value) { + ALOGW( + "DisplaySurface::OnClientSetAttributes: Failed to set display " + "surface attribute '%d' because of incompatible type: %d", + key, variant->index()); + } else { + // Only update the attribute map with valid values. + attributes_[attribute.first] = attribute.second; + + // All attribute changes generate a notification, even if the value + // doesn't change. Visibility attributes set a flag only if the value + // changes. + update_flags.Set(display::SurfaceUpdateFlags::AttributesChanged); + if (visibility_changed) + update_flags.Set(display::SurfaceUpdateFlags::VisibilityChanged); + } + } + + SurfaceUpdated(update_flags); + return {}; +} + +void DisplaySurface::SurfaceUpdated(display::SurfaceUpdateFlags update_flags) { + ALOGD_IF(TRACE, + "DisplaySurface::SurfaceUpdated: surface_id=%d update_flags=0x%x", + surface_id(), update_flags.value()); + + update_flags_.Set(update_flags); + service()->SurfaceUpdated(surface_type(), update_flags_); +} + +void DisplaySurface::ClearUpdate() { + ALOGD_IF(TRACE, "DisplaySurface::ClearUpdate: surface_id=%d", surface_id()); + update_flags_ = display::SurfaceUpdateFlags::None; +} + +Status<display::SurfaceInfo> DisplaySurface::OnGetSurfaceInfo( + Message& /*message*/) { + ALOGD_IF( + TRACE, + "DisplaySurface::OnGetSurfaceInfo: surface_id=%d visible=%d z_order=%d", + surface_id(), visible(), z_order()); + return {{surface_id(), visible(), z_order()}}; +} + +Status<void> DisplaySurface::RegisterQueue( + const std::shared_ptr<ConsumerQueue>& consumer_queue) { + ALOGD_IF(TRACE, "DisplaySurface::RegisterQueue: surface_id=%d queue_id=%d", + surface_id(), consumer_queue->id()); + // Capture references for the lambda to work around apparent clang bug. + // TODO(eieio): Figure out if there is a clang bug or C++11 ambiguity when + // capturing self and consumer_queue by copy in the following case: + // auto self = Self(); + // [self, consumer_queue](int events) { + // self->OnQueueEvent(consuemr_queue, events); } + // + struct State { + std::shared_ptr<DisplaySurface> surface; + std::shared_ptr<ConsumerQueue> queue; + }; + State state{Self(), consumer_queue}; + + return service()->AddEventHandler( + consumer_queue->queue_fd(), EPOLLIN | EPOLLHUP | EPOLLET, + [state](int events) { + state.surface->OnQueueEvent(state.queue, events); + }); +} + +Status<void> DisplaySurface::UnregisterQueue( + const std::shared_ptr<ConsumerQueue>& consumer_queue) { + ALOGD_IF(TRACE, "DisplaySurface::UnregisterQueue: surface_id=%d queue_id=%d", + surface_id(), consumer_queue->id()); + return service()->RemoveEventHandler(consumer_queue->queue_fd()); +} + +void DisplaySurface::OnQueueEvent( + const std::shared_ptr<ConsumerQueue>& /*consumer_queue*/, int /*events*/) { + ALOGE( + "DisplaySurface::OnQueueEvent: ERROR base virtual method should not be " + "called!!!"); +} + +std::shared_ptr<ConsumerQueue> ApplicationDisplaySurface::GetQueue( + int32_t queue_id) { + ALOGD_IF(TRACE, + "ApplicationDisplaySurface::GetQueue: surface_id=%d queue_id=%d", + surface_id(), queue_id); + + auto search = consumer_queues_.find(queue_id); + if (search != consumer_queues_.end()) + return search->second; + else + return nullptr; +} + +std::vector<int32_t> ApplicationDisplaySurface::GetQueueIds() const { + std::vector<int32_t> queue_ids; + for (const auto& entry : consumer_queues_) + queue_ids.push_back(entry.first); + return queue_ids; +} + +Status<LocalChannelHandle> ApplicationDisplaySurface::OnCreateQueue( + Message& /*message*/, size_t meta_size_bytes) { + ATRACE_NAME("ApplicationDisplaySurface::OnCreateQueue"); + ALOGD_IF(TRACE, + "ApplicationDisplaySurface::OnCreateQueue: surface_id=%d, " + "meta_size_bytes=%zu", + surface_id(), meta_size_bytes); + + std::lock_guard<std::mutex> autolock(lock_); + auto producer = ProducerQueue::Create(meta_size_bytes); + if (!producer) { + ALOGE( + "ApplicationDisplaySurface::OnCreateQueue: Failed to create producer " + "queue!"); + return ErrorStatus(ENOMEM); + } + + std::shared_ptr<ConsumerQueue> consumer = + producer->CreateSilentConsumerQueue(); + auto status = RegisterQueue(consumer); + if (!status) { + ALOGE( + "ApplicationDisplaySurface::OnCreateQueue: Failed to register consumer " + "queue: %s", + status.GetErrorMessage().c_str()); + return status.error_status(); + } + + consumer_queues_[consumer->id()] = std::move(consumer); + + SurfaceUpdated(display::SurfaceUpdateFlags::BuffersChanged); + return std::move(producer->GetChannelHandle()); +} + +void ApplicationDisplaySurface::OnQueueEvent( + const std::shared_ptr<ConsumerQueue>& consumer_queue, int events) { + ALOGD_IF(TRACE, + "ApplicationDisplaySurface::OnQueueEvent: queue_id=%d events=%x", + consumer_queue->id(), events); + + // Always give the queue a chance to handle its internal bookkeeping. + consumer_queue->HandleQueueEvents(); + + // Check for hangup and remove a queue that is no longer needed. + std::lock_guard<std::mutex> autolock(lock_); + if (consumer_queue->hung_up()) { + ALOGD_IF(TRACE, "ApplicationDisplaySurface::OnQueueEvent: Removing queue."); + UnregisterQueue(consumer_queue); + auto search = consumer_queues_.find(consumer_queue->id()); + if (search != consumer_queues_.end()) { + consumer_queues_.erase(search); + } else { + ALOGE( + "ApplicationDisplaySurface::OnQueueEvent: Failed to find queue_id=%d", + consumer_queue->id()); + } + SurfaceUpdated(display::SurfaceUpdateFlags::BuffersChanged); + } +} + +Status<LocalChannelHandle> DirectDisplaySurface::OnCreateQueue( + Message& /*message*/, size_t meta_size_bytes) { + ATRACE_NAME("DirectDisplaySurface::OnCreateQueue"); + ALOGD_IF( + TRACE, + "DirectDisplaySurface::OnCreateQueue: surface_id=%d meta_size_bytes=%zu", + surface_id(), meta_size_bytes); + + std::lock_guard<std::mutex> autolock(lock_); + if (!direct_queue_) { + auto producer = ProducerQueue::Create(meta_size_bytes); + if (!producer) { + ALOGE( + "DirectDisplaySurface::OnCreateQueue: Failed to create producer " + "queue!"); + return ErrorStatus(ENOMEM); + } + + direct_queue_ = producer->CreateConsumerQueue(); + auto status = RegisterQueue(direct_queue_); + if (!status) { + ALOGE( + "DirectDisplaySurface::OnCreateQueue: Failed to register consumer " + "queue: %s", + status.GetErrorMessage().c_str()); + return status.error_status(); + } + + return std::move(producer->GetChannelHandle()); + } else { + return ErrorStatus(EALREADY); + } +} + +void DirectDisplaySurface::OnQueueEvent( + const std::shared_ptr<ConsumerQueue>& consumer_queue, int events) { + ALOGD_IF(TRACE, "DirectDisplaySurface::OnQueueEvent: queue_id=%d events=%x", + consumer_queue->id(), events); + + // Always give the queue a chance to handle its internal bookkeeping. + consumer_queue->HandleQueueEvents(); + + // Check for hangup and remove a queue that is no longer needed. + std::lock_guard<std::mutex> autolock(lock_); + if (consumer_queue->hung_up()) { + ALOGD_IF(TRACE, "DirectDisplaySurface::OnQueueEvent: Removing queue."); + UnregisterQueue(consumer_queue); + direct_queue_ = nullptr; + } +} + +void DirectDisplaySurface::DequeueBuffersLocked() { + if (direct_queue_ == nullptr) { + ALOGE( + "DirectDisplaySurface::DequeueBuffersLocked: Consumer queue is not " + "initialized."); + return; + } + + while (true) { + LocalHandle acquire_fence; + size_t slot; + auto buffer_status = direct_queue_->Dequeue(0, &slot, &acquire_fence); + if (!buffer_status) { + ALOGD_IF( + TRACE && buffer_status.error() == ETIMEDOUT, + "DirectDisplaySurface::DequeueBuffersLocked: All buffers dequeued."); + ALOGE_IF(buffer_status.error() != ETIMEDOUT, + "DirectDisplaySurface::DequeueBuffersLocked: Failed to dequeue " + "buffer: %s", + buffer_status.GetErrorMessage().c_str()); + return; + } + auto buffer_consumer = buffer_status.take(); + + if (!visible()) { + ATRACE_NAME("DropFrameOnInvisibleSurface"); + ALOGD_IF(TRACE, + "DirectDisplaySurface::DequeueBuffersLocked: Discarding " + "buffer_id=%d on invisible surface.", + buffer_consumer->id()); + buffer_consumer->Discard(); + continue; + } + + if (acquired_buffers_.IsFull()) { + ALOGE( + "DirectDisplaySurface::DequeueBuffersLocked: Posted buffers full, " + "overwriting."); + acquired_buffers_.PopBack(); + } + + acquired_buffers_.Append( + AcquiredBuffer(buffer_consumer, std::move(acquire_fence))); + } +} + +AcquiredBuffer DirectDisplaySurface::AcquireCurrentBuffer() { + std::lock_guard<std::mutex> autolock(lock_); + DequeueBuffersLocked(); + + if (acquired_buffers_.IsEmpty()) { + ALOGE( + "DirectDisplaySurface::AcquireCurrentBuffer: attempt to acquire buffer " + "when none are posted."); + return AcquiredBuffer(); + } + AcquiredBuffer buffer = std::move(acquired_buffers_.Front()); + acquired_buffers_.PopFront(); + ALOGD_IF(TRACE, "DirectDisplaySurface::AcquireCurrentBuffer: buffer: %p", + buffer.buffer().get()); + return buffer; +} + +AcquiredBuffer DirectDisplaySurface::AcquireNewestAvailableBuffer( + AcquiredBuffer* skipped_buffer) { + std::lock_guard<std::mutex> autolock(lock_); + DequeueBuffersLocked(); + + AcquiredBuffer buffer; + int frames = 0; + // Basic latency stopgap for when the application misses a frame: + // If the application recovers on the 2nd or 3rd (etc) frame after + // missing, this code will skip frames to catch up by checking if + // the next frame is also available. + while (!acquired_buffers_.IsEmpty() && + acquired_buffers_.Front().IsAvailable()) { + // Capture the skipped buffer into the result parameter. + // Note that this API only supports skipping one buffer per vsync. + if (frames > 0 && skipped_buffer) + *skipped_buffer = std::move(buffer); + ++frames; + buffer = std::move(acquired_buffers_.Front()); + acquired_buffers_.PopFront(); + if (frames == 2) + break; + } + ALOGD_IF(TRACE, + "DirectDisplaySurface::AcquireNewestAvailableBuffer: buffer: %p", + buffer.buffer().get()); + return buffer; +} + +bool DirectDisplaySurface::IsBufferAvailable() { + std::lock_guard<std::mutex> autolock(lock_); + DequeueBuffersLocked(); + + return !acquired_buffers_.IsEmpty() && + acquired_buffers_.Front().IsAvailable(); +} + +bool DirectDisplaySurface::IsBufferPosted() { + std::lock_guard<std::mutex> autolock(lock_); + DequeueBuffersLocked(); + + return !acquired_buffers_.IsEmpty(); +} + +Status<std::shared_ptr<DisplaySurface>> DisplaySurface::Create( + DisplayService* service, int surface_id, int process_id, int user_id, + const display::SurfaceAttributes& attributes) { + bool direct = false; + auto search = attributes.find(display::SurfaceAttribute::Direct); + if (search != attributes.end()) { + if (!IfAnyOf<int32_t, int64_t, bool, float>::Get(&search->second, + &direct)) { + ALOGE( + "DisplaySurface::Create: Invalid type for SurfaceAttribute::Direct!"); + return ErrorStatus(EINVAL); + } + } + + ALOGD_IF(TRACE, + "DisplaySurface::Create: surface_id=%d process_id=%d user_id=%d " + "direct=%d", + surface_id, process_id, user_id, direct); + + if (direct) { + const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id); + if (trusted) { + return {std::shared_ptr<DisplaySurface>{new DirectDisplaySurface( + service, surface_id, process_id, user_id, attributes)}}; + } else { + ALOGE( + "DisplaySurface::Create: Direct surfaces may only be created by " + "trusted UIDs: user_id=%d", + user_id); + return ErrorStatus(EPERM); + } + } else { + return {std::shared_ptr<DisplaySurface>{new ApplicationDisplaySurface( + service, surface_id, process_id, user_id, attributes)}}; + } +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libvrflinger/display_surface.h b/libs/vr/libvrflinger/display_surface.h new file mode 100644 index 0000000000..c456b1050f --- /dev/null +++ b/libs/vr/libvrflinger/display_surface.h @@ -0,0 +1,185 @@ +#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_ +#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_ + +#include <pdx/file_handle.h> +#include <pdx/service.h> +#include <private/dvr/buffer_hub_queue_client.h> +#include <private/dvr/display_protocol.h> +#include <private/dvr/ring_buffer.h> + +#include <functional> +#include <iterator> +#include <memory> +#include <string> +#include <vector> + +#include "acquired_buffer.h" + +namespace android { +namespace dvr { + +class DisplayService; + +enum class SurfaceType { + Direct, + Application, +}; + +class DisplaySurface : public pdx::Channel { + public: + static pdx::Status<std::shared_ptr<DisplaySurface>> Create( + DisplayService* service, int surface_id, int process_id, int user_id, + const display::SurfaceAttributes& attributes); + + ~DisplaySurface() override; + + DisplayService* service() const { return service_; } + SurfaceType surface_type() const { return surface_type_; } + int surface_id() const { return surface_id_; } + int process_id() const { return process_id_; } + int user_id() const { return user_id_; } + + bool visible() const { return visible_; } + int z_order() const { return z_order_; } + + const display::SurfaceAttributes& attributes() const { return attributes_; } + display::SurfaceUpdateFlags update_flags() const { return update_flags_; } + + virtual std::vector<int32_t> GetQueueIds() const { return {}; } + + bool IsUpdatePending() const { + return update_flags_.value() != display::SurfaceUpdateFlags::None; + } + + protected: + DisplaySurface(DisplayService* service, SurfaceType surface_type, + int surface_id, int process_id, int user_id, + const display::SurfaceAttributes& attributes); + + // Utility to retrieve a shared pointer to this channel as the desired derived + // type. + template < + typename T = DisplaySurface, + typename = std::enable_if_t<std::is_base_of<DisplaySurface, T>::value>> + std::shared_ptr<T> Self() { + return std::static_pointer_cast<T>(shared_from_this()); + } + + virtual pdx::Status<pdx::LocalChannelHandle> OnCreateQueue( + pdx::Message& message, size_t meta_size_bytes) = 0; + + // Registers a consumer queue with the event dispatcher in DisplayService. The + // OnQueueEvent callback below is called to handle queue events. + pdx::Status<void> RegisterQueue( + const std::shared_ptr<ConsumerQueue>& consumer_queue); + pdx::Status<void> UnregisterQueue( + const std::shared_ptr<ConsumerQueue>& consumer_queue); + + // Called by the event dispatcher in DisplayService when a registered queue + // event triggers. Executes on the event dispatcher thread. + virtual void OnQueueEvent( + const std::shared_ptr<ConsumerQueue>& consumer_queue, int events); + + void SurfaceUpdated(display::SurfaceUpdateFlags update_flags); + void ClearUpdate(); + + // Synchronizes access to mutable state below between message dispatch thread + // and frame post thread. + mutable std::mutex lock_; + + private: + friend class DisplayService; + friend class DisplayManagerService; + + // Dispatches display surface messages to the appropriate handlers. This + // handler runs on the VrFlinger message dispatch thread. + pdx::Status<void> HandleMessage(pdx::Message& message); + + pdx::Status<void> OnSetAttributes( + pdx::Message& message, const display::SurfaceAttributes& attributes); + pdx::Status<display::SurfaceInfo> OnGetSurfaceInfo(pdx::Message& message); + + DisplayService* service_; + SurfaceType surface_type_; + int surface_id_; + int process_id_; + int user_id_; + + display::SurfaceAttributes attributes_; + display::SurfaceUpdateFlags update_flags_ = display::SurfaceUpdateFlags::None; + + // Subset of attributes that may be interpreted by the display service. + bool visible_ = false; + int z_order_ = 0; + + DisplaySurface(const DisplaySurface&) = delete; + void operator=(const DisplaySurface&) = delete; +}; + +class ApplicationDisplaySurface : public DisplaySurface { + public: + ApplicationDisplaySurface(DisplayService* service, int surface_id, + int process_id, int user_id, + const display::SurfaceAttributes& attributes) + : DisplaySurface(service, SurfaceType::Application, surface_id, + process_id, user_id, attributes) {} + + std::shared_ptr<ConsumerQueue> GetQueue(int32_t queue_id); + std::vector<int32_t> GetQueueIds() const override; + + private: + pdx::Status<pdx::LocalChannelHandle> OnCreateQueue( + pdx::Message& message, size_t meta_size_bytes) override; + void OnQueueEvent(const std::shared_ptr<ConsumerQueue>& consumer_queue, + int events) override; + + std::unordered_map<int32_t, std::shared_ptr<ConsumerQueue>> consumer_queues_; +}; + +class DirectDisplaySurface : public DisplaySurface { + public: + DirectDisplaySurface(DisplayService* service, int surface_id, int process_id, + int user_id, + const display::SurfaceAttributes& attributes) + : DisplaySurface(service, SurfaceType::Direct, surface_id, process_id, + user_id, attributes), + acquired_buffers_(kMaxPostedBuffers) {} + bool IsBufferAvailable(); + bool IsBufferPosted(); + AcquiredBuffer AcquireCurrentBuffer(); + + // Get the newest buffer. Up to one buffer will be skipped. If a buffer is + // skipped, it will be stored in skipped_buffer if non null. + AcquiredBuffer AcquireNewestAvailableBuffer(AcquiredBuffer* skipped_buffer); + + private: + pdx::Status<pdx::LocalChannelHandle> OnCreateQueue( + pdx::Message& message, size_t meta_size_bytes) override; + void OnQueueEvent(const std::shared_ptr<ConsumerQueue>& consumer_queue, + int events) override; + + // The capacity of the pending buffer queue. Should be enough to hold all the + // buffers of this DisplaySurface, although in practice only 1 or 2 frames + // will be pending at a time. + static constexpr int kSurfaceBufferMaxCount = 4; + static constexpr int kSurfaceViewMaxCount = 4; + static constexpr int kMaxPostedBuffers = + kSurfaceBufferMaxCount * kSurfaceViewMaxCount; + + // Returns whether a frame is available without locking the mutex. + bool IsFrameAvailableNoLock() const; + + // Dequeue all available buffers from the consumer queue. + void DequeueBuffersLocked(); + + // In a triple-buffered surface, up to kMaxPostedBuffers buffers may be + // posted and pending. + RingBuffer<AcquiredBuffer> acquired_buffers_; + + std::shared_ptr<ConsumerQueue> direct_queue_; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_ diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp new file mode 100644 index 0000000000..06b69bbc26 --- /dev/null +++ b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp @@ -0,0 +1,142 @@ +#include "epoll_event_dispatcher.h" + +#include <log/log.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> +#include <sys/prctl.h> + +#include <dvr/performance_client_api.h> + +namespace android { +namespace dvr { + +EpollEventDispatcher::EpollEventDispatcher() { + epoll_fd_.Reset(epoll_create(64)); + if (!epoll_fd_) { + ALOGE("Failed to create epoll fd: %s", strerror(errno)); + return; + } + + event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); + if (!event_fd_) { + ALOGE("Failed to create event for epolling: %s", strerror(errno)); + return; + } + + // Add watch for eventfd. This should only watch for EPOLLIN, which gets set + // when eventfd_write occurs. Use "this" as a unique sentinal value to + // identify events from the event fd. + epoll_event event = {.events = EPOLLIN, .data = {.ptr = this}}; + if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd_.Get(), &event) < 0) { + ALOGE("Failed to add eventfd to epoll set because: %s", strerror(errno)); + return; + } + + thread_ = std::thread(&EpollEventDispatcher::EventThread, this); +} + +EpollEventDispatcher::~EpollEventDispatcher() { Stop(); } + +void EpollEventDispatcher::Stop() { + exit_thread_.store(true); + eventfd_write(event_fd_.Get(), 1); +} + +pdx::Status<void> EpollEventDispatcher::AddEventHandler(int fd, int event_mask, + Handler handler) { + std::lock_guard<std::mutex> lock(lock_); + + epoll_event event; + event.events = event_mask; + event.data.ptr = &(handlers_[fd] = handler); + + ALOGD_IF( + TRACE, + "EpollEventDispatcher::AddEventHandler: fd=%d event_mask=0x%x handler=%p", + fd, event_mask, event.data.ptr); + + if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, fd, &event) < 0) { + const int error = errno; + ALOGE("Failed to add fd to epoll set because: %s", strerror(error)); + return pdx::ErrorStatus(error); + } else { + return {}; + } +} + +pdx::Status<void> EpollEventDispatcher::RemoveEventHandler(int fd) { + ALOGD_IF(TRACE, "EpollEventDispatcher::RemoveEventHandler: fd=%d", fd); + std::lock_guard<std::mutex> lock(lock_); + + epoll_event dummy; // See BUGS in man 2 epoll_ctl. + if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &dummy) < 0) { + const int error = errno; + ALOGE("Failed to remove fd from epoll set because: %s", strerror(error)); + return pdx::ErrorStatus(error); + } + + // If the fd was valid above, add it to the list of ids to remove. + removed_handlers_.push_back(fd); + + // Wake up the event thread to clean up. + eventfd_write(event_fd_.Get(), 1); + + return {}; +} + +void EpollEventDispatcher::EventThread() { + prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrEvent"), 0, 0, 0); + + const int error = dvrSetSchedulerClass(0, "graphics"); + LOG_ALWAYS_FATAL_IF( + error < 0, + "EpollEventDispatcher::EventThread: Failed to set scheduler class: %s", + strerror(-error)); + + const size_t kMaxNumEvents = 128; + epoll_event events[kMaxNumEvents]; + + while (!exit_thread_.load()) { + const int num_events = epoll_wait(epoll_fd_.Get(), events, kMaxNumEvents, -1); + if (num_events < 0 && errno != EINTR) + break; + + ALOGD_IF(TRACE, "EpollEventDispatcher::EventThread: num_events=%d", + num_events); + + for (int i = 0; i < num_events; i++) { + ALOGD_IF( + TRACE, + "EpollEventDispatcher::EventThread: event %d: handler=%p events=0x%x", + i, events[i].data.ptr, events[i].events); + + if (events[i].data.ptr == this) { + // Clear pending event on event_fd_. Serialize the read with respect to + // writes from other threads. + std::lock_guard<std::mutex> lock(lock_); + eventfd_t value; + eventfd_read(event_fd_.Get(), &value); + } else { + auto handler = reinterpret_cast<Handler*>(events[i].data.ptr); + if (handler) + (*handler)(events[i].events); + } + } + + // Remove any handlers that have been posted for removal. This is done here + // instead of in RemoveEventHandler() to prevent races between the dispatch + // thread and the code requesting the removal. Handlers are guaranteed to + // stay alive between exiting epoll_wait() and the dispatch loop above. + std::lock_guard<std::mutex> lock(lock_); + for (auto handler_fd : removed_handlers_) { + ALOGD_IF(TRACE, + "EpollEventDispatcher::EventThread: removing handler: fd=%d", + handler_fd); + handlers_.erase(handler_fd); + } + removed_handlers_.clear(); + } +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.h b/libs/vr/libvrflinger/epoll_event_dispatcher.h new file mode 100644 index 0000000000..eb687f4e86 --- /dev/null +++ b/libs/vr/libvrflinger/epoll_event_dispatcher.h @@ -0,0 +1,63 @@ +#ifndef ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_ +#define ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_ + +#include <sys/epoll.h> + +#include <atomic> +#include <functional> +#include <mutex> +#include <thread> +#include <unordered_map> +#include <vector> + +#include <pdx/file_handle.h> +#include <pdx/status.h> + +namespace android { +namespace dvr { + +class EpollEventDispatcher { + public: + // Function type for event handlers. The handler receives a bitmask of the + // epoll events that occurred on the file descriptor associated with the + // handler. + using Handler = std::function<void(int)>; + + EpollEventDispatcher(); + ~EpollEventDispatcher(); + + // |handler| is called on the internal dispatch thread when |fd| is signaled + // by events in |event_mask|. + pdx::Status<void> AddEventHandler(int fd, int event_mask, Handler handler); + pdx::Status<void> RemoveEventHandler(int fd); + + void Stop(); + + private: + void EventThread(); + + std::thread thread_; + std::atomic<bool> exit_thread_{false}; + + // Protects handlers_ and removed_handlers_ and serializes operations on + // epoll_fd_ and event_fd_. + std::mutex lock_; + + // Maintains a map of fds to event handlers. This is primarily to keep any + // references alive that may be bound in the std::function instances. It is + // not used at dispatch time to avoid performance problems with different + // versions of std::unordered_map. + std::unordered_map<int, Handler> handlers_; + + // List of fds to be removed from the map. The actual removal is performed + // by the event dispatch thread to avoid races. + std::vector<int> removed_handlers_; + + pdx::LocalHandle epoll_fd_; + pdx::LocalHandle event_fd_; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_ diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp new file mode 100644 index 0000000000..34474d969a --- /dev/null +++ b/libs/vr/libvrflinger/hardware_composer.cpp @@ -0,0 +1,1134 @@ +#include "hardware_composer.h" + +#include <cutils/properties.h> +#include <cutils/sched_policy.h> +#include <fcntl.h> +#include <log/log.h> +#include <poll.h> +#include <sync/sync.h> +#include <sys/eventfd.h> +#include <sys/prctl.h> +#include <sys/resource.h> +#include <sys/system_properties.h> +#include <sys/timerfd.h> +#include <time.h> +#include <unistd.h> +#include <utils/Trace.h> + +#include <algorithm> +#include <chrono> +#include <functional> +#include <map> + +#include <dvr/dvr_display_types.h> +#include <dvr/performance_client_api.h> +#include <private/dvr/clock_ns.h> +#include <private/dvr/ion_buffer.h> +#include <private/dvr/pose_client_internal.h> + +using android::pdx::LocalHandle; +using android::pdx::rpc::EmptyVariant; +using android::pdx::rpc::IfAnyOf; + +using namespace std::chrono_literals; + +namespace android { +namespace dvr { + +namespace { + +// If the number of pending fences goes over this count at the point when we +// are about to submit a new frame to HWC, we will drop the frame. This should +// be a signal that the display driver has begun queuing frames. Note that with +// smart displays (with RAM), the fence is signaled earlier than the next vsync, +// at the point when the DMA to the display completes. Currently we use a smart +// display and the EDS timing coincides with zero pending fences, so this is 0. +constexpr int kAllowedPendingFenceCount = 0; + +// Offset before vsync to submit frames to hardware composer. +constexpr int64_t kFramePostOffsetNs = 4000000; // 4ms + +const char kBacklightBrightnessSysFile[] = + "/sys/class/leds/lcd-backlight/brightness"; + +const char kPrimaryDisplayVSyncEventFile[] = + "/sys/class/graphics/fb0/vsync_event"; + +const char kPrimaryDisplayWaitPPEventFile[] = "/sys/class/graphics/fb0/wait_pp"; + +const char kDvrPerformanceProperty[] = "sys.dvr.performance"; + +const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns"; + +// Get time offset from a vsync to when the pose for that vsync should be +// predicted out to. For example, if scanout gets halfway through the frame +// at the halfway point between vsyncs, then this could be half the period. +// With global shutter displays, this should be changed to the offset to when +// illumination begins. Low persistence adds a frame of latency, so we predict +// to the center of the next frame. +inline int64_t GetPosePredictionTimeOffset(int64_t vsync_period_ns) { + return (vsync_period_ns * 150) / 100; +} + +// Attempts to set the scheduler class and partiton for the current thread. +// Returns true on success or false on failure. +bool SetThreadPolicy(const std::string& scheduler_class, + const std::string& partition) { + int error = dvrSetSchedulerClass(0, scheduler_class.c_str()); + if (error < 0) { + ALOGE( + "SetThreadPolicy: Failed to set scheduler class \"%s\" for " + "thread_id=%d: %s", + scheduler_class.c_str(), gettid(), strerror(-error)); + return false; + } + error = dvrSetCpuPartition(0, partition.c_str()); + if (error < 0) { + ALOGE( + "SetThreadPolicy: Failed to set cpu partiton \"%s\" for thread_id=%d: " + "%s", + partition.c_str(), gettid(), strerror(-error)); + return false; + } + return true; +} + +} // anonymous namespace + +// Layer static data. +Hwc2::Composer* Layer::hwc2_hidl_; +const HWCDisplayMetrics* Layer::display_metrics_; + +// HardwareComposer static data; +constexpr size_t HardwareComposer::kMaxHardwareLayers; + +HardwareComposer::HardwareComposer() + : HardwareComposer(nullptr, RequestDisplayCallback()) {} + +HardwareComposer::HardwareComposer( + Hwc2::Composer* hwc2_hidl, RequestDisplayCallback request_display_callback) + : initialized_(false), + hwc2_hidl_(hwc2_hidl), + request_display_callback_(request_display_callback), + callbacks_(new ComposerCallback) {} + +HardwareComposer::~HardwareComposer(void) { + UpdatePostThreadState(PostThreadState::Quit, true); + if (post_thread_.joinable()) + post_thread_.join(); +} + +bool HardwareComposer::Initialize() { + if (initialized_) { + ALOGE("HardwareComposer::Initialize: already initialized."); + return false; + } + + HWC::Error error = HWC::Error::None; + + Hwc2::Config config; + error = hwc2_hidl_->getActiveConfig(HWC_DISPLAY_PRIMARY, &config); + + if (error != HWC::Error::None) { + ALOGE("HardwareComposer: Failed to get current display config : %d", + config); + return false; + } + + error = + GetDisplayMetrics(HWC_DISPLAY_PRIMARY, config, &native_display_metrics_); + + if (error != HWC::Error::None) { + ALOGE( + "HardwareComposer: Failed to get display attributes for current " + "configuration : %d", + error.value); + return false; + } + + ALOGI( + "HardwareComposer: primary display attributes: width=%d height=%d " + "vsync_period_ns=%d DPI=%dx%d", + native_display_metrics_.width, native_display_metrics_.height, + native_display_metrics_.vsync_period_ns, native_display_metrics_.dpi.x, + native_display_metrics_.dpi.y); + + // Set the display metrics but never use rotation to avoid the long latency of + // rotation processing in hwc. + display_transform_ = HWC_TRANSFORM_NONE; + display_metrics_ = native_display_metrics_; + + // Pass hwc instance and metrics to setup globals for Layer. + Layer::InitializeGlobals(hwc2_hidl_, &native_display_metrics_); + + post_thread_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); + LOG_ALWAYS_FATAL_IF( + !post_thread_event_fd_, + "HardwareComposer: Failed to create interrupt event fd : %s", + strerror(errno)); + + post_thread_ = std::thread(&HardwareComposer::PostThread, this); + + initialized_ = true; + + return initialized_; +} + +void HardwareComposer::Enable() { + UpdatePostThreadState(PostThreadState::Suspended, false); +} + +void HardwareComposer::Disable() { + UpdatePostThreadState(PostThreadState::Suspended, true); +} + +// Update the post thread quiescent state based on idle and suspended inputs. +void HardwareComposer::UpdatePostThreadState(PostThreadStateType state, + bool suspend) { + std::unique_lock<std::mutex> lock(post_thread_mutex_); + + // Update the votes in the state variable before evaluating the effective + // quiescent state. Any bits set in post_thread_state_ indicate that the post + // thread should be suspended. + if (suspend) { + post_thread_state_ |= state; + } else { + post_thread_state_ &= ~state; + } + + const bool quit = post_thread_state_ & PostThreadState::Quit; + const bool effective_suspend = post_thread_state_ != PostThreadState::Active; + if (quit) { + post_thread_quiescent_ = true; + eventfd_write(post_thread_event_fd_.Get(), 1); + post_thread_wait_.notify_one(); + } else if (effective_suspend && !post_thread_quiescent_) { + post_thread_quiescent_ = true; + eventfd_write(post_thread_event_fd_.Get(), 1); + } else if (!effective_suspend && post_thread_quiescent_) { + post_thread_quiescent_ = false; + eventfd_t value; + eventfd_read(post_thread_event_fd_.Get(), &value); + post_thread_wait_.notify_one(); + } + + // Wait until the post thread is in the requested state. + post_thread_ready_.wait(lock, [this, effective_suspend] { + return effective_suspend != post_thread_resumed_; + }); +} + +void HardwareComposer::OnPostThreadResumed() { + hwc2_hidl_->resetCommands(); + + // Connect to pose service. + pose_client_ = dvrPoseCreate(); + ALOGE_IF(!pose_client_, "HardwareComposer: Failed to create pose client"); + + // HIDL HWC seems to have an internal race condition. If we submit a frame too + // soon after turning on VSync we don't get any VSync signals. Give poor HWC + // implementations a chance to enable VSync before we continue. + EnableVsync(false); + std::this_thread::sleep_for(100ms); + EnableVsync(true); + std::this_thread::sleep_for(100ms); + + // TODO(skiazyk): We need to do something about accessing this directly, + // supposedly there is a backlight service on the way. + // TODO(steventhomas): When we change the backlight setting, will surface + // flinger (or something else) set it back to its original value once we give + // control of the display back to surface flinger? + SetBacklightBrightness(255); + + // Trigger target-specific performance mode change. + property_set(kDvrPerformanceProperty, "performance"); +} + +void HardwareComposer::OnPostThreadPaused() { + retire_fence_fds_.clear(); + display_surfaces_.clear(); + + for (size_t i = 0; i < kMaxHardwareLayers; ++i) { + layers_[i].Reset(); + } + active_layer_count_ = 0; + + if (pose_client_) { + dvrPoseDestroy(pose_client_); + pose_client_ = nullptr; + } + + EnableVsync(false); + + hwc2_hidl_->resetCommands(); + + // Trigger target-specific performance mode change. + property_set(kDvrPerformanceProperty, "idle"); +} + +HWC::Error HardwareComposer::Validate(hwc2_display_t display) { + uint32_t num_types; + uint32_t num_requests; + HWC::Error error = + hwc2_hidl_->validateDisplay(display, &num_types, &num_requests); + + if (error == HWC2_ERROR_HAS_CHANGES) { + // TODO(skiazyk): We might need to inspect the requested changes first, but + // so far it seems like we shouldn't ever hit a bad state. + // error = hwc2_funcs_.accept_display_changes_fn_(hardware_composer_device_, + // display); + error = hwc2_hidl_->acceptDisplayChanges(display); + } + + return error; +} + +int32_t HardwareComposer::EnableVsync(bool enabled) { + return (int32_t)hwc2_hidl_->setVsyncEnabled( + HWC_DISPLAY_PRIMARY, + (Hwc2::IComposerClient::Vsync)(enabled ? HWC2_VSYNC_ENABLE + : HWC2_VSYNC_DISABLE)); +} + +HWC::Error HardwareComposer::Present(hwc2_display_t display) { + int32_t present_fence; + HWC::Error error = hwc2_hidl_->presentDisplay(display, &present_fence); + + // According to the documentation, this fence is signaled at the time of + // vsync/DMA for physical displays. + if (error == HWC::Error::None) { + ATRACE_INT("HardwareComposer: VsyncFence", present_fence); + retire_fence_fds_.emplace_back(present_fence); + } else { + ATRACE_INT("HardwareComposer: PresentResult", error); + } + + return error; +} + +HWC::Error HardwareComposer::GetDisplayAttribute(hwc2_display_t display, + hwc2_config_t config, + hwc2_attribute_t attribute, + int32_t* out_value) const { + return hwc2_hidl_->getDisplayAttribute( + display, config, (Hwc2::IComposerClient::Attribute)attribute, out_value); +} + +HWC::Error HardwareComposer::GetDisplayMetrics( + hwc2_display_t display, hwc2_config_t config, + HWCDisplayMetrics* out_metrics) const { + HWC::Error error; + + error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_WIDTH, + &out_metrics->width); + if (error != HWC::Error::None) { + ALOGE( + "HardwareComposer::GetDisplayMetrics: Failed to get display width: %s", + error.to_string().c_str()); + return error; + } + + error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_HEIGHT, + &out_metrics->height); + if (error != HWC::Error::None) { + ALOGE( + "HardwareComposer::GetDisplayMetrics: Failed to get display height: %s", + error.to_string().c_str()); + return error; + } + + error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_VSYNC_PERIOD, + &out_metrics->vsync_period_ns); + if (error != HWC::Error::None) { + ALOGE( + "HardwareComposer::GetDisplayMetrics: Failed to get display height: %s", + error.to_string().c_str()); + return error; + } + + error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_DPI_X, + &out_metrics->dpi.x); + if (error != HWC::Error::None) { + ALOGE( + "HardwareComposer::GetDisplayMetrics: Failed to get display DPI X: %s", + error.to_string().c_str()); + return error; + } + + error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_DPI_Y, + &out_metrics->dpi.y); + if (error != HWC::Error::None) { + ALOGE( + "HardwareComposer::GetDisplayMetrics: Failed to get display DPI Y: %s", + error.to_string().c_str()); + return error; + } + + return HWC::Error::None; +} + +std::string HardwareComposer::Dump() { return hwc2_hidl_->dumpDebugInfo(); } + +void HardwareComposer::PostLayers() { + ATRACE_NAME("HardwareComposer::PostLayers"); + + // Setup the hardware composer layers with current buffers. + for (size_t i = 0; i < active_layer_count_; i++) { + layers_[i].Prepare(); + } + + HWC::Error error = Validate(HWC_DISPLAY_PRIMARY); + if (error != HWC::Error::None) { + ALOGE("HardwareComposer::PostLayers: Validate failed: %s", + error.to_string().c_str()); + return; + } + + // Now that we have taken in a frame from the application, we have a chance + // to drop the frame before passing the frame along to HWC. + // If the display driver has become backed up, we detect it here and then + // react by skipping this frame to catch up latency. + while (!retire_fence_fds_.empty() && + (!retire_fence_fds_.front() || + sync_wait(retire_fence_fds_.front().Get(), 0) == 0)) { + // There are only 2 fences in here, no performance problem to shift the + // array of ints. + retire_fence_fds_.erase(retire_fence_fds_.begin()); + } + + const bool is_frame_pending = IsFramePendingInDriver(); + const bool is_fence_pending = + retire_fence_fds_.size() > kAllowedPendingFenceCount; + + if (is_fence_pending || is_frame_pending) { + ATRACE_INT("frame_skip_count", ++frame_skip_count_); + + ALOGW_IF(is_frame_pending, "Warning: frame already queued, dropping frame"); + ALOGW_IF(is_fence_pending, + "Warning: dropping a frame to catch up with HWC (pending = %zd)", + retire_fence_fds_.size()); + + for (size_t i = 0; i < active_layer_count_; i++) { + layers_[i].Drop(); + } + return; + } else { + // Make the transition more obvious in systrace when the frame skip happens + // above. + ATRACE_INT("frame_skip_count", 0); + } + +#if TRACE + for (size_t i = 0; i < active_layer_count_; i++) + ALOGI("HardwareComposer::PostLayers: layer=%zu composition=%s", i, + layers_[i].GetCompositionType().to_string().c_str()); +#endif + + error = Present(HWC_DISPLAY_PRIMARY); + if (error != HWC::Error::None) { + ALOGE("HardwareComposer::PostLayers: Present failed: %s", + error.to_string().c_str()); + return; + } + + std::vector<Hwc2::Layer> out_layers; + std::vector<int> out_fences; + error = hwc2_hidl_->getReleaseFences(HWC_DISPLAY_PRIMARY, &out_layers, + &out_fences); + ALOGE_IF(error != HWC::Error::None, + "HardwareComposer::PostLayers: Failed to get release fences: %s", + error.to_string().c_str()); + + // Perform post-frame bookkeeping. Unused layers are a no-op. + uint32_t num_elements = out_layers.size(); + for (size_t i = 0; i < num_elements; ++i) { + for (size_t j = 0; j < active_layer_count_; ++j) { + if (layers_[j].GetLayerHandle() == out_layers[i]) { + layers_[j].Finish(out_fences[i]); + } + } + } +} + +void HardwareComposer::SetDisplaySurfaces( + std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces) { + ALOGI("HardwareComposer::SetDisplaySurfaces: surface count=%zd", + surfaces.size()); + const bool display_idle = surfaces.size() == 0; + { + std::unique_lock<std::mutex> lock(post_thread_mutex_); + pending_surfaces_ = std::move(surfaces); + } + + // Set idle state based on whether there are any surfaces to handle. + UpdatePostThreadState(PostThreadState::Idle, display_idle); + + // XXX: TEMPORARY + // Request control of the display based on whether there are any surfaces to + // handle. This callback sets the post thread active state once the transition + // is complete in SurfaceFlinger. + // TODO(eieio): Unify the control signal used to move SurfaceFlinger into VR + // mode. Currently this is hooked up to persistent VR mode, but perhaps this + // makes more sense to control it from VrCore, which could in turn base its + // decision on persistent VR mode. + if (request_display_callback_) + request_display_callback_(!display_idle); +} + +int HardwareComposer::PostThreadPollInterruptible( + const pdx::LocalHandle& event_fd, int requested_events) { + pollfd pfd[2] = { + { + .fd = event_fd.Get(), + .events = static_cast<short>(requested_events), + .revents = 0, + }, + { + .fd = post_thread_event_fd_.Get(), + .events = POLLPRI | POLLIN, + .revents = 0, + }, + }; + int ret, error; + do { + ret = poll(pfd, 2, -1); + error = errno; + ALOGW_IF(ret < 0, + "HardwareComposer::PostThreadPollInterruptible: Error during " + "poll(): %s (%d)", + strerror(error), error); + } while (ret < 0 && error == EINTR); + + if (ret < 0) { + return -error; + } else if (pfd[0].revents != 0) { + return 0; + } else if (pfd[1].revents != 0) { + ALOGI("VrHwcPost thread interrupted"); + return kPostThreadInterrupted; + } else { + return 0; + } +} + +// Reads the value of the display driver wait_pingpong state. Returns 0 or 1 +// (the value of the state) on success or a negative error otherwise. +// TODO(eieio): This is pretty driver specific, this should be moved to a +// separate class eventually. +int HardwareComposer::ReadWaitPPState() { + // Gracefully handle when the kernel does not support this feature. + if (!primary_display_wait_pp_fd_) + return 0; + + const int wait_pp_fd = primary_display_wait_pp_fd_.Get(); + int ret, error; + + ret = lseek(wait_pp_fd, 0, SEEK_SET); + if (ret < 0) { + error = errno; + ALOGE("HardwareComposer::ReadWaitPPState: Failed to seek wait_pp fd: %s", + strerror(error)); + return -error; + } + + char data = -1; + ret = read(wait_pp_fd, &data, sizeof(data)); + if (ret < 0) { + error = errno; + ALOGE("HardwareComposer::ReadWaitPPState: Failed to read wait_pp state: %s", + strerror(error)); + return -error; + } + + switch (data) { + case '0': + return 0; + case '1': + return 1; + default: + ALOGE( + "HardwareComposer::ReadWaitPPState: Unexpected value for wait_pp: %d", + data); + return -EINVAL; + } +} + +// Reads the timestamp of the last vsync from the display driver. +// TODO(eieio): This is pretty driver specific, this should be moved to a +// separate class eventually. +int HardwareComposer::ReadVSyncTimestamp(int64_t* timestamp) { + const int event_fd = primary_display_vsync_event_fd_.Get(); + int ret, error; + + // The driver returns data in the form "VSYNC=<timestamp ns>". + std::array<char, 32> data; + data.fill('\0'); + + // Seek back to the beginning of the event file. + ret = lseek(event_fd, 0, SEEK_SET); + if (ret < 0) { + error = errno; + ALOGE( + "HardwareComposer::ReadVSyncTimestamp: Failed to seek vsync event fd: " + "%s", + strerror(error)); + return -error; + } + + // Read the vsync event timestamp. + ret = read(event_fd, data.data(), data.size()); + if (ret < 0) { + error = errno; + ALOGE_IF( + error != EAGAIN, + "HardwareComposer::ReadVSyncTimestamp: Error while reading timestamp: " + "%s", + strerror(error)); + return -error; + } + + ret = sscanf(data.data(), "VSYNC=%" PRIu64, + reinterpret_cast<uint64_t*>(timestamp)); + if (ret < 0) { + error = errno; + ALOGE( + "HardwareComposer::ReadVSyncTimestamp: Error while parsing timestamp: " + "%s", + strerror(error)); + return -error; + } + + return 0; +} + +// Blocks until the next vsync event is signaled by the display driver. +// TODO(eieio): This is pretty driver specific, this should be moved to a +// separate class eventually. +int HardwareComposer::BlockUntilVSync() { + // Vsync is signaled by POLLPRI on the fb vsync node. + return PostThreadPollInterruptible(primary_display_vsync_event_fd_, POLLPRI); +} + +// Waits for the next vsync and returns the timestamp of the vsync event. If +// vsync already passed since the last call, returns the latest vsync timestamp +// instead of blocking. This method updates the last_vsync_timeout_ in the +// process. +// +// TODO(eieio): This is pretty driver specific, this should be moved to a +// separate class eventually. +int HardwareComposer::WaitForVSync(int64_t* timestamp) { + int error; + + // Get the current timestamp and decide what to do. + while (true) { + int64_t current_vsync_timestamp; + error = ReadVSyncTimestamp(¤t_vsync_timestamp); + if (error < 0 && error != -EAGAIN) + return error; + + if (error == -EAGAIN) { + // Vsync was turned off, wait for the next vsync event. + error = BlockUntilVSync(); + if (error < 0 || error == kPostThreadInterrupted) + return error; + + // Try again to get the timestamp for this new vsync interval. + continue; + } + + // Check that we advanced to a later vsync interval. + if (TimestampGT(current_vsync_timestamp, last_vsync_timestamp_)) { + *timestamp = last_vsync_timestamp_ = current_vsync_timestamp; + return 0; + } + + // See how close we are to the next expected vsync. If we're within 1ms, + // sleep for 1ms and try again. + const int64_t ns_per_frame = display_metrics_.vsync_period_ns; + const int64_t threshold_ns = 1000000; // 1ms + + const int64_t next_vsync_est = last_vsync_timestamp_ + ns_per_frame; + const int64_t distance_to_vsync_est = next_vsync_est - GetSystemClockNs(); + + if (distance_to_vsync_est > threshold_ns) { + // Wait for vsync event notification. + error = BlockUntilVSync(); + if (error < 0 || error == kPostThreadInterrupted) + return error; + } else { + // Sleep for a short time (1 millisecond) before retrying. + error = SleepUntil(GetSystemClockNs() + threshold_ns); + if (error < 0 || error == kPostThreadInterrupted) + return error; + } + } +} + +int HardwareComposer::SleepUntil(int64_t wakeup_timestamp) { + const int timer_fd = vsync_sleep_timer_fd_.Get(); + const itimerspec wakeup_itimerspec = { + .it_interval = {.tv_sec = 0, .tv_nsec = 0}, + .it_value = NsToTimespec(wakeup_timestamp), + }; + int ret = + timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &wakeup_itimerspec, nullptr); + int error = errno; + if (ret < 0) { + ALOGE("HardwareComposer::SleepUntil: Failed to set timerfd: %s", + strerror(error)); + return -error; + } + + return PostThreadPollInterruptible(vsync_sleep_timer_fd_, POLLIN); +} + +void HardwareComposer::PostThread() { + // NOLINTNEXTLINE(runtime/int) + prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrHwcPost"), 0, 0, 0); + + // Set the scheduler to SCHED_FIFO with high priority. If this fails here + // there may have been a startup timing issue between this thread and + // performanced. Try again later when this thread becomes active. + bool thread_policy_setup = + SetThreadPolicy("graphics:high", "/system/performance"); + +#if ENABLE_BACKLIGHT_BRIGHTNESS + // TODO(hendrikw): This isn't required at the moment. It's possible that there + // is another method to access this when needed. + // Open the backlight brightness control sysfs node. + backlight_brightness_fd_ = LocalHandle(kBacklightBrightnessSysFile, O_RDWR); + ALOGW_IF(!backlight_brightness_fd_, + "HardwareComposer: Failed to open backlight brightness control: %s", + strerror(errno)); +#endif // ENABLE_BACKLIGHT_BRIGHTNESS + + // Open the vsync event node for the primary display. + // TODO(eieio): Move this into a platform-specific class. + primary_display_vsync_event_fd_ = + LocalHandle(kPrimaryDisplayVSyncEventFile, O_RDONLY); + ALOGE_IF(!primary_display_vsync_event_fd_, + "HardwareComposer: Failed to open vsync event node for primary " + "display: %s", + strerror(errno)); + + // Open the wait pingpong status node for the primary display. + // TODO(eieio): Move this into a platform-specific class. + primary_display_wait_pp_fd_ = + LocalHandle(kPrimaryDisplayWaitPPEventFile, O_RDONLY); + ALOGW_IF( + !primary_display_wait_pp_fd_, + "HardwareComposer: Failed to open wait_pp node for primary display: %s", + strerror(errno)); + + // Create a timerfd based on CLOCK_MONOTINIC. + vsync_sleep_timer_fd_.Reset(timerfd_create(CLOCK_MONOTONIC, 0)); + LOG_ALWAYS_FATAL_IF( + !vsync_sleep_timer_fd_, + "HardwareComposer: Failed to create vsync sleep timerfd: %s", + strerror(errno)); + + const int64_t ns_per_frame = display_metrics_.vsync_period_ns; + const int64_t photon_offset_ns = GetPosePredictionTimeOffset(ns_per_frame); + + // TODO(jbates) Query vblank time from device, when such an API is available. + // This value (6.3%) was measured on A00 in low persistence mode. + int64_t vblank_ns = ns_per_frame * 63 / 1000; + int64_t right_eye_photon_offset_ns = (ns_per_frame - vblank_ns) / 2; + + // Check property for overriding right eye offset value. + right_eye_photon_offset_ns = + property_get_int64(kRightEyeOffsetProperty, right_eye_photon_offset_ns); + + bool was_running = false; + + while (1) { + ATRACE_NAME("HardwareComposer::PostThread"); + + while (post_thread_quiescent_) { + std::unique_lock<std::mutex> lock(post_thread_mutex_); + ALOGI("HardwareComposer::PostThread: Entering quiescent state."); + + // Tear down resources. + OnPostThreadPaused(); + + was_running = false; + post_thread_resumed_ = false; + post_thread_ready_.notify_all(); + + if (post_thread_state_ & PostThreadState::Quit) { + ALOGI("HardwareComposer::PostThread: Quitting."); + return; + } + + post_thread_wait_.wait(lock, [this] { return !post_thread_quiescent_; }); + + post_thread_resumed_ = true; + post_thread_ready_.notify_all(); + + ALOGI("HardwareComposer::PostThread: Exiting quiescent state."); + } + + if (!was_running) { + // Setup resources. + OnPostThreadResumed(); + was_running = true; + + // Try to setup the scheduler policy if it failed during startup. Only + // attempt to do this on transitions from inactive to active to avoid + // spamming the system with RPCs and log messages. + if (!thread_policy_setup) { + thread_policy_setup = + SetThreadPolicy("graphics:high", "/system/performance"); + } + } + + int64_t vsync_timestamp = 0; + { + std::array<char, 128> buf; + snprintf(buf.data(), buf.size(), "wait_vsync|vsync=%d|", + vsync_count_ + 1); + ATRACE_NAME(buf.data()); + + const int error = WaitForVSync(&vsync_timestamp); + ALOGE_IF( + error < 0, + "HardwareComposer::PostThread: Failed to wait for vsync event: %s", + strerror(-error)); + // Don't bother processing this frame if a pause was requested + if (error == kPostThreadInterrupted) + continue; + } + + ++vsync_count_; + + if (pose_client_) { + // Signal the pose service with vsync info. + // Display timestamp is in the middle of scanout. + privateDvrPoseNotifyVsync(pose_client_, vsync_count_, + vsync_timestamp + photon_offset_ns, + ns_per_frame, right_eye_photon_offset_ns); + } + + const bool layer_config_changed = UpdateLayerConfig(); + + // Signal all of the vsync clients. Because absolute time is used for the + // wakeup time below, this can take a little time if necessary. + if (vsync_callback_) + vsync_callback_(HWC_DISPLAY_PRIMARY, vsync_timestamp, + /*frame_time_estimate*/ 0, vsync_count_); + + { + // Sleep until shortly before vsync. + ATRACE_NAME("sleep"); + + const int64_t display_time_est_ns = vsync_timestamp + ns_per_frame; + const int64_t now_ns = GetSystemClockNs(); + const int64_t sleep_time_ns = + display_time_est_ns - now_ns - kFramePostOffsetNs; + const int64_t wakeup_time_ns = display_time_est_ns - kFramePostOffsetNs; + + ATRACE_INT64("sleep_time_ns", sleep_time_ns); + if (sleep_time_ns > 0) { + int error = SleepUntil(wakeup_time_ns); + ALOGE_IF(error < 0, "HardwareComposer::PostThread: Failed to sleep: %s", + strerror(-error)); + if (error == kPostThreadInterrupted) { + if (layer_config_changed) { + // If the layer config changed we need to validateDisplay() even if + // we're going to drop the frame, to flush the Composer object's + // internal command buffer and apply our layer changes. + Validate(HWC_DISPLAY_PRIMARY); + } + continue; + } + } + } + + PostLayers(); + } +} + +// Checks for changes in the surface stack and updates the layer config to +// accomodate the new stack. +bool HardwareComposer::UpdateLayerConfig() { + std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces; + { + std::unique_lock<std::mutex> lock(post_thread_mutex_); + if (pending_surfaces_.empty()) + return false; + + surfaces = std::move(pending_surfaces_); + } + + ATRACE_NAME("UpdateLayerConfig_HwLayers"); + + display_surfaces_.clear(); + + Layer* target_layer; + size_t layer_index; + for (layer_index = 0; + layer_index < std::min(surfaces.size(), kMaxHardwareLayers); + layer_index++) { + // The bottom layer is opaque, other layers blend. + HWC::BlendMode blending = + layer_index == 0 ? HWC::BlendMode::None : HWC::BlendMode::Coverage; + layers_[layer_index].Setup(surfaces[layer_index], blending, + display_transform_, HWC::Composition::Device, + layer_index); + display_surfaces_.push_back(surfaces[layer_index]); + } + + // Clear unused layers. + for (size_t i = layer_index; i < kMaxHardwareLayers; i++) + layers_[i].Reset(); + + active_layer_count_ = layer_index; + ALOGD_IF(TRACE, "HardwareComposer::UpdateLayerConfig: %zd active layers", + active_layer_count_); + + // Any surfaces left over could not be assigned a hardware layer and will + // not be displayed. + ALOGW_IF(surfaces.size() != display_surfaces_.size(), + "HardwareComposer::UpdateLayerConfig: More surfaces than layers: " + "pending_surfaces=%zu display_surfaces=%zu", + surfaces.size(), display_surfaces_.size()); + + return true; +} + +void HardwareComposer::SetVSyncCallback(VSyncCallback callback) { + vsync_callback_ = callback; +} + +void HardwareComposer::HwcRefresh(hwc2_callback_data_t /*data*/, + hwc2_display_t /*display*/) { + // TODO(eieio): implement invalidate callbacks. +} + +void HardwareComposer::HwcVSync(hwc2_callback_data_t /*data*/, + hwc2_display_t /*display*/, + int64_t /*timestamp*/) { + ATRACE_NAME(__PRETTY_FUNCTION__); + // Intentionally empty. HWC may require a callback to be set to enable vsync + // signals. We bypass this callback thread by monitoring the vsync event + // directly, but signals still need to be enabled. +} + +void HardwareComposer::HwcHotplug(hwc2_callback_data_t /*callbackData*/, + hwc2_display_t /*display*/, + hwc2_connection_t /*connected*/) { + // TODO(eieio): implement display hotplug callbacks. +} + +void HardwareComposer::OnHardwareComposerRefresh() { + // TODO(steventhomas): Handle refresh. +} + +void HardwareComposer::SetBacklightBrightness(int brightness) { + if (backlight_brightness_fd_) { + std::array<char, 32> text; + const int length = snprintf(text.data(), text.size(), "%d", brightness); + write(backlight_brightness_fd_.Get(), text.data(), length); + } +} + +void Layer::InitializeGlobals(Hwc2::Composer* hwc2_hidl, + const HWCDisplayMetrics* metrics) { + hwc2_hidl_ = hwc2_hidl; + display_metrics_ = metrics; +} + +void Layer::Reset() { + if (hwc2_hidl_ != nullptr && hardware_composer_layer_) { + hwc2_hidl_->destroyLayer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_); + hardware_composer_layer_ = 0; + } + + z_order_ = 0; + blending_ = HWC::BlendMode::None; + transform_ = HWC::Transform::None; + composition_type_ = HWC::Composition::Invalid; + target_composition_type_ = composition_type_; + source_ = EmptyVariant{}; + acquire_fence_.Close(); + surface_rect_functions_applied_ = false; +} + +void Layer::Setup(const std::shared_ptr<DirectDisplaySurface>& surface, + HWC::BlendMode blending, HWC::Transform transform, + HWC::Composition composition_type, size_t z_order) { + Reset(); + z_order_ = z_order; + blending_ = blending; + transform_ = transform; + composition_type_ = HWC::Composition::Invalid; + target_composition_type_ = composition_type; + source_ = SourceSurface{surface}; + CommonLayerSetup(); +} + +void Layer::Setup(const std::shared_ptr<IonBuffer>& buffer, + HWC::BlendMode blending, HWC::Transform transform, + HWC::Composition composition_type, size_t z_order) { + Reset(); + z_order_ = z_order; + blending_ = blending; + transform_ = transform; + composition_type_ = HWC::Composition::Invalid; + target_composition_type_ = composition_type; + source_ = SourceBuffer{buffer}; + CommonLayerSetup(); +} + +void Layer::UpdateBuffer(const std::shared_ptr<IonBuffer>& buffer) { + if (source_.is<SourceBuffer>()) + std::get<SourceBuffer>(source_) = {buffer}; +} + +void Layer::SetBlending(HWC::BlendMode blending) { blending_ = blending; } +void Layer::SetZOrder(size_t z_order) { z_order_ = z_order; } + +IonBuffer* Layer::GetBuffer() { + struct Visitor { + IonBuffer* operator()(SourceSurface& source) { return source.GetBuffer(); } + IonBuffer* operator()(SourceBuffer& source) { return source.GetBuffer(); } + IonBuffer* operator()(EmptyVariant) { return nullptr; } + }; + return source_.Visit(Visitor{}); +} + +void Layer::UpdateLayerSettings() { + if (!IsLayerSetup()) { + ALOGE( + "HardwareComposer::Layer::UpdateLayerSettings: Attempt to update " + "unused Layer!"); + return; + } + + HWC::Error error; + hwc2_display_t display = HWC_DISPLAY_PRIMARY; + + error = hwc2_hidl_->setLayerCompositionType( + display, hardware_composer_layer_, + composition_type_.cast<Hwc2::IComposerClient::Composition>()); + ALOGE_IF( + error != HWC::Error::None, + "Layer::UpdateLayerSettings: Error setting layer composition type: %s", + error.to_string().c_str()); + + error = hwc2_hidl_->setLayerBlendMode( + display, hardware_composer_layer_, + blending_.cast<Hwc2::IComposerClient::BlendMode>()); + ALOGE_IF(error != HWC::Error::None, + "Layer::UpdateLayerSettings: Error setting layer blend mode: %s", + error.to_string().c_str()); + + // TODO(eieio): Use surface attributes or some other mechanism to control + // the layer display frame. + error = hwc2_hidl_->setLayerDisplayFrame( + display, hardware_composer_layer_, + {0, 0, display_metrics_->width, display_metrics_->height}); + ALOGE_IF(error != HWC::Error::None, + "Layer::UpdateLayerSettings: Error setting layer display frame: %s", + error.to_string().c_str()); + + error = hwc2_hidl_->setLayerVisibleRegion( + display, hardware_composer_layer_, + {{0, 0, display_metrics_->width, display_metrics_->height}}); + ALOGE_IF(error != HWC::Error::None, + "Layer::UpdateLayerSettings: Error setting layer visible region: %s", + error.to_string().c_str()); + + error = + hwc2_hidl_->setLayerPlaneAlpha(display, hardware_composer_layer_, 1.0f); + ALOGE_IF(error != HWC::Error::None, + "Layer::UpdateLayerSettings: Error setting layer plane alpha: %s", + error.to_string().c_str()); + + error = + hwc2_hidl_->setLayerZOrder(display, hardware_composer_layer_, z_order_); + ALOGE_IF(error != HWC::Error::None, + "Layer::UpdateLayerSettings: Error setting z_ order: %s", + error.to_string().c_str()); +} + +void Layer::CommonLayerSetup() { + HWC::Error error = + hwc2_hidl_->createLayer(HWC_DISPLAY_PRIMARY, &hardware_composer_layer_); + ALOGE_IF( + error != HWC::Error::None, + "Layer::CommonLayerSetup: Failed to create layer on primary display: %s", + error.to_string().c_str()); + UpdateLayerSettings(); +} + +void Layer::Prepare() { + int right, bottom; + sp<GraphicBuffer> handle; + + // Acquire the next buffer according to the type of source. + IfAnyOf<SourceSurface, SourceBuffer>::Call(&source_, [&](auto& source) { + std::tie(right, bottom, handle, acquire_fence_) = source.Acquire(); + }); + + // When a layer is first setup there may be some time before the first buffer + // arrives. Setup the HWC layer as a solid color to stall for time until the + // first buffer arrives. Once the first buffer arrives there will always be a + // buffer for the frame even if it is old. + if (!handle.get()) { + if (composition_type_ == HWC::Composition::Invalid) { + composition_type_ = HWC::Composition::SolidColor; + hwc2_hidl_->setLayerCompositionType( + HWC_DISPLAY_PRIMARY, hardware_composer_layer_, + composition_type_.cast<Hwc2::IComposerClient::Composition>()); + Hwc2::IComposerClient::Color layer_color = {0, 0, 0, 0}; + hwc2_hidl_->setLayerColor(HWC_DISPLAY_PRIMARY, hardware_composer_layer_, + layer_color); + } else { + // The composition type is already set. Nothing else to do until a + // buffer arrives. + } + } else { + if (composition_type_ != target_composition_type_) { + composition_type_ = target_composition_type_; + hwc2_hidl_->setLayerCompositionType( + HWC_DISPLAY_PRIMARY, hardware_composer_layer_, + composition_type_.cast<Hwc2::IComposerClient::Composition>()); + } + + HWC::Error error{HWC::Error::None}; + error = hwc2_hidl_->setLayerBuffer(HWC_DISPLAY_PRIMARY, + hardware_composer_layer_, 0, handle, + acquire_fence_.Get()); + + ALOGE_IF(error != HWC::Error::None, + "Layer::Prepare: Error setting layer buffer: %s", + error.to_string().c_str()); + + if (!surface_rect_functions_applied_) { + const float float_right = right; + const float float_bottom = bottom; + error = hwc2_hidl_->setLayerSourceCrop(HWC_DISPLAY_PRIMARY, + hardware_composer_layer_, + {0, 0, float_right, float_bottom}); + + ALOGE_IF(error != HWC::Error::None, + "Layer::Prepare: Error setting layer source crop: %s", + error.to_string().c_str()); + + surface_rect_functions_applied_ = true; + } + } +} + +void Layer::Finish(int release_fence_fd) { + IfAnyOf<SourceSurface, SourceBuffer>::Call( + &source_, [release_fence_fd](auto& source) { + source.Finish(LocalHandle(release_fence_fd)); + }); +} + +void Layer::Drop() { acquire_fence_.Close(); } + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h new file mode 100644 index 0000000000..8ba72ab773 --- /dev/null +++ b/libs/vr/libvrflinger/hardware_composer.h @@ -0,0 +1,457 @@ +#ifndef ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_ +#define ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_ + +#include <ui/GraphicBuffer.h> +#include "DisplayHardware/ComposerHal.h" +#include "hwc_types.h" + +#include <hardware/gralloc.h> +#include <log/log.h> + +#include <array> +#include <condition_variable> +#include <memory> +#include <mutex> +#include <thread> +#include <tuple> +#include <vector> + +#include <dvr/pose_client.h> +#include <pdx/file_handle.h> +#include <pdx/rpc/variant.h> +#include <private/dvr/buffer_hub_client.h> + +#include "acquired_buffer.h" +#include "display_surface.h" + +// Hardware composer HAL doesn't define HWC_TRANSFORM_NONE as of this writing. +#ifndef HWC_TRANSFORM_NONE +#define HWC_TRANSFORM_NONE static_cast<hwc_transform_t>(0) +#endif + +namespace android { +namespace dvr { + +// Basic display metrics for physical displays. Dimensions and densities are +// relative to the physical display orientation, which may be different from the +// logical display orientation exposed to applications. +struct HWCDisplayMetrics { + int width; + int height; + struct { + int x; + int y; + } dpi; + int vsync_period_ns; +}; + +// Layer represents the connection between a hardware composer layer and the +// source supplying buffers for the layer's contents. +class Layer { + public: + Layer() {} + + // Sets up the global state used by all Layer instances. This must be called + // before using any Layer methods. + static void InitializeGlobals(Hwc2::Composer* hwc2_hidl, + const HWCDisplayMetrics* metrics); + + // Releases any shared pointers and fence handles held by this instance. + void Reset(); + + // Sets up the layer to use a display surface as its content source. The Layer + // automatically handles ACQUIRE/RELEASE phases for the surface's buffer train + // every frame. + // + // |blending| receives HWC_BLENDING_* values. + // |transform| receives HWC_TRANSFORM_* values. + // |composition_type| receives either HWC_FRAMEBUFFER for most layers or + // HWC_FRAMEBUFFER_TARGET (unless you know what you are doing). + // |index| is the index of this surface in the DirectDisplaySurface array. + void Setup(const std::shared_ptr<DirectDisplaySurface>& surface, + HWC::BlendMode blending, HWC::Transform transform, + HWC::Composition composition_type, size_t z_roder); + + // Sets up the layer to use a direct buffer as its content source. No special + // handling of the buffer is performed; responsibility for updating or + // changing the buffer each frame is on the caller. + // + // |blending| receives HWC_BLENDING_* values. + // |transform| receives HWC_TRANSFORM_* values. + // |composition_type| receives either HWC_FRAMEBUFFER for most layers or + // HWC_FRAMEBUFFER_TARGET (unless you know what you are doing). + void Setup(const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending, + HWC::Transform transform, HWC::Composition composition_type, + size_t z_order); + + // Layers that use a direct IonBuffer should call this each frame to update + // which buffer will be used for the next PostLayers. + void UpdateBuffer(const std::shared_ptr<IonBuffer>& buffer); + + // Sets up the hardware composer layer for the next frame. When the layer is + // associated with a display surface, this method automatically ACQUIRES a new + // buffer if one is available. + void Prepare(); + + // After calling prepare, if this frame is to be dropped instead of passing + // along to the HWC, call Drop to close the contained fence(s). + void Drop(); + + // Performs fence bookkeeping after the frame has been posted to hardware + // composer. + void Finish(int release_fence_fd); + + // Sets the blending for the layer. |blending| receives HWC_BLENDING_* values. + void SetBlending(HWC::BlendMode blending); + + // Sets the z-order of this layer + void SetZOrder(size_t z_order); + + // Gets the current IonBuffer associated with this layer. Ownership of the + // buffer DOES NOT pass to the caller and the pointer is not guaranteed to + // remain valid across calls to Layer::Setup(), Layer::Prepare(), or + // Layer::Reset(). YOU HAVE BEEN WARNED. + IonBuffer* GetBuffer(); + + HWC::Composition GetCompositionType() const { return composition_type_; } + HWC::Layer GetLayerHandle() const { return hardware_composer_layer_; } + bool IsLayerSetup() const { return !source_.empty(); } + + // Applies all of the settings to this layer using the hwc functions + void UpdateLayerSettings(); + + int GetSurfaceId() const { + int surface_id = -1; + pdx::rpc::IfAnyOf<SourceSurface>::Call( + &source_, [&surface_id](const SourceSurface& surface_source) { + surface_id = surface_source.surface->surface_id(); + }); + return surface_id; + } + + private: + void CommonLayerSetup(); + + static Hwc2::Composer* hwc2_hidl_; + static const HWCDisplayMetrics* display_metrics_; + + // The hardware composer layer and metrics to use during the prepare cycle. + hwc2_layer_t hardware_composer_layer_ = 0; + + // Layer properties used to setup the hardware composer layer during the + // Prepare phase. + size_t z_order_ = 0; + HWC::BlendMode blending_ = HWC::BlendMode::None; + HWC::Transform transform_ = HWC::Transform::None; + HWC::Composition composition_type_ = HWC::Composition::Invalid; + HWC::Composition target_composition_type_ = HWC::Composition::Device; + + // State when the layer is connected to a surface. Provides the same interface + // as SourceBuffer to simplify internal use by Layer. + struct SourceSurface { + std::shared_ptr<DirectDisplaySurface> surface; + AcquiredBuffer acquired_buffer; + pdx::LocalHandle release_fence; + + SourceSurface(const std::shared_ptr<DirectDisplaySurface>& surface) + : surface(surface) {} + + // Attempts to acquire a new buffer from the surface and return a tuple with + // width, height, buffer handle, and fence. If a new buffer is not available + // the previous buffer is returned or an empty value if no buffer has ever + // been posted. When a new buffer is acquired the previous buffer's release + // fence is passed out automatically. + std::tuple<int, int, sp<GraphicBuffer>, pdx::LocalHandle> Acquire() { + if (surface->IsBufferAvailable()) { + acquired_buffer.Release(std::move(release_fence)); + acquired_buffer = surface->AcquireCurrentBuffer(); + ATRACE_ASYNC_END("BufferPost", acquired_buffer.buffer()->id()); + } + if (!acquired_buffer.IsEmpty()) { + return std::make_tuple(acquired_buffer.buffer()->width(), + acquired_buffer.buffer()->height(), + acquired_buffer.buffer()->buffer()->buffer(), + acquired_buffer.ClaimAcquireFence()); + } else { + return std::make_tuple(0, 0, nullptr, pdx::LocalHandle{}); + } + } + + void Finish(pdx::LocalHandle fence) { release_fence = std::move(fence); } + + // Gets a pointer to the current acquired buffer or returns nullptr if there + // isn't one. + IonBuffer* GetBuffer() { + if (acquired_buffer.IsAvailable()) + return acquired_buffer.buffer()->buffer(); + else + return nullptr; + } + + // Returns the surface id of the surface. + int GetSurfaceId() { return surface->surface_id(); } + }; + + // State when the layer is connected to a buffer. Provides the same interface + // as SourceSurface to simplify internal use by Layer. + struct SourceBuffer { + std::shared_ptr<IonBuffer> buffer; + + std::tuple<int, int, sp<GraphicBuffer>, pdx::LocalHandle> Acquire() { + if (buffer) + return std::make_tuple(buffer->width(), buffer->height(), + buffer->buffer(), pdx::LocalHandle{}); + else + return std::make_tuple(0, 0, nullptr, pdx::LocalHandle{}); + } + + void Finish(pdx::LocalHandle /*fence*/) {} + + IonBuffer* GetBuffer() { return buffer.get(); } + + int GetSurfaceId() const { return -1; } + }; + + // The underlying hardware composer layer is supplied buffers either from a + // surface buffer train or from a buffer directly. + pdx::rpc::Variant<SourceSurface, SourceBuffer> source_; + + pdx::LocalHandle acquire_fence_; + bool surface_rect_functions_applied_ = false; + + Layer(const Layer&) = delete; + void operator=(const Layer&) = delete; +}; + +// HardwareComposer encapsulates the hardware composer HAL, exposing a +// simplified API to post buffers to the display. +// +// HardwareComposer is accessed by both the vr flinger dispatcher thread and the +// surface flinger main thread, in addition to internally running a separate +// thread for compositing/EDS and posting layers to the HAL. When changing how +// variables are used or adding new state think carefully about which threads +// will access the state and whether it needs to be synchronized. +class HardwareComposer { + public: + // Type for vsync callback. + using VSyncCallback = std::function<void(int, int64_t, int64_t, uint32_t)>; + using RequestDisplayCallback = std::function<void(bool)>; + + // Since there is no universal way to query the number of hardware layers, + // just set it to 4 for now. + static constexpr size_t kMaxHardwareLayers = 4; + + HardwareComposer(); + HardwareComposer(Hwc2::Composer* hidl, + RequestDisplayCallback request_display_callback); + ~HardwareComposer(); + + bool Initialize(); + + bool IsInitialized() const { return initialized_; } + + // Start the post thread if there's work to do (i.e. visible layers). This + // should only be called from surface flinger's main thread. + void Enable(); + // Pause the post thread, blocking until the post thread has signaled that + // it's paused. This should only be called from surface flinger's main thread. + void Disable(); + + // Get the HMD display metrics for the current display. + display::Metrics GetHmdDisplayMetrics() const; + + HWC::Error GetDisplayAttribute(hwc2_display_t display, hwc2_config_t config, + hwc2_attribute_t attributes, + int32_t* out_value) const; + HWC::Error GetDisplayMetrics(hwc2_display_t display, hwc2_config_t config, + HWCDisplayMetrics* out_metrics) const; + std::string Dump(); + + void SetVSyncCallback(VSyncCallback callback); + + // Metrics of the logical display, which is always landscape. + int DisplayWidth() const { return display_metrics_.width; } + int DisplayHeight() const { return display_metrics_.height; } + HWCDisplayMetrics display_metrics() const { return display_metrics_; } + + // Metrics of the native display, which depends on the specific hardware + // implementation of the display. + HWCDisplayMetrics native_display_metrics() const { + return native_display_metrics_; + } + + // Sets the display surfaces to compose the hardware layer stack. + void SetDisplaySurfaces( + std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces); + + void OnHardwareComposerRefresh(); + + private: + int32_t EnableVsync(bool enabled); + + class ComposerCallback : public Hwc2::IComposerCallback { + public: + ComposerCallback() {} + + hardware::Return<void> onHotplug(Hwc2::Display /*display*/, + Connection /*connected*/) override { + // TODO(skiazyk): depending on how the server is implemented, we might + // have to set it up to synchronize with receiving this event, as it can + // potentially be a critical event for setting up state within the + // hwc2 module. That is, we (technically) should not call any other hwc + // methods until this method has been called after registering the + // callbacks. + return hardware::Void(); + } + + hardware::Return<void> onRefresh(Hwc2::Display /*display*/) override { + return hardware::Void(); + } + + hardware::Return<void> onVsync(Hwc2::Display /*display*/, + int64_t /*timestamp*/) override { + return hardware::Void(); + } + }; + + HWC::Error Validate(hwc2_display_t display); + HWC::Error Present(hwc2_display_t display); + + void SetBacklightBrightness(int brightness); + + void PostLayers(); + void PostThread(); + + // The post thread has two controlling states: + // 1. Idle: no work to do (no visible surfaces). + // 2. Suspended: explicitly halted (system is not in VR mode). + // When either #1 or #2 is true then the post thread is quiescent, otherwise + // it is active. + using PostThreadStateType = uint32_t; + struct PostThreadState { + enum : PostThreadStateType { + Active = 0, + Idle = (1 << 0), + Suspended = (1 << 1), + Quit = (1 << 2), + }; + }; + + void UpdatePostThreadState(uint32_t state, bool suspend); + + // Blocks until either event_fd becomes readable, or we're interrupted by a + // control thread. Any errors are returned as negative errno values. If we're + // interrupted, kPostThreadInterrupted will be returned. + int PostThreadPollInterruptible(const pdx::LocalHandle& event_fd, + int requested_events); + + // BlockUntilVSync, WaitForVSync, and SleepUntil are all blocking calls made + // on the post thread that can be interrupted by a control thread. If + // interrupted, these calls return kPostThreadInterrupted. + int ReadWaitPPState(); + int BlockUntilVSync(); + int ReadVSyncTimestamp(int64_t* timestamp); + int WaitForVSync(int64_t* timestamp); + int SleepUntil(int64_t wakeup_timestamp); + + bool IsFramePendingInDriver() { return ReadWaitPPState() == 1; } + + // Reconfigures the layer stack if the display surfaces changed since the last + // frame. Called only from the post thread. + bool UpdateLayerConfig(); + + // Called on the post thread when the post thread is resumed. + void OnPostThreadResumed(); + // Called on the post thread when the post thread is paused or quits. + void OnPostThreadPaused(); + + bool initialized_; + + // Hardware composer HAL device from SurfaceFlinger. VrFlinger does not own + // this pointer. + Hwc2::Composer* hwc2_hidl_; + RequestDisplayCallback request_display_callback_; + sp<ComposerCallback> callbacks_; + + // Display metrics of the physical display. + HWCDisplayMetrics native_display_metrics_; + // Display metrics of the logical display, adjusted so that orientation is + // landscape. + HWCDisplayMetrics display_metrics_; + // Transform required to get from native to logical display orientation. + HWC::Transform display_transform_ = HWC::Transform::None; + + // Pending surface list. Set by the display service when DirectSurfaces are + // added, removed, or change visibility. Written by the message dispatch + // thread and read by the post thread. + std::vector<std::shared_ptr<DirectDisplaySurface>> pending_surfaces_; + + // The surfaces displayed by the post thread. Used exclusively by the post + // thread. + std::vector<std::shared_ptr<DirectDisplaySurface>> display_surfaces_; + + // Layer array for handling buffer flow into hardware composer layers. + std::array<Layer, kMaxHardwareLayers> layers_; + size_t active_layer_count_ = 0; + + // Handler to hook vsync events outside of this class. + VSyncCallback vsync_callback_; + + // The layer posting thread. This thread wakes up a short time before vsync to + // hand buffers to hardware composer. + std::thread post_thread_; + + // Post thread state machine and synchronization primitives. + PostThreadStateType post_thread_state_{PostThreadState::Idle}; + std::atomic<bool> post_thread_quiescent_{true}; + bool post_thread_resumed_{false}; + pdx::LocalHandle post_thread_event_fd_; + std::mutex post_thread_mutex_; + std::condition_variable post_thread_wait_; + std::condition_variable post_thread_ready_; + + // Backlight LED brightness sysfs node. + pdx::LocalHandle backlight_brightness_fd_; + + // Primary display vsync event sysfs node. + pdx::LocalHandle primary_display_vsync_event_fd_; + + // Primary display wait_pingpong state sysfs node. + pdx::LocalHandle primary_display_wait_pp_fd_; + + // VSync sleep timerfd. + pdx::LocalHandle vsync_sleep_timer_fd_; + + // The timestamp of the last vsync. + int64_t last_vsync_timestamp_ = 0; + + // Vsync count since display on. + uint32_t vsync_count_ = 0; + + // Counter tracking the number of skipped frames. + int frame_skip_count_ = 0; + + // Fd array for tracking retire fences that are returned by hwc. This allows + // us to detect when the display driver begins queuing frames. + std::vector<pdx::LocalHandle> retire_fence_fds_; + + // Pose client for frame count notifications. Pose client predicts poses + // out to display frame boundaries, so we need to tell it about vsyncs. + DvrPose* pose_client_ = nullptr; + + static constexpr int kPostThreadInterrupted = 1; + + static void HwcRefresh(hwc2_callback_data_t data, hwc2_display_t display); + static void HwcVSync(hwc2_callback_data_t data, hwc2_display_t display, + int64_t timestamp); + static void HwcHotplug(hwc2_callback_data_t callbackData, + hwc2_display_t display, hwc2_connection_t connected); + + HardwareComposer(const HardwareComposer&) = delete; + void operator=(const HardwareComposer&) = delete; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_ diff --git a/libs/vr/libvrflinger/hwc_types.h b/libs/vr/libvrflinger/hwc_types.h new file mode 100644 index 0000000000..cbf636c2dc --- /dev/null +++ b/libs/vr/libvrflinger/hwc_types.h @@ -0,0 +1,300 @@ +#ifndef ANDROID_LIBVRFLINGER_HWCTYPES_H +#define ANDROID_LIBVRFLINGER_HWCTYPES_H + +// General HWC type support. Hardware composer type support is a bit of a mess +// between HWC1, HWC2 C/C++11, and HIDL types. Particularly bothersome is the +// use of enum classes, which make analogous types between versions much +// harder to deal with in a uniform way. +// +// These utilities help address some of these pains by providing a type-safe, +// flexible interface to translate between different type spaces. + +#define HWC2_INCLUDE_STRINGIFICATION +#define HWC2_USE_CPP11 +#include <hardware/hwcomposer2.h> +#undef HWC2_INCLUDE_STRINGIFICATION +#undef HWC2_USE_CPP11 + +#include <string> +#include <type_traits> + +namespace HWC { + +// Value types derived from HWC HAL types. Some of these are stand-alone, +// while others are also wrapped in translator classes below. +using ColorMode = int32_t; // android_color_mode_t; +using Config = hwc2_config_t; +using ColorTransform = + std::underlying_type<android_color_transform_t>::type; // int32_t; +using Dataspace = std::underlying_type<android_dataspace_t>::type; // int32_t; +using DisplayId = hwc2_display_t; +using DisplayRequest = std::underlying_type<HWC2::DisplayRequest>::type; +using Hdr = std::underlying_type<android_hdr_t>::type; // int32_t; +using Layer = hwc2_layer_t; +using PixelFormat = + std::underlying_type<android_pixel_format_t>::type; // int32_t; + +// Type traits and casting utilities. + +// SFINAE utility to evaluate type expressions. +template <typename...> +using TestTypeExpression = void; + +// Traits type to determine the underlying type of an enum, integer, +// or wrapper class. +template <typename T, typename = typename std::is_enum<T>::type, + typename = typename std::is_integral<T>::type, typename = void> +struct UnderlyingType { + using Type = T; +}; +// Partial specialization that matches enum types. Captures the underlying type +// of the enum in member type Type. +template <typename T> +struct UnderlyingType<T, std::true_type, std::false_type> { + using Type = typename std::underlying_type<T>::type; +}; +// Partial specialization that matches integral types. Captures the type of the +// integer in member type Type. +template <typename T> +struct UnderlyingType<T, std::false_type, std::true_type> { + using Type = T; +}; +// Partial specialization that matches the wrapper types below. Captures +// wrapper member type ValueType in member type Type. +template <typename T> +struct UnderlyingType<T, std::false_type, std::false_type, + TestTypeExpression<typename T::ValueType>> { + using Type = typename T::ValueType; +}; + +// Enable if T is an enum with underlying type U. +template <typename T, typename U, typename ReturnType = void> +using EnableIfMatchingEnum = typename std::enable_if< + std::is_enum<T>::value && + std::is_same<U, typename UnderlyingType<T>::Type>::value, + ReturnType>::type; + +// Enable if T and U are the same size/alignment and have the same underlying +// type. Handles enum, integral, and wrapper classes below. +template <typename T, typename U, typename Return = void> +using EnableIfSafeCast = typename std::enable_if< + sizeof(T) == sizeof(U) && alignof(T) == alignof(U) && + std::is_same<typename UnderlyingType<T>::Type, + typename UnderlyingType<U>::Type>::value, + Return>::type; + +// Safely cast between std::vectors of matching enum/integer/wraper types. +// Normally this is not possible with pendantic compiler type checks. However, +// given the same size, alignment, and underlying type this is safe due to +// allocator requirements and array-like element access guarantees. +template <typename T, typename U> +EnableIfSafeCast<T, U, std::vector<T>*> VectorCast(std::vector<U>* in) { + return reinterpret_cast<std::vector<T>*>(in); +} + +// Translator classes that wrap specific HWC types to make translating +// between different types (especially enum class) in code cleaner. + +// Base type for the enum wrappers below. This type provides type definitions +// and implicit conversion logic common to each wrapper type. +template <typename EnumType> +struct Wrapper { + // Alias type of this instantiantion of Wrapper. Useful for inheriting + // constructors in subclasses via "using Base::Base;" statements. + using Base = Wrapper<EnumType>; + + // The enum type wrapped by this instantiation of Wrapper. + using BaseType = EnumType; + + // The underlying type of the base enum type. + using ValueType = typename UnderlyingType<BaseType>::Type; + + // A default constructor is not defined here. Subclasses should define one + // as appropriate to define the correct inital value for the enum type. + + // Default copy constructor. + Wrapper(const Wrapper&) = default; + + // Implicit conversion from ValueType. + Wrapper(ValueType value) : value(value) {} + + // Implicit conversion from BaseType. + Wrapper(BaseType value) : value(static_cast<ValueType>(value)) {} + + // Implicit conversion from an enum type of the same underlying type. + template <typename T, typename = EnableIfMatchingEnum<T, ValueType>> + Wrapper(const T& value) : value(static_cast<ValueType>(value)) {} + + // Implicit conversion to BaseType. + operator BaseType() const { return static_cast<BaseType>(value); } + + // Implicit conversion to ValueType. + operator ValueType() const { return value; } + + template <typename T, typename = EnableIfMatchingEnum<T, ValueType>> + T cast() const { + return static_cast<T>(value); + } + + // Converts to string using HWC2 stringification of BaseType. + std::string to_string() const { + return HWC2::to_string(static_cast<BaseType>(value)); + } + + bool operator!=(const Wrapper& other) const { return value != other.value; } + bool operator!=(ValueType other_value) const { return value != other_value; } + bool operator!=(BaseType other_value) const { + return static_cast<BaseType>(value) != other_value; + } + bool operator==(const Wrapper& other) const { return value == other.value; } + bool operator==(ValueType other_value) const { return value == other_value; } + bool operator==(BaseType other_value) const { + return static_cast<BaseType>(value) == other_value; + } + + ValueType value; +}; + +struct Attribute final : public Wrapper<HWC2::Attribute> { + enum : ValueType { + Invalid = HWC2_ATTRIBUTE_INVALID, + Width = HWC2_ATTRIBUTE_WIDTH, + Height = HWC2_ATTRIBUTE_HEIGHT, + VsyncPeriod = HWC2_ATTRIBUTE_VSYNC_PERIOD, + DpiX = HWC2_ATTRIBUTE_DPI_X, + DpiY = HWC2_ATTRIBUTE_DPI_Y, + }; + + Attribute() : Base(Invalid) {} + using Base::Base; +}; + +struct BlendMode final : public Wrapper<HWC2::BlendMode> { + enum : ValueType { + Invalid = HWC2_BLEND_MODE_INVALID, + None = HWC2_BLEND_MODE_NONE, + Premultiplied = HWC2_BLEND_MODE_PREMULTIPLIED, + Coverage = HWC2_BLEND_MODE_COVERAGE, + }; + + BlendMode() : Base(Invalid) {} + using Base::Base; +}; + +struct Composition final : public Wrapper<HWC2::Composition> { + enum : ValueType { + Invalid = HWC2_COMPOSITION_INVALID, + Client = HWC2_COMPOSITION_CLIENT, + Device = HWC2_COMPOSITION_DEVICE, + SolidColor = HWC2_COMPOSITION_SOLID_COLOR, + Cursor = HWC2_COMPOSITION_CURSOR, + Sideband = HWC2_COMPOSITION_SIDEBAND, + }; + + Composition() : Base(Invalid) {} + using Base::Base; +}; + +struct DisplayType final : public Wrapper<HWC2::DisplayType> { + enum : ValueType { + Invalid = HWC2_DISPLAY_TYPE_INVALID, + Physical = HWC2_DISPLAY_TYPE_PHYSICAL, + Virtual = HWC2_DISPLAY_TYPE_VIRTUAL, + }; + + DisplayType() : Base(Invalid) {} + using Base::Base; +}; + +struct Error final : public Wrapper<HWC2::Error> { + enum : ValueType { + None = HWC2_ERROR_NONE, + BadConfig = HWC2_ERROR_BAD_CONFIG, + BadDisplay = HWC2_ERROR_BAD_DISPLAY, + BadLayer = HWC2_ERROR_BAD_LAYER, + BadParameter = HWC2_ERROR_BAD_PARAMETER, + HasChanges = HWC2_ERROR_HAS_CHANGES, + NoResources = HWC2_ERROR_NO_RESOURCES, + NotValidated = HWC2_ERROR_NOT_VALIDATED, + Unsupported = HWC2_ERROR_UNSUPPORTED, + }; + + Error() : Base(None) {} + using Base::Base; +}; + +struct LayerRequest final : public Wrapper<HWC2::LayerRequest> { + enum : ValueType { + ClearClientTarget = HWC2_LAYER_REQUEST_CLEAR_CLIENT_TARGET, + }; + + LayerRequest() : Base(0) {} + using Base::Base; +}; + +struct PowerMode final : public Wrapper<HWC2::PowerMode> { + enum : ValueType { + Off = HWC2_POWER_MODE_OFF, + DozeSuspend = HWC2_POWER_MODE_DOZE_SUSPEND, + Doze = HWC2_POWER_MODE_DOZE, + On = HWC2_POWER_MODE_ON, + }; + + PowerMode() : Base(Off) {} + using Base::Base; +}; + +struct Transform final : public Wrapper<HWC2::Transform> { + enum : ValueType { + None = 0, + FlipH = HWC_TRANSFORM_FLIP_H, + FlipV = HWC_TRANSFORM_FLIP_V, + Rotate90 = HWC_TRANSFORM_ROT_90, + Rotate180 = HWC_TRANSFORM_ROT_180, + Rotate270 = HWC_TRANSFORM_ROT_270, + FlipHRotate90 = HWC_TRANSFORM_FLIP_H_ROT_90, + FlipVRotate90 = HWC_TRANSFORM_FLIP_V_ROT_90, + }; + + Transform() : Base(None) {} + using Base::Base; +}; + +struct Vsync final : public Wrapper<HWC2::Vsync> { + enum : ValueType { + Invalid = HWC2_VSYNC_INVALID, + Enable = HWC2_VSYNC_ENABLE, + Disable = HWC2_VSYNC_DISABLE, + }; + + Vsync() : Base(Invalid) {} + using Base::Base; +}; + +// Utility color type. +struct Color final { + Color(const Color&) = default; + Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a) {} + Color(hwc_color_t color) : r(color.r), g(color.g), b(color.b), a(color.a) {} + + operator hwc_color_t() const { return {r, g, b, a}; } + + uint8_t r __attribute__((aligned(1))); + uint8_t g __attribute__((aligned(1))); + uint8_t b __attribute__((aligned(1))); + uint8_t a __attribute__((aligned(1))); +}; + +// Utility rectangle type. +struct Rect final { + // TODO(eieio): Implicit conversion to/from Android rect types. + + int32_t left __attribute__((aligned(4))); + int32_t top __attribute__((aligned(4))); + int32_t right __attribute__((aligned(4))); + int32_t bottom __attribute__((aligned(4))); +}; + +} // namespace HWC + +#endif // ANDROID_LIBVRFLINGER_HWCTYPES_H diff --git a/libs/vr/libvrflinger/include/dvr/vr_flinger.h b/libs/vr/libvrflinger/include/dvr/vr_flinger.h new file mode 100644 index 0000000000..145852e949 --- /dev/null +++ b/libs/vr/libvrflinger/include/dvr/vr_flinger.h @@ -0,0 +1,60 @@ +#ifndef ANDROID_DVR_VR_FLINGER_H_ +#define ANDROID_DVR_VR_FLINGER_H_ + +#include <thread> +#include <memory> + +#include <pdx/default_transport/service_dispatcher.h> +#include <vr/vr_manager/vr_manager.h> + +namespace android { + +namespace Hwc2 { +class Composer; +} // namespace Hwc2 + +namespace dvr { + +class DisplayService; + +class VrFlinger { + public: + using RequestDisplayCallback = std::function<void(bool)>; + static std::unique_ptr<VrFlinger> Create( + Hwc2::Composer* hidl, RequestDisplayCallback request_display_callback); + ~VrFlinger(); + + // These functions are all called on surface flinger's main thread. + void OnBootFinished(); + void GrantDisplayOwnership(); + void SeizeDisplayOwnership(); + + // Called on a binder thread. + void OnHardwareComposerRefresh(); + + private: + VrFlinger(); + bool Init(Hwc2::Composer* hidl, + RequestDisplayCallback request_display_callback); + + // Needs to be a separate class for binder's ref counting + class PersistentVrStateCallback : public BnPersistentVrStateCallbacks { + public: + PersistentVrStateCallback(RequestDisplayCallback request_display_callback) + : request_display_callback_(request_display_callback) {} + void onPersistentVrStateChanged(bool enabled) override; + private: + RequestDisplayCallback request_display_callback_; + }; + + std::thread dispatcher_thread_; + std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher_; + std::shared_ptr<android::dvr::DisplayService> display_service_; + sp<PersistentVrStateCallback> persistent_vr_state_callback_; + RequestDisplayCallback request_display_callback_; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_VR_FLINGER_H_ diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp new file mode 100644 index 0000000000..b2dc1d86e8 --- /dev/null +++ b/libs/vr/libvrflinger/vr_flinger.cpp @@ -0,0 +1,151 @@ +#include <dvr/vr_flinger.h> + +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <signal.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <memory> + +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <cutils/properties.h> +#include <cutils/sched_policy.h> +#include <log/log.h> +#include <private/dvr/display_client.h> +#include <sys/prctl.h> +#include <sys/resource.h> + +#include <pdx/default_transport/service_dispatcher.h> + +#include <functional> + +#include "DisplayHardware/ComposerHal.h" +#include "display_manager_service.h" +#include "display_service.h" +#include "vsync_service.h" + +namespace android { +namespace dvr { + +std::unique_ptr<VrFlinger> VrFlinger::Create( + Hwc2::Composer* hidl, RequestDisplayCallback request_display_callback) { + std::unique_ptr<VrFlinger> vr_flinger(new VrFlinger); + if (vr_flinger->Init(hidl, request_display_callback)) + return vr_flinger; + else + return nullptr; +} + +VrFlinger::VrFlinger() {} + +VrFlinger::~VrFlinger() { + if (persistent_vr_state_callback_.get()) { + sp<IVrManager> vr_manager = interface_cast<IVrManager>( + defaultServiceManager()->checkService(String16("vrmanager"))); + if (vr_manager.get()) { + vr_manager->unregisterPersistentVrStateListener( + persistent_vr_state_callback_); + } + } + + if (dispatcher_) + dispatcher_->SetCanceled(true); + if (dispatcher_thread_.joinable()) + dispatcher_thread_.join(); +} + +bool VrFlinger::Init(Hwc2::Composer* hidl, + RequestDisplayCallback request_display_callback) { + if (!hidl || !request_display_callback) + return false; + + std::shared_ptr<android::pdx::Service> service; + + ALOGI("Starting up VrFlinger..."); + + setpriority(PRIO_PROCESS, 0, android::PRIORITY_URGENT_DISPLAY); + set_sched_policy(0, SP_FOREGROUND); + + // We need to be able to create endpoints with full perms. + umask(0000); + + android::ProcessState::self()->startThreadPool(); + + request_display_callback_ = request_display_callback; + + dispatcher_ = android::pdx::default_transport::ServiceDispatcher::Create(); + CHECK_ERROR(!dispatcher_, error, "Failed to create service dispatcher."); + + display_service_ = + android::dvr::DisplayService::Create(hidl, request_display_callback); + CHECK_ERROR(!display_service_, error, "Failed to create display service."); + dispatcher_->AddService(display_service_); + + service = android::dvr::DisplayManagerService::Create(display_service_); + CHECK_ERROR(!service, error, "Failed to create display manager service."); + dispatcher_->AddService(service); + + service = android::dvr::VSyncService::Create(); + CHECK_ERROR(!service, error, "Failed to create vsync service."); + dispatcher_->AddService(service); + + display_service_->SetVSyncCallback( + std::bind(&android::dvr::VSyncService::VSyncEvent, + std::static_pointer_cast<android::dvr::VSyncService>(service), + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, std::placeholders::_4)); + + dispatcher_thread_ = std::thread([this]() { + prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrDispatch"), 0, 0, 0); + ALOGI("Entering message loop."); + + int ret = dispatcher_->EnterDispatchLoop(); + if (ret < 0) { + ALOGE("Dispatch loop exited because: %s\n", strerror(-ret)); + } + }); + + return true; + +error: + return false; +} + +void VrFlinger::OnBootFinished() { + sp<IVrManager> vr_manager = interface_cast<IVrManager>( + defaultServiceManager()->checkService(String16("vrmanager"))); + if (vr_manager.get()) { + persistent_vr_state_callback_ = + new PersistentVrStateCallback(request_display_callback_); + vr_manager->registerPersistentVrStateListener( + persistent_vr_state_callback_); + } else { + ALOGE("Unable to register vr flinger for persistent vr mode changes"); + } +} + +void VrFlinger::GrantDisplayOwnership() { + display_service_->GrantDisplayOwnership(); +} + +void VrFlinger::SeizeDisplayOwnership() { + display_service_->SeizeDisplayOwnership(); +} + +void VrFlinger::OnHardwareComposerRefresh() { + display_service_->OnHardwareComposerRefresh(); +} + +void VrFlinger::PersistentVrStateCallback::onPersistentVrStateChanged( + bool enabled) { + ALOGV("Notified persistent vr mode is %s", enabled ? "on" : "off"); + // TODO(eieio): Determine the correct signal to request display control. + // Persistent VR mode is not enough. + // request_display_callback_(enabled); +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libvrflinger/vsync_service.cpp b/libs/vr/libvrflinger/vsync_service.cpp new file mode 100644 index 0000000000..2a83933470 --- /dev/null +++ b/libs/vr/libvrflinger/vsync_service.cpp @@ -0,0 +1,213 @@ +#include "vsync_service.h" + +#include <hardware/hwcomposer.h> +#include <log/log.h> +#include <poll.h> +#include <sys/prctl.h> +#include <time.h> +#include <utils/Trace.h> + +#include <dvr/dvr_display_types.h> +#include <pdx/default_transport/service_endpoint.h> +#include <private/dvr/clock_ns.h> +#include <private/dvr/display_protocol.h> + +using android::dvr::display::VSyncProtocol; +using android::dvr::display::VSyncSchedInfo; +using android::pdx::Channel; +using android::pdx::Message; +using android::pdx::MessageInfo; +using android::pdx::default_transport::Endpoint; +using android::pdx::rpc::DispatchRemoteMethod; + +namespace android { +namespace dvr { + +VSyncService::VSyncService() + : BASE("VSyncService", Endpoint::Create(VSyncProtocol::kClientPath)), + last_vsync_(0), + current_vsync_(0), + compositor_time_ns_(0), + current_vsync_count_(0) {} + +VSyncService::~VSyncService() {} + +void VSyncService::VSyncEvent(int display, int64_t timestamp_ns, + int64_t compositor_time_ns, + uint32_t vsync_count) { + ATRACE_NAME("VSyncService::VSyncEvent"); + std::lock_guard<std::mutex> autolock(mutex_); + + if (display == HWC_DISPLAY_PRIMARY) { + last_vsync_ = current_vsync_; + current_vsync_ = timestamp_ns; + compositor_time_ns_ = compositor_time_ns; + current_vsync_count_ = vsync_count; + + NotifyWaiters(); + UpdateClients(); + } +} + +std::shared_ptr<Channel> VSyncService::OnChannelOpen(pdx::Message& message) { + const MessageInfo& info = message.GetInfo(); + + auto client = std::make_shared<VSyncChannel>(*this, info.pid, info.cid); + AddClient(client); + + return client; +} + +void VSyncService::OnChannelClose(pdx::Message& /*message*/, + const std::shared_ptr<Channel>& channel) { + auto client = std::static_pointer_cast<VSyncChannel>(channel); + if (!client) { + ALOGW("WARNING: VSyncChannel was NULL!!!\n"); + return; + } + + RemoveClient(client); +} + +void VSyncService::AddWaiter(pdx::Message& message) { + std::lock_guard<std::mutex> autolock(mutex_); + std::unique_ptr<VSyncWaiter> waiter(new VSyncWaiter(message)); + waiters_.push_back(std::move(waiter)); +} + +void VSyncService::AddClient(const std::shared_ptr<VSyncChannel>& client) { + std::lock_guard<std::mutex> autolock(mutex_); + clients_.push_back(client); +} + +void VSyncService::RemoveClient(const std::shared_ptr<VSyncChannel>& client) { + std::lock_guard<std::mutex> autolock(mutex_); + clients_.remove(client); +} + +// Private. Assumes mutex is held. +void VSyncService::NotifyWaiters() { + ATRACE_NAME("VSyncService::NotifyWaiters"); + auto first = waiters_.begin(); + auto last = waiters_.end(); + + while (first != last) { + (*first)->Notify(current_vsync_); + waiters_.erase(first++); + } +} + +// Private. Assumes mutex is held. +void VSyncService::UpdateClients() { + ATRACE_NAME("VSyncService::UpdateClients"); + auto first = clients_.begin(); + auto last = clients_.end(); + + while (first != last) { + (*first)->Signal(); + first++; + } +} + +pdx::Status<void> VSyncService::HandleMessage(pdx::Message& message) { + switch (message.GetOp()) { + case VSyncProtocol::Wait::Opcode: + AddWaiter(message); + return {}; + + case VSyncProtocol::GetLastTimestamp::Opcode: + DispatchRemoteMethod<VSyncProtocol::GetLastTimestamp>( + *this, &VSyncService::OnGetLastTimestamp, message); + return {}; + + case VSyncProtocol::GetSchedInfo::Opcode: + DispatchRemoteMethod<VSyncProtocol::GetSchedInfo>( + *this, &VSyncService::OnGetSchedInfo, message); + return {}; + + case VSyncProtocol::Acknowledge::Opcode: + DispatchRemoteMethod<VSyncProtocol::Acknowledge>( + *this, &VSyncService::OnAcknowledge, message); + return {}; + + default: + return Service::HandleMessage(message); + } +} + +pdx::Status<int64_t> VSyncService::OnGetLastTimestamp(pdx::Message& message) { + auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel()); + std::lock_guard<std::mutex> autolock(mutex_); + + // Getting the timestamp has the side effect of ACKing. + client->Ack(); + return {current_vsync_}; +} + +pdx::Status<VSyncSchedInfo> VSyncService::OnGetSchedInfo( + pdx::Message& message) { + auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel()); + std::lock_guard<std::mutex> autolock(mutex_); + + // Getting the timestamp has the side effect of ACKing. + client->Ack(); + + uint32_t next_vsync_count = current_vsync_count_ + 1; + int64_t current_time = GetSystemClockNs(); + int64_t vsync_period_ns = 0; + int64_t next_warp; + if (current_vsync_ == 0 || last_vsync_ == 0) { + // Handle startup when current_vsync_ or last_vsync_ are 0. + // Normally should not happen because vsync_service is running before + // applications, but in case it does a sane time prevents applications + // from malfunctioning. + vsync_period_ns = 20000000; + next_warp = current_time; + } else { + // TODO(jbates) When we have an accurate reading of the true vsync + // period, use that instead of this estimated value. + vsync_period_ns = current_vsync_ - last_vsync_; + // Clamp the period, because when there are no surfaces the last_vsync_ + // value will get stale. Note this is temporary and goes away as soon + // as we have an accurate vsync period reported by the system. + vsync_period_ns = std::min(vsync_period_ns, INT64_C(20000000)); + next_warp = current_vsync_ + vsync_period_ns - compositor_time_ns_; + // If the request missed the present window, move up to the next vsync. + if (current_time > next_warp) { + next_warp += vsync_period_ns; + ++next_vsync_count; + } + } + + return {{vsync_period_ns, next_warp, next_vsync_count}}; +} + +pdx::Status<void> VSyncService::OnAcknowledge(pdx::Message& message) { + auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel()); + std::lock_guard<std::mutex> autolock(mutex_); + client->Ack(); + return {}; +} + +void VSyncWaiter::Notify(int64_t timestamp) { + timestamp_ = timestamp; + DispatchRemoteMethod<VSyncProtocol::Wait>(*this, &VSyncWaiter::OnWait, + message_); +} + +pdx::Status<int64_t> VSyncWaiter::OnWait(pdx::Message& /*message*/) { + return {timestamp_}; +} + +void VSyncChannel::Ack() { + ALOGD_IF(TRACE, "VSyncChannel::Ack: pid=%d cid=%d\n", pid_, cid_); + service_.ModifyChannelEvents(cid_, POLLPRI, 0); +} + +void VSyncChannel::Signal() { + ALOGD_IF(TRACE, "VSyncChannel::Signal: pid=%d cid=%d\n", pid_, cid_); + service_.ModifyChannelEvents(cid_, 0, POLLPRI); +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libvrflinger/vsync_service.h b/libs/vr/libvrflinger/vsync_service.h new file mode 100644 index 0000000000..215948eb05 --- /dev/null +++ b/libs/vr/libvrflinger/vsync_service.h @@ -0,0 +1,106 @@ +#ifndef ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_ +#define ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_ + +#include <pdx/service.h> + +#include <list> +#include <memory> +#include <mutex> +#include <thread> + +#include "display_service.h" + +namespace android { +namespace dvr { + +// VSyncWaiter encapsulates a client blocked waiting for the next vsync. +// It is used to enqueue the Message to reply to when the next vsync event +// occurs. +class VSyncWaiter { + public: + explicit VSyncWaiter(pdx::Message& message) : message_(std::move(message)) {} + + void Notify(int64_t timestamp); + + private: + pdx::Status<int64_t> OnWait(pdx::Message& message); + + pdx::Message message_; + int64_t timestamp_ = 0; + + VSyncWaiter(const VSyncWaiter&) = delete; + void operator=(const VSyncWaiter&) = delete; +}; + +// VSyncChannel manages the service-side per-client context for each client +// using the service. +class VSyncChannel : public pdx::Channel { + public: + VSyncChannel(pdx::Service& service, int pid, int cid) + : service_(service), pid_(pid), cid_(cid) {} + + void Ack(); + void Signal(); + + private: + pdx::Service& service_; + pid_t pid_; + int cid_; + + VSyncChannel(const VSyncChannel&) = delete; + void operator=(const VSyncChannel&) = delete; +}; + +// VSyncService implements the displayd vsync service over ServiceFS. +class VSyncService : public pdx::ServiceBase<VSyncService> { + public: + ~VSyncService() override; + + pdx::Status<void> HandleMessage(pdx::Message& message) override; + + std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override; + void OnChannelClose(pdx::Message& message, + const std::shared_ptr<pdx::Channel>& channel) override; + + // Called by the hardware composer HAL, or similar, whenever a vsync event + // occurs. |compositor_time_ns| is the number of ns before the next vsync when + // the compositor will preempt the GPU to do EDS and lens warp. + void VSyncEvent(int display, int64_t timestamp_ns, int64_t compositor_time_ns, + uint32_t vsync_count); + + private: + friend BASE; + + VSyncService(); + + pdx::Status<int64_t> OnGetLastTimestamp(pdx::Message& message); + pdx::Status<display::VSyncSchedInfo> OnGetSchedInfo(pdx::Message& message); + pdx::Status<void> OnAcknowledge(pdx::Message& message); + + void NotifierThreadFunction(); + + void AddWaiter(pdx::Message& message); + void NotifyWaiters(); + void UpdateClients(); + + void AddClient(const std::shared_ptr<VSyncChannel>& client); + void RemoveClient(const std::shared_ptr<VSyncChannel>& client); + + int64_t last_vsync_; + int64_t current_vsync_; + int64_t compositor_time_ns_; + uint32_t current_vsync_count_; + + std::mutex mutex_; + + std::list<std::unique_ptr<VSyncWaiter>> waiters_; + std::list<std::shared_ptr<VSyncChannel>> clients_; + + VSyncService(const VSyncService&) = delete; + void operator=(VSyncService&) = delete; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_ diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp new file mode 100644 index 0000000000..abad78b338 --- /dev/null +++ b/libs/vr/libvrsensor/Android.bp @@ -0,0 +1,48 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +sourceFiles = [ + "pose_client.cpp", + "sensor_client.cpp", + "latency_model.cpp", +] + +includeFiles = [ + "include", +] + +staticLibraries = [ + "libbufferhub", + "libbufferhubqueue", + "libdvrcommon", + "libpdx_default_transport", +] + +sharedLibraries = [ + "libbase", + "libcutils", + "libhardware", + "liblog", + "libutils", + "libui", +] + +cc_library { + srcs: sourceFiles, + export_include_dirs: includeFiles, + static_libs: staticLibraries, + shared_libs: sharedLibraries, + name: "libvrsensor", +} + diff --git a/libs/vr/libvrsensor/include/CPPLINT.cfg b/libs/vr/libvrsensor/include/CPPLINT.cfg new file mode 100644 index 0000000000..2f8a3c018c --- /dev/null +++ b/libs/vr/libvrsensor/include/CPPLINT.cfg @@ -0,0 +1 @@ +filter=-build/header_guard diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h new file mode 100644 index 0000000000..ed75f84216 --- /dev/null +++ b/libs/vr/libvrsensor/include/dvr/pose_client.h @@ -0,0 +1,205 @@ +#ifndef ANDROID_DVR_POSE_CLIENT_H_ +#define ANDROID_DVR_POSE_CLIENT_H_ + +#ifdef __ARM_NEON +#include <arm_neon.h> +#else +#ifndef __FLOAT32X4T_86 +#define __FLOAT32X4T_86 +typedef float float32x4_t __attribute__ ((__vector_size__ (16))); +typedef struct float32x4x4_t { float32x4_t val[4]; }; +#endif +#endif + +#include <stdbool.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DvrPose DvrPose; + +// Represents the current state provided by the pose service, containing a +// rotation and translation. +typedef struct __attribute__((packed, aligned(8))) DvrPoseState { + // A quaternion representing the rotation of the HMD in Start Space. + struct __attribute__((packed)) { + float x, y, z, w; + } head_from_start_rotation; + // The position of the HMD in Start Space. + struct __attribute__((packed)) { + float x, y, z; + } head_from_start_translation; + // Time in nanoseconds for the current pose. + uint64_t timestamp_ns; + // The rotational velocity of the HMD. + struct __attribute__((packed)) { + float x, y, z; + } sensor_from_start_rotation_velocity; +} DvrPoseState; + +enum { + DVR_POSE_FLAG_VALID = (1UL << 0), // This pose is valid. + DVR_POSE_FLAG_HEAD = (1UL << 1), // This pose is the head. + DVR_POSE_FLAG_CONTROLLER = (1UL << 2), // This pose is a controller. +}; + +// Represents an estimated pose, accessed asynchronously through a shared ring +// buffer. No assumptions should be made about the data in padding space. +// The size of this struct is 128 bytes. +typedef struct __attribute__((packed, aligned(16))) DvrPoseAsync { + // Left eye head-from-start orientation quaternion x,y,z,w. + float32x4_t orientation; + // Left eye head-from-start translation x,y,z,pad in meters. + float32x4_t translation; + // Right eye head-from-start orientation quaternion x,y,z,w. + float32x4_t right_orientation; + // Right eye head-from-start translation x,y,z,pad in meters. + float32x4_t right_translation; + // Start-space angular velocity x,y,z,pad in radians per second. + float32x4_t angular_velocity; + // Start-space positional velocity x,y,z,pad in meters per second. + float32x4_t velocity; + // Timestamp of when this pose is predicted for, typically halfway through + // scanout. + int64_t timestamp_ns; + // Bitmask of DVR_POSE_FLAG_* constants that apply to this pose. + // + // If DVR_POSE_FLAG_VALID is not set, the pose is indeterminate. + uint64_t flags; + // Reserved padding to 128 bytes. + uint8_t pad[16]; +} DvrPoseAsync; + +// Returned by the async pose ring buffer access API. +typedef struct DvrPoseRingBufferInfo { + // Read-only pointer to the pose ring buffer. The current pose is in this + // buffer at element buffer[current_frame & (buffer_size - 1)]. The next + // frame's forecasted pose is at element + // ((current_frame + 1) & (buffer_size - 1)). And so on. The poses are + // predicted for when 50% of the corresponding frame's pixel data is visible + // to the user. + // The last value returned by dvrPresent is the count for the next frame, + // which is the earliest that the application could display something if they + // were to render promptly. (TODO(jbates) move this comment to dvrPresent). + volatile const DvrPoseAsync* buffer; + // Minimum number of accurate forecasted poses including the current frame's + // pose. This is the number of poses that are udpated by the pose service. + // If the application reads past this count, they will get a stale prediction + // from a previous frame. Guaranteed to be at least 2. + uint32_t min_future_count; + // Number of elements in buffer. At least 8 and greater than min_future_count. + // Guaranteed to be a power of two. The total size of the buffer in bytes is: + // total_count * sizeof(DvrPoseAsync) + uint32_t total_count; +} DvrPoseRingBufferInfo; + +typedef enum DvrPoseMode { + DVR_POSE_MODE_6DOF = 0, + DVR_POSE_MODE_3DOF, + DVR_POSE_MODE_MOCK_FROZEN, + DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW, + DVR_POSE_MODE_MOCK_HEAD_TURN_FAST, + DVR_POSE_MODE_MOCK_ROTATE_SLOW, + DVR_POSE_MODE_MOCK_ROTATE_MEDIUM, + DVR_POSE_MODE_MOCK_ROTATE_FAST, + DVR_POSE_MODE_MOCK_CIRCLE_STRAFE, + + // Always last. + DVR_POSE_MODE_COUNT, +} DvrPoseMode; + +typedef enum DvrControllerId { + DVR_CONTROLLER_0 = 0, + DVR_CONTROLLER_1 = 1, +} DvrControllerId; + +// Creates a new pose client. +// +// @return Pointer to the created pose client, nullptr on failure. +DvrPose* dvrPoseCreate(); + +// Destroys a pose client. +// +// @param client Pointer to the pose client to be destroyed. +void dvrPoseDestroy(DvrPose* client); + +// Gets the pose for the given vsync count. +// +// @param client Pointer to the pose client. +// @param vsync_count Vsync that this pose should be forward-predicted to. +// Typically this is the count returned by dvrGetNextVsyncCount. +// @param out_pose Struct to store pose state. +// @return Zero on success, negative error code on failure. +int dvrPoseGet(DvrPose* client, uint32_t vsync_count, DvrPoseAsync* out_pose); + +// Gets the current vsync count. +uint32_t dvrPoseGetVsyncCount(DvrPose* client); + +// Gets the pose for the given controller at the given vsync count. +// +// @param client Pointer to the pose client. +// @param controller_id The controller id. +// @param vsync_count Vsync that this pose should be forward-predicted to. +// Typically this is the count returned by dvrGetNextVsyncCount. +// @param out_pose Struct to store pose state. +// @return Zero on success, negative error code on failure. +int dvrPoseGetController(DvrPose* client, int32_t controller_id, + uint32_t vsync_count, DvrPoseAsync* out_pose); + +// Enables/disables logging for the controller fusion. +// +// @param client Pointer to the pose client. +// @param enable True starts logging, False stops. +// @return Zero on success, negative error code on failure. +int dvrPoseLogController(DvrPose* client, bool enable); + +// DEPRECATED +// Polls current pose state. +// +// @param client Pointer to the pose client. +// @param state Struct to store polled state. +// @return Zero on success, negative error code on failure. +int dvrPosePoll(DvrPose* client, DvrPoseState* state); + +// Freezes the pose to the provided state. +// +// Future poll operations will return this state until a different state is +// frozen or dvrPoseSetMode() is called with a different mode. The timestamp is +// not frozen. +// +// @param client Pointer to the pose client. +// @param frozen_state State pose to be frozen to. +// @return Zero on success, negative error code on failure. +int dvrPoseFreeze(DvrPose* client, const DvrPoseState* frozen_state); + +// Sets the pose service mode. +// +// @param mode The requested pose mode. +// @return Zero on success, negative error code on failure. +int dvrPoseSetMode(DvrPose* client, DvrPoseMode mode); + +// Gets the pose service mode. +// +// @param mode Return value for the current pose mode. +// @return Zero on success, negative error code on failure. +int dvrPoseGetMode(DvrPose* client, DvrPoseMode* mode); + +// Get access to the shared memory pose ring buffer. +// A future pose at vsync <current> + <offset> is accessed at index: +// index = (<current> + <offset>) % out_buffer_size +// Where <current> was the last value returned by dvrPresent and +// <offset> is less than or equal to |out_min_future_count|. +// |out_buffer| will be set to a pointer to the buffer. +// |out_fd| will be set to the gralloc buffer file descriptor, which is +// required for binding this buffer for GPU use. +// Returns 0 on success. +int dvrPoseGetRingBuffer(DvrPose* client, DvrPoseRingBufferInfo* out_info); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ANDROID_DVR_POSE_CLIENT_H_ diff --git a/libs/vr/libvrsensor/include/private/dvr/latency_model.h b/libs/vr/libvrsensor/include/private/dvr/latency_model.h new file mode 100644 index 0000000000..40b4638d69 --- /dev/null +++ b/libs/vr/libvrsensor/include/private/dvr/latency_model.h @@ -0,0 +1,29 @@ +#ifndef ANDROID_DVR_LATENCY_MODEL_H_ +#define ANDROID_DVR_LATENCY_MODEL_H_ + +#include <vector> + +namespace android { +namespace dvr { + +// This class models the latency from sensors. It will look at the first +// window_size measurements and return their average after that. +class LatencyModel { + public: + LatencyModel(size_t window_size); + ~LatencyModel() = default; + + void AddLatency(int64_t latency_ns); + int64_t CurrentLatencyEstimate() const { return latency_; } + + private: + size_t window_size_; + int64_t latency_sum_ = 0; + size_t num_summed_ = 0; + int64_t latency_ = 0; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_LATENCY_MODEL_H_ diff --git a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h new file mode 100644 index 0000000000..0616d46106 --- /dev/null +++ b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h @@ -0,0 +1,28 @@ +#ifndef ANDROID_DVR_POSE_IPC_H_ +#define ANDROID_DVR_POSE_IPC_H_ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define DVR_POSE_SERVICE_BASE "system/vr/pose" +#define DVR_POSE_SERVICE_CLIENT (DVR_POSE_SERVICE_BASE "/client") + +enum { + DVR_POSE_POLL = 0, + DVR_POSE_FREEZE, + DVR_POSE_SET_MODE, + DVR_POSE_GET_RING_BUFFER, + DVR_POSE_NOTIFY_VSYNC, + DVR_POSE_GET_MODE, + DVR_POSE_GET_CONTROLLER_RING_BUFFER, + DVR_POSE_LOG_CONTROLLER, +}; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ANDROID_DVR_POSE_IPC_H_ diff --git a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h new file mode 100644 index 0000000000..66c4c7c49d --- /dev/null +++ b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h @@ -0,0 +1,43 @@ +#ifndef ANDROID_DVR_POSE_CLIENT_INTERNAL_H_ +#define ANDROID_DVR_POSE_CLIENT_INTERNAL_H_ + +#include <stdint.h> + +#include <dvr/pose_client.h> +#include <pdx/file_handle.h> +#include <private/dvr/sensor_constants.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// Sensord head pose ring buffer. +typedef struct __attribute__((packed, aligned(16))) DvrPoseRingBuffer { + // Ring buffer always at the beginning of the structure, as consumers may + // not have access to this parent structure definition. + DvrPoseAsync ring[kPoseAsyncBufferTotalCount]; + // Current vsync_count (where sensord is writing poses from). + uint32_t vsync_count; +} DvrPoseMetadata; + +// Called by displayd to give vsync count info to the pose service. +// |display_timestamp| Display timestamp is in the middle of scanout. +// |display_period_ns| Nanos between vsyncs. +// |right_eye_photon_offset_ns| Nanos to shift the prediction timestamp for +// the right eye head pose (relative to the left eye prediction). +int privateDvrPoseNotifyVsync(DvrPose* client, uint32_t vsync_count, + int64_t display_timestamp, + int64_t display_period_ns, + int64_t right_eye_photon_offset_ns); + +// Get file descriptor for access to the shared memory pose buffer. This can be +// used with GL extensions that support shared memory buffer objects. The caller +// takes ownership of the returned fd and must close it or pass on ownership. +int privateDvrPoseGetRingBufferFd(DvrPose* client, + android::pdx::LocalHandle* fd); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ANDROID_DVR_POSE_CLIENT_INTERNAL_H_ diff --git a/libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h b/libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h new file mode 100644 index 0000000000..b2ebd95060 --- /dev/null +++ b/libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h @@ -0,0 +1,17 @@ +#ifndef ANDROID_DVR_SENSOR_IPC_H_ +#define ANDROID_DVR_SENSOR_IPC_H_ + +#define DVR_SENSOR_SERVICE_BASE "system/vr/sensors" + +#define DVR_SENSOR_SERVICE_CLIENT (DVR_SENSOR_SERVICE_BASE "/client") + +/* + * Endpoint ops + */ +enum { + DVR_SENSOR_START = 0, + DVR_SENSOR_STOP, + DVR_SENSOR_POLL, +}; + +#endif // ANDROID_DVR_SENSOR_IPC_H_ diff --git a/libs/vr/libvrsensor/include/private/dvr/sensor_client.h b/libs/vr/libvrsensor/include/private/dvr/sensor_client.h new file mode 100644 index 0000000000..15a9b8f5d1 --- /dev/null +++ b/libs/vr/libvrsensor/include/private/dvr/sensor_client.h @@ -0,0 +1,37 @@ +#ifndef ANDROID_DVR_SENSOR_CLIENT_H_ +#define ANDROID_DVR_SENSOR_CLIENT_H_ + +#include <hardware/sensors.h> +#include <pdx/client.h> +#include <poll.h> + +namespace android { +namespace dvr { + +// SensorClient is a remote interface to the sensor service in sensord. +class SensorClient : public pdx::ClientBase<SensorClient> { + public: + ~SensorClient(); + + int StartSensor(); + int StopSensor(); + int Poll(sensors_event_t* events, int max_count); + + private: + friend BASE; + + // Set up a channel associated with the sensor of the indicated type. + // NOTE(segal): If our hardware ends up with multiple sensors of the same + // type, we'll have to change this. + explicit SensorClient(int sensor_type); + + int sensor_type_; + + SensorClient(const SensorClient&); + SensorClient& operator=(const SensorClient&); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_SENSOR_CLIENT_H_ diff --git a/libs/vr/libvrsensor/include/private/dvr/sensor_constants.h b/libs/vr/libvrsensor/include/private/dvr/sensor_constants.h new file mode 100644 index 0000000000..8fa87b39a0 --- /dev/null +++ b/libs/vr/libvrsensor/include/private/dvr/sensor_constants.h @@ -0,0 +1,23 @@ +#ifndef ANDROID_DVR_SENSOR_CONSTANTS_H_ +#define ANDROID_DVR_SENSOR_CONSTANTS_H_ + +namespace android { +namespace dvr { + +// Number of elements in the async pose buffer. +// Must be power of two. +// Macro so that shader code can easily include this value. +#define kPoseAsyncBufferTotalCount 8 + +// Mask for accessing the current ring buffer array element: +// index = vsync_count & kPoseAsyncBufferIndexMask +constexpr uint32_t kPoseAsyncBufferIndexMask = kPoseAsyncBufferTotalCount - 1; + +// Number of pose frames including the current frame that are kept updated with +// pose forecast data. The other poses are left their last known estimates. +constexpr uint32_t kPoseAsyncBufferMinFutureCount = 4; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_SENSOR_CONSTANTS_H_ diff --git a/libs/vr/libvrsensor/latency_model.cpp b/libs/vr/libvrsensor/latency_model.cpp new file mode 100644 index 0000000000..d3a45210a7 --- /dev/null +++ b/libs/vr/libvrsensor/latency_model.cpp @@ -0,0 +1,24 @@ +#include <private/dvr/latency_model.h> + +#include <cmath> + +namespace android { +namespace dvr { + +LatencyModel::LatencyModel(size_t window_size) : window_size_(window_size) {} + +void LatencyModel::AddLatency(int64_t latency_ns) { + // Not enough samples yet? + if (num_summed_ < window_size_) { + // Accumulate. + latency_sum_ += latency_ns; + + // Have enough samples for latency estimate? + if (++num_summed_ == window_size_) { + latency_ = latency_sum_ / window_size_; + } + } +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp new file mode 100644 index 0000000000..9eae3aab86 --- /dev/null +++ b/libs/vr/libvrsensor/pose_client.cpp @@ -0,0 +1,338 @@ +#define LOG_TAG "PoseClient" +#include <dvr/pose_client.h> + +#include <stdint.h> + +#include <log/log.h> +#include <pdx/client.h> +#include <pdx/default_transport/client_channel_factory.h> +#include <pdx/file_handle.h> +#include <private/dvr/buffer_hub_client.h> +#include <private/dvr/pose-ipc.h> +#include <private/dvr/pose_client_internal.h> +#include <private/dvr/sensor_constants.h> + +using android::pdx::LocalHandle; +using android::pdx::LocalChannelHandle; +using android::pdx::Status; +using android::pdx::Transaction; + +#define arraysize(x) (static_cast<int32_t>(std::extent<decltype(x)>::value)) + +namespace android { +namespace dvr { + +// PoseClient is a remote interface to the pose service in sensord. +class PoseClient : public pdx::ClientBase<PoseClient> { + public: + ~PoseClient() override {} + + // Casts C handle into an instance of this class. + static PoseClient* FromC(DvrPose* client) { + return reinterpret_cast<PoseClient*>(client); + } + + // Polls the pose service for the current state and stores it in *state. + // Returns zero on success, a negative error code otherwise. + int Poll(DvrPoseState* state) { + Transaction trans{*this}; + Status<int> status = + trans.Send<int>(DVR_POSE_POLL, nullptr, 0, state, sizeof(*state)); + ALOGE_IF(!status, "Pose poll() failed because: %s\n", + status.GetErrorMessage().c_str()); + return ReturnStatusOrError(status); + } + + int GetPose(uint32_t vsync_count, DvrPoseAsync* out_pose) { + if (!mapped_pose_buffer_) { + int ret = GetRingBuffer(nullptr); + if (ret < 0) + return ret; + } + *out_pose = + mapped_pose_buffer_->ring[vsync_count & kPoseAsyncBufferIndexMask]; + return 0; + } + + uint32_t GetVsyncCount() { + if (!mapped_pose_buffer_) { + int ret = GetRingBuffer(nullptr); + if (ret < 0) + return 0; + } + return mapped_pose_buffer_->vsync_count; + } + + int GetControllerPose(int32_t controller_id, uint32_t vsync_count, + DvrPoseAsync* out_pose) { + if (controller_id < 0 || controller_id >= arraysize(controllers_)) { + return -EINVAL; + } + if (!controllers_[controller_id].mapped_pose_buffer) { + int ret = GetControllerRingBuffer(controller_id); + if (ret < 0) + return ret; + } + *out_pose = + controllers_[controller_id] + .mapped_pose_buffer[vsync_count & kPoseAsyncBufferIndexMask]; + return 0; + } + + int LogController(bool enable) { + Transaction trans{*this}; + Status<int> status = trans.Send<int>(DVR_POSE_LOG_CONTROLLER, &enable, + sizeof(enable), nullptr, 0); + ALOGE_IF(!status, "Pose LogController() failed because: %s", + status.GetErrorMessage().c_str()); + return ReturnStatusOrError(status); + } + + // Freezes the pose to the provided state. Future poll operations will return + // this state until a different state is frozen or SetMode() is called with a + // different mode. + // Returns zero on success, a negative error code otherwise. + int Freeze(const DvrPoseState& frozen_state) { + Transaction trans{*this}; + Status<int> status = trans.Send<int>(DVR_POSE_FREEZE, &frozen_state, + sizeof(frozen_state), nullptr, 0); + ALOGE_IF(!status, "Pose Freeze() failed because: %s\n", + status.GetErrorMessage().c_str()); + return ReturnStatusOrError(status); + } + + // Sets the data mode for the pose service. + int SetMode(DvrPoseMode mode) { + Transaction trans{*this}; + Status<int> status = + trans.Send<int>(DVR_POSE_SET_MODE, &mode, sizeof(mode), nullptr, 0); + ALOGE_IF(!status, "Pose SetPoseMode() failed because: %s", + status.GetErrorMessage().c_str()); + return ReturnStatusOrError(status); + } + + // Gets the data mode for the pose service. + int GetMode(DvrPoseMode* out_mode) { + int mode; + Transaction trans{*this}; + Status<int> status = + trans.Send<int>(DVR_POSE_GET_MODE, nullptr, 0, &mode, sizeof(mode)); + ALOGE_IF(!status, "Pose GetPoseMode() failed because: %s", + status.GetErrorMessage().c_str()); + if (status) + *out_mode = DvrPoseMode(mode); + return ReturnStatusOrError(status); + } + + int GetRingBuffer(DvrPoseRingBufferInfo* out_info) { + if (pose_buffer_.get()) { + if (out_info) { + GetPoseRingBufferInfo(out_info); + } + return 0; + } + + Transaction trans{*this}; + Status<LocalChannelHandle> status = + trans.Send<LocalChannelHandle>(DVR_POSE_GET_RING_BUFFER); + if (!status) { + ALOGE("Pose GetRingBuffer() failed because: %s", + status.GetErrorMessage().c_str()); + return -status.error(); + } + + auto buffer = BufferConsumer::Import(status.take()); + if (!buffer) { + ALOGE("Pose failed to import ring buffer"); + return -EIO; + } + void* addr = nullptr; + int ret = buffer->GetBlobReadOnlyPointer(sizeof(DvrPoseRingBuffer), &addr); + if (ret < 0 || !addr) { + ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr); + return -EIO; + } + pose_buffer_.swap(buffer); + mapped_pose_buffer_ = static_cast<const DvrPoseRingBuffer*>(addr); + ALOGI("Mapped pose data translation %f,%f,%f quat %f,%f,%f,%f", + mapped_pose_buffer_->ring[0].translation[0], + mapped_pose_buffer_->ring[0].translation[1], + mapped_pose_buffer_->ring[0].translation[2], + mapped_pose_buffer_->ring[0].orientation[0], + mapped_pose_buffer_->ring[0].orientation[1], + mapped_pose_buffer_->ring[0].orientation[2], + mapped_pose_buffer_->ring[0].orientation[3]); + if (out_info) { + GetPoseRingBufferInfo(out_info); + } + return 0; + } + + int GetControllerRingBuffer(int32_t controller_id) { + if (controller_id < 0 || controller_id >= arraysize(controllers_)) { + return -EINVAL; + } + ControllerClientState& client_state = controllers_[controller_id]; + if (client_state.pose_buffer.get()) { + return 0; + } + + Transaction trans{*this}; + Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>( + DVR_POSE_GET_CONTROLLER_RING_BUFFER, &controller_id, + sizeof(controller_id), nullptr, 0); + if (!status) { + return -status.error(); + } + + auto buffer = BufferConsumer::Import(status.take()); + if (!buffer) { + ALOGE("Pose failed to import ring buffer"); + return -EIO; + } + constexpr size_t size = kPoseAsyncBufferTotalCount * sizeof(DvrPoseAsync); + void* addr = nullptr; + int ret = buffer->GetBlobReadOnlyPointer(size, &addr); + if (ret < 0 || !addr) { + ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr); + return -EIO; + } + client_state.pose_buffer.swap(buffer); + client_state.mapped_pose_buffer = static_cast<const DvrPoseAsync*>(addr); + ALOGI( + "Mapped controller %d pose data translation %f,%f,%f quat %f,%f,%f,%f", + controller_id, client_state.mapped_pose_buffer[0].translation[0], + client_state.mapped_pose_buffer[0].translation[1], + client_state.mapped_pose_buffer[0].translation[2], + client_state.mapped_pose_buffer[0].orientation[0], + client_state.mapped_pose_buffer[0].orientation[1], + client_state.mapped_pose_buffer[0].orientation[2], + client_state.mapped_pose_buffer[0].orientation[3]); + return 0; + } + + int NotifyVsync(uint32_t vsync_count, int64_t display_timestamp, + int64_t display_period_ns, + int64_t right_eye_photon_offset_ns) { + const struct iovec data[] = { + {.iov_base = &vsync_count, .iov_len = sizeof(vsync_count)}, + {.iov_base = &display_timestamp, .iov_len = sizeof(display_timestamp)}, + {.iov_base = &display_period_ns, .iov_len = sizeof(display_period_ns)}, + {.iov_base = &right_eye_photon_offset_ns, + .iov_len = sizeof(right_eye_photon_offset_ns)}, + }; + Transaction trans{*this}; + Status<int> status = + trans.SendVector<int>(DVR_POSE_NOTIFY_VSYNC, data, nullptr); + ALOGE_IF(!status, "Pose NotifyVsync() failed because: %s\n", + status.GetErrorMessage().c_str()); + return ReturnStatusOrError(status); + } + + int GetRingBufferFd(LocalHandle* fd) { + int ret = GetRingBuffer(nullptr); + if (ret < 0) + return ret; + *fd = pose_buffer_->GetBlobFd(); + return 0; + } + + private: + friend BASE; + + // Set up a channel to the pose service. + PoseClient() + : BASE(pdx::default_transport::ClientChannelFactory::Create( + DVR_POSE_SERVICE_CLIENT)) { + // TODO(eieio): Cache the pose and make timeout 0 so that the API doesn't + // block while waiting for the pose service to come back up. + EnableAutoReconnect(kInfiniteTimeout); + } + + PoseClient(const PoseClient&) = delete; + PoseClient& operator=(const PoseClient&) = delete; + + void GetPoseRingBufferInfo(DvrPoseRingBufferInfo* out_info) const { + out_info->min_future_count = kPoseAsyncBufferMinFutureCount; + out_info->total_count = kPoseAsyncBufferTotalCount; + out_info->buffer = mapped_pose_buffer_->ring; + } + + std::unique_ptr<BufferConsumer> pose_buffer_; + const DvrPoseRingBuffer* mapped_pose_buffer_ = nullptr; + + struct ControllerClientState { + std::unique_ptr<BufferConsumer> pose_buffer; + const DvrPoseAsync* mapped_pose_buffer = nullptr; + }; + ControllerClientState controllers_[2]; +}; + +} // namespace dvr +} // namespace android + +using android::dvr::PoseClient; + +struct DvrPose {}; + +extern "C" { + +DvrPose* dvrPoseCreate() { + PoseClient* client = PoseClient::Create().release(); + return reinterpret_cast<DvrPose*>(client); +} + +void dvrPoseDestroy(DvrPose* client) { delete PoseClient::FromC(client); } + +int dvrPoseGet(DvrPose* client, uint32_t vsync_count, DvrPoseAsync* out_pose) { + return PoseClient::FromC(client)->GetPose(vsync_count, out_pose); +} + +uint32_t dvrPoseGetVsyncCount(DvrPose* client) { + return PoseClient::FromC(client)->GetVsyncCount(); +} + +int dvrPoseGetController(DvrPose* client, int32_t controller_id, + uint32_t vsync_count, DvrPoseAsync* out_pose) { + return PoseClient::FromC(client)->GetControllerPose(controller_id, + vsync_count, out_pose); +} + +int dvrPoseLogController(DvrPose* client, bool enable) { + return PoseClient::FromC(client)->LogController(enable); +} + +int dvrPosePoll(DvrPose* client, DvrPoseState* state) { + return PoseClient::FromC(client)->Poll(state); +} + +int dvrPoseFreeze(DvrPose* client, const DvrPoseState* frozen_state) { + return PoseClient::FromC(client)->Freeze(*frozen_state); +} + +int dvrPoseSetMode(DvrPose* client, DvrPoseMode mode) { + return PoseClient::FromC(client)->SetMode(mode); +} + +int dvrPoseGetMode(DvrPose* client, DvrPoseMode* mode) { + return PoseClient::FromC(client)->GetMode(mode); +} + +int dvrPoseGetRingBuffer(DvrPose* client, DvrPoseRingBufferInfo* out_info) { + return PoseClient::FromC(client)->GetRingBuffer(out_info); +} + +int privateDvrPoseNotifyVsync(DvrPose* client, uint32_t vsync_count, + int64_t display_timestamp, + int64_t display_period_ns, + int64_t right_eye_photon_offset_ns) { + return PoseClient::FromC(client)->NotifyVsync(vsync_count, display_timestamp, + display_period_ns, + right_eye_photon_offset_ns); +} + +int privateDvrPoseGetRingBufferFd(DvrPose* client, LocalHandle* fd) { + return PoseClient::FromC(client)->GetRingBufferFd(fd); +} + +} // extern "C" diff --git a/libs/vr/libvrsensor/sensor_client.cpp b/libs/vr/libvrsensor/sensor_client.cpp new file mode 100644 index 0000000000..04e88cc9a1 --- /dev/null +++ b/libs/vr/libvrsensor/sensor_client.cpp @@ -0,0 +1,79 @@ +#define LOG_TAG "SensorClient" +#include <private/dvr/sensor_client.h> + +#include <log/log.h> +#include <poll.h> + +#include <pdx/default_transport/client_channel_factory.h> +#include <private/dvr/sensor-ipc.h> + +using android::pdx::Transaction; + +namespace android { +namespace dvr { + +SensorClient::SensorClient(int sensor_type) + : BASE(pdx::default_transport::ClientChannelFactory::Create( + DVR_SENSOR_SERVICE_CLIENT)), + sensor_type_(sensor_type) {} + +SensorClient::~SensorClient() {} + +int SensorClient::StartSensor() { + Transaction trans{*this}; + auto status = trans.Send<int>(DVR_SENSOR_START, &sensor_type_, + sizeof(sensor_type_), nullptr, 0); + ALOGE_IF(!status, "startSensor() failed because: %s\n", + status.GetErrorMessage().c_str()); + return ReturnStatusOrError(status); +} + +int SensorClient::StopSensor() { + Transaction trans{*this}; + auto status = trans.Send<int>(DVR_SENSOR_STOP); + ALOGE_IF(!status, "stopSensor() failed because: %s\n", + status.GetErrorMessage().c_str()); + return ReturnStatusOrError(status); +} + +int SensorClient::Poll(sensors_event_t* events, int max_events) { + int num_events = 0; + struct iovec rvec[] = { + {.iov_base = &num_events, .iov_len = sizeof(int)}, + {.iov_base = events, .iov_len = max_events * sizeof(sensors_event_t)}, + }; + Transaction trans{*this}; + auto status = trans.SendVector<int>(DVR_SENSOR_POLL, nullptr, rvec); + ALOGE_IF(!status, "Sensor poll() failed because: %s\n", + status.GetErrorMessage().c_str()); + return !status ? -status.error() : num_events; +} + +} // namespace dvr +} // namespace android + +// Entrypoints to simplify using the library when programmatically dynamicly +// loading it. +// Allows us to call this library without linking it, as, for instance, +// when compiling GVR in Google3. +// NOTE(segal): It's kind of a hack. + +extern "C" uint64_t dvrStartSensor(int type) { + android::dvr::SensorClient* service = + android::dvr::SensorClient::Create(type).release(); + service->StartSensor(); + return (uint64_t)service; +} + +extern "C" void dvrStopSensor(uint64_t service) { + android::dvr::SensorClient* iss = + reinterpret_cast<android::dvr::SensorClient*>(service); + iss->StopSensor(); + delete iss; +} + +extern "C" int dvrPollSensor(uint64_t service, int max_count, + sensors_event_t* events) { + return reinterpret_cast<android::dvr::SensorClient*>(service)->Poll( + events, max_count); +} |