diff options
author | Craig Donner <cdonner@google.com> | 2016-12-16 14:07:03 -0800 |
---|---|---|
committer | Craig Donner <cdonner@google.com> | 2017-01-19 17:01:19 -0800 |
commit | 0cff9d955e7bd378bcc57d34d34e8829cecb9dd3 (patch) | |
tree | 5d84e11d0cb9e4abd1b2accd91de77cb46fcaa5b /native/android/hardware_buffer.cpp | |
parent | 54df14956eb890bcd92ffa1590705f8b417ccf5b (diff) |
AHardwareBuffer implementation.
Bug: 34050596
Test: Added gunit tests in CTS (CtsNativeHardwareTestCases)
Change-Id: I41f914a6d346fbb5f818a6591c86e7bf12f0576c
Diffstat (limited to 'native/android/hardware_buffer.cpp')
-rw-r--r-- | native/android/hardware_buffer.cpp | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/native/android/hardware_buffer.cpp b/native/android/hardware_buffer.cpp new file mode 100644 index 000000000000..6a10cb587be2 --- /dev/null +++ b/native/android/hardware_buffer.cpp @@ -0,0 +1,302 @@ +/* + * 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 <android/hardware_buffer_jni.h> + +#include <errno.h> +#include <sys/socket.h> + +#include <memory> + +#include <android_runtime/android_hardware_HardwareBuffer.h> +#include <binder/Binder.h> +#include <binder/Parcel.h> +#include <cutils/native_handle.h> +#include <binder/IServiceManager.h> +#include <gui/ISurfaceComposer.h> +#include <gui/IGraphicBufferAlloc.h> +#include <ui/GraphicBuffer.h> +#include <utils/Flattenable.h> +#include <utils/Log.h> + +static constexpr int kDataBufferSize = 64 * sizeof(int); // 64 ints + +using namespace android; + +static inline const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer( + const AHardwareBuffer* buffer) { + return reinterpret_cast<const GraphicBuffer*>(buffer); +} + +static inline GraphicBuffer* AHardwareBuffer_to_GraphicBuffer( + AHardwareBuffer* buffer) { + return reinterpret_cast<GraphicBuffer*>(buffer); +} + +static inline AHardwareBuffer* GraphicBuffer_to_AHardwareBuffer( + GraphicBuffer* buffer) { + return reinterpret_cast<AHardwareBuffer*>(buffer); +} + +// ---------------------------------------------------------------------------- +// Public functions +// ---------------------------------------------------------------------------- + +int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, + AHardwareBuffer** outBuffer) { + if (!outBuffer || !desc) return BAD_VALUE; + + // The holder is used to destroy the buffer if an error occurs. + sp<IServiceManager> sm = defaultServiceManager(); + if (sm == nullptr) { + ALOGE("Unable to connect to ServiceManager"); + return PERMISSION_DENIED; + } + + // Get the SurfaceFlingerService. + sp<ISurfaceComposer> composer = interface_cast<ISurfaceComposer>( + sm->getService(String16("SurfaceFlinger"))); + if (composer == nullptr) { + ALOGE("Unable to connect to surface composer"); + return PERMISSION_DENIED; + } + // Get an IGraphicBufferAlloc to create the buffer. + sp<IGraphicBufferAlloc> allocator = composer->createGraphicBufferAlloc(); + if (allocator == nullptr) { + ALOGE("Unable to obtain a buffer allocator"); + return PERMISSION_DENIED; + } + + int format = android_hardware_HardwareBuffer_convertToPixelFormat( + desc->format); + if (format == 0) { + ALOGE("Invalid pixel format"); + return BAD_VALUE; + } + + status_t err; + uint32_t usage = android_hardware_HardwareBuffer_convertToGrallocUsageBits( + desc->usage0, desc->usage1); + sp<GraphicBuffer> gbuffer = allocator->createGraphicBuffer(desc->width, + desc->height, format, desc->layers, usage, &err); + if (err != NO_ERROR) { + return err; + } + + *outBuffer = GraphicBuffer_to_AHardwareBuffer(gbuffer.get()); + // Ensure the buffer doesn't get destroyed with the sp<> goes away. + AHardwareBuffer_acquire(*outBuffer); + return NO_ERROR; +} + +void AHardwareBuffer_acquire(AHardwareBuffer* buffer) { + AHardwareBuffer_to_GraphicBuffer(buffer)->incStrong( + (void*)AHardwareBuffer_acquire); +} + +void AHardwareBuffer_release(AHardwareBuffer* buffer) { + 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->usage0 = + android_hardware_HardwareBuffer_convertFromGrallocUsageBits( + gbuffer->getUsage()); + outDesc->usage1 = 0; + outDesc->format = android_hardware_HardwareBuffer_convertFromPixelFormat( + static_cast<uint32_t>(gbuffer->getPixelFormat())); +} + +int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage0, + int32_t fence, const ARect* rect, void** outVirtualAddress) { + if (!buffer) return BAD_VALUE; + + if (usage0 & ~(AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN | + AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN)) { + ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only " + " AHARDWAREBUFFER_USAGE0_CPU_* flags are allowed"); + return BAD_VALUE; + } + + uint32_t usage = android_hardware_HardwareBuffer_convertToGrallocUsageBits( + usage0, 0); + GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer); + if (!rect) { + return gBuffer->lockAsync(usage, outVirtualAddress, fence); + } else { + Rect bounds(rect->left, rect->top, rect->right, rect->bottom); + return gBuffer->lockAsync(usage, bounds, outVirtualAddress, fence); + } +} + +int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) { + if (!buffer) return BAD_VALUE; + + GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer); + 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(kDataBufferSize)]; + 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 = sendmsg(socketFd, &msg, 0); + if (result <= 0) { + ALOGE("Error writing AHardwareBuffer to socket: error %#x (%s)", + result, strerror(errno)); + return result; + } + return NO_ERROR; +} + +int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, + AHardwareBuffer** outBuffer) { + if (!outBuffer) return BAD_VALUE; + + char dataBuf[CMSG_SPACE(kDataBufferSize)]; + char fdBuf[CMSG_SPACE(kDataBufferSize)]; + struct iovec iov[1]; + iov[0].iov_base = dataBuf; + iov[0].iov_len = sizeof(dataBuf); + + struct msghdr msg = { + .msg_control = fdBuf, + .msg_controllen = sizeof(fdBuf), + .msg_iov = &iov[0], + .msg_iovlen = 1, + }; + + int result = recvmsg(socketFd, &msg, 0); + if (result <= 0) { + ALOGE("Error reading AHardwareBuffer from socket: error %#x (%s)", + result, strerror(errno)); + 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 = GraphicBuffer_to_AHardwareBuffer(gBuffer); + // Ensure the buffer has a positive ref-count. + AHardwareBuffer_acquire(*outBuffer); + + return NO_ERROR; +} + +const struct native_handle* AHardwareBuffer_getNativeHandle( + const AHardwareBuffer* buffer) { + if (!buffer) return nullptr; + const GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer); + return gbuffer->handle; +} + +// ---------------------------------------------------------------------------- +// JNI functions +// ---------------------------------------------------------------------------- + +AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env, + jobject hardwareBufferObj) { + return android_hardware_HardwareBuffer_getNativeHardwareBuffer(env, + hardwareBufferObj); +} + +jobject AHardwareBuffer_toHardwareBuffer(JNIEnv* env, + AHardwareBuffer* hardwareBuffer) { + return android_hardware_HardwareBuffer_createFromAHardwareBuffer(env, + hardwareBuffer); +} |