AHardwareBuffer implementation.
Bug: 34050596 Test: Added gunit tests in CTS (CtsNativeHardwareTestCases) Change-Id: I41f914a6d346fbb5f818a6591c86e7bf12f0576c
This commit is contained in:
@@ -9,6 +9,7 @@ LOCAL_SRC_FILES:= \
|
||||
asset_manager.cpp \
|
||||
choreographer.cpp \
|
||||
configuration.cpp \
|
||||
hardware_buffer.cpp \
|
||||
input.cpp \
|
||||
looper.cpp \
|
||||
native_activity.cpp \
|
||||
|
||||
302
native/android/hardware_buffer.cpp
Normal file
302
native/android/hardware_buffer.cpp
Normal file
@@ -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);
|
||||
}
|
||||
@@ -139,6 +139,17 @@ LIBANDROID {
|
||||
ANativeActivity_setWindowFlags;
|
||||
ANativeActivity_setWindowFormat;
|
||||
ANativeActivity_showSoftInput;
|
||||
AHardwareBuffer_acquire; # introduced=26
|
||||
AHardwareBuffer_allocate; # introduced=26
|
||||
AHardwareBuffer_describe; # introduced=26
|
||||
AHardwareBuffer_fromHardwareBuffer; # introduced=26
|
||||
AHardwareBuffer_getNativeHandle; # introduced=26
|
||||
AHardwareBuffer_lock; # introduced=26
|
||||
AHardwareBuffer_recvHandleFromUnixSocket; # introduced=26
|
||||
AHardwareBuffer_release; # introduced=26
|
||||
AHardwareBuffer_sendHandleToUnixSocket; # introduced=26
|
||||
AHardwareBuffer_toHardwareBuffer; # introduced=26
|
||||
AHardwareBuffer_unlock; # introduced=26
|
||||
ANativeWindow_acquire;
|
||||
ANativeWindow_fromSurface;
|
||||
ANativeWindow_fromSurfaceTexture; # introduced-arm=13 introduced-mips=13 introduced-x86=13
|
||||
|
||||
Reference in New Issue
Block a user