Surface ‐ ANativeWindow ‐ IGraphicBufferProducer ‐ NativeHandle - tonykwok/made-mistakes-again GitHub Wiki
Ref. frameworks/base/native/android/native_window_jni.cpp
#include <android/native_window_jni.h>
#include <android/native_window.h>
#include <system/window.h>
#include <gui/Surface.h>
#include <utils/StrongPointer.h>
#include <android_runtime/android_view_Surface.h>
using namespace android;
ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface) {
sp<ANativeWindow> win = android_view_Surface_getNativeWindow(env, surface);
if (win != NULL) {
ANativeWindow_acquire(win.get());
}
return win.get();
}
jobject ANativeWindow_toSurface(JNIEnv* env, ANativeWindow* window) {
if (window == NULL) {
return NULL;
}
sp<Surface> surface = static_cast<Surface*>(window);
return android_view_Surface_createFromSurface(env, surface);
}
Ref. frameworks/hardware/interfaces/automotive/display/aidl/vts/functional/VtsHalCarDisplayTargetTest.cpp
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <aidl/android/frameworks/automotive/display/DisplayDesc.h>
#include <aidl/android/frameworks/automotive/display/ICarDisplayProxy.h>
#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
#include <android/binder_ibinder.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <android/binder_status.h>
#include <bufferqueueconverter/BufferQueueConverter.h>
namespace {
using ::aidl::android::frameworks::automotive::display::DisplayDesc;
using ::aidl::android::frameworks::automotive::display::ICarDisplayProxy;
using ::aidl::android::hardware::common::NativeHandle;
android::sp<HGraphicBufferProducer> convertNativeHandleToHGBP(const NativeHandle& aidlHandle) {
native_handle_t* handle = ::android::dupFromAidl(aidlHandle);
if (handle == nullptr || handle->numFds != 0 ||
handle->numInts < std::ceil(sizeof(size_t) / sizeof(int))) {
LOG(ERROR) << "Invalid native handle";
return nullptr;
}
android::hardware::hidl_vec<uint8_t> halToken;
halToken.setToExternal(reinterpret_cast<uint8_t*>(const_cast<int*>(&(handle->data[1]))),
handle->data[0]);
android::sp<HGraphicBufferProducer> hgbp =
HGraphicBufferProducer::castFrom(::android::retrieveHalInterface(halToken));
return std::move(hgbp);
}
} // namespace
TEST_P(CarDisplayAidlTest, getIGBPObject) {
LOG(INFO) << "Test getHGraphicBufferProducer method";
for (const auto& id : mDisplayIds) {
// Get a display info.
DisplayDesc desc;
ASSERT_TRUE(mDisplayProxy->getDisplayInfo(id, &desc).isOk());
// Get a HGBP object as a native handle object.
NativeHandle handle;
ASSERT_TRUE(mDisplayProxy->getHGraphicBufferProducer(id, &handle).isOk());
// Convert a native handle object into a HGBP object.
android::sp<android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer>
gfxBufferProducer = convertNativeHandleToHGBP(handle);
ASSERT_NE(gfxBufferProducer, nullptr);
// Create a Surface object.
android::SurfaceHolderUniquePtr surfaceHolder = getSurfaceFromHGBP(gfxBufferProducer);
ASSERT_NE(surfaceHolder, nullptr);
// Verify the size.
ANativeWindow* nativeWindow = getNativeWindow(surfaceHolder.get());
ASSERT_EQ(desc.width, ANativeWindow_getWidth(nativeWindow));
ASSERT_EQ(desc.height, ANativeWindow_getHeight(nativeWindow));
}
}
Ref. frameworks/native/libs/bufferqueueconverter/BufferQueueConverter.cpp
#include <gui/Surface.h>
#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
#include "include/bufferqueueconverter/BufferQueueConverter.h"
using ::android::Surface;
using ::android::IGraphicBufferProducer;
using ::android::hardware::graphics::bufferqueue::V2_0::utils::H2BGraphicBufferProducer;
namespace android {
struct SurfaceHolder {
sp<Surface> surface;
SurfaceHolder(const sp<Surface>& s) : surface(s) {}
};
/**
* Custom deleter for SurfaceHolder unique pointer
*/
void destroySurfaceHolder(SurfaceHolder* surfaceHolder) {
delete surfaceHolder;
}
SurfaceHolderUniquePtr getSurfaceFromHGBP(const sp<HGraphicBufferProducer>& token) {
if (token == nullptr) {
ALOGE("Passed IGraphicBufferProducer handle is invalid.");
return SurfaceHolderUniquePtr(nullptr, nullptr);
}
sp<IGraphicBufferProducer> bufferProducer = new H2BGraphicBufferProducer(token);
if (bufferProducer == nullptr) {
ALOGE("Failed to get IGraphicBufferProducer.");
return SurfaceHolderUniquePtr(nullptr, nullptr);
}
sp<Surface> newSurface(new Surface(bufferProducer, true));
if (newSurface == nullptr) {
ALOGE("Failed to create Surface from HGBP.");
return SurfaceHolderUniquePtr(nullptr, nullptr);
}
return SurfaceHolderUniquePtr(new SurfaceHolder(newSurface), destroySurfaceHolder);
}
ANativeWindow* getNativeWindow(SurfaceHolder* handle) {
if (handle == nullptr) {
ALOGE("SurfaceHolder is invalid.");
return nullptr;
}
return static_cast<ANativeWindow*>(handle->surface.get());
}
} // namespace android
Ref. frameworks/base/services/core/jni/com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp
#define LOG_TAG "SurfaceToNativeHandleConverter"
#include <android_os_NativeHandle.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_view_Surface.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/Surface.h>
#include <gui/bufferqueue/1.0/WGraphicBufferProducer.h>
#include <utils/Log.h>
#include "jni.h"
namespace android {
namespace {
constexpr int WINDOW_HAL_TOKEN_SIZE_MAX = 256;
native_handle_t* convertHalTokenToNativeHandle(const HalToken& halToken) {
// We attempt to store halToken in the ints of the native_handle_t after its
// size. The first int stores the size of the token. We store this in an int
// to avoid alignment issues where size_t and int do not have the same
// alignment.
size_t nhDataByteSize = halToken.size();
if (nhDataByteSize > WINDOW_HAL_TOKEN_SIZE_MAX) {
// The size of the token isn't reasonable..
return nullptr;
}
size_t numInts = ceil(nhDataByteSize / sizeof(int)) + 1;
// We don't check for overflow, whether numInts can fit in an int, since we
// expect WINDOW_HAL_TOKEN_SIZE_MAX to be a reasonable limit.
// create a native_handle_t with 0 numFds and numInts number of ints.
native_handle_t* nh = native_handle_create(0, numInts);
if (!nh) {
return nullptr;
}
// Store the size of the token in the first int.
nh->data[0] = nhDataByteSize;
memcpy(&(nh->data[1]), halToken.data(), nhDataByteSize);
return nh;
}
HalToken convertNativeHandleToHalToken(native_handle_t* handle) {
int size = handle->data[0];
auto data = reinterpret_cast<uint8_t*>(&handle->data[1]);
HalToken halToken;
halToken.setToExternal(data, size);
return halToken;
}
jobject acquireSurfaceHandle(JNIEnv* env, jobject /* clazz */, jobject jSurface) {
ALOGD("%s", __func__);
if (jSurface == nullptr) {
ALOGE("%s: jSurface is null", __func__);
return nullptr;
}
sp<Surface> surface = android_view_Surface_getSurface(env, jSurface);
if (surface == nullptr) {
ALOGE("%s: surface is null", __func__);
return nullptr;
}
sp<IGraphicBufferProducer> igbp = surface->getIGraphicBufferProducer();
sp<HGraphicBufferProducer> hgbp = new TWGraphicBufferProducer<HGraphicBufferProducer>(igbp);
// The HAL token will be closed in releaseSurfaceHandle.
HalToken halToken;
createHalToken(hgbp, &halToken);
native_handle_t* native_handle = convertHalTokenToNativeHandle(halToken);
if (native_handle == nullptr) {
ALOGE("%s: native_handle is null", __func__);
return nullptr;
}
jobject jHandle = JNativeHandle::MakeJavaNativeHandleObj(env, native_handle);
native_handle_delete(native_handle);
return jHandle;
}
void releaseSurfaceHandle(JNIEnv* env, jobject /* clazz */, jobject jHandle) {
ALOGD("%s", __func__);
// Creates a native handle from a Java handle. We must call native_handle_delete when we're done
// with it because we created it, but we shouldn't call native_handle_close because we don't own
// the underlying FDs.
native_handle_t* handle =
JNativeHandle::MakeCppNativeHandle(env, jHandle, nullptr /* storage */);
if (handle == nullptr) {
ALOGE("%s: handle is null", __func__);
return;
}
HalToken token = convertNativeHandleToHalToken(handle);
ALOGD("%s: deleteHalToken, success: %d", __func__, deleteHalToken(token));
ALOGD("%s: native_handle_delete, success: %d", __func__, !native_handle_delete(handle));
}
const JNINativeMethod method_table[] = {
{"acquireSurfaceHandle", "(Landroid/view/Surface;)Landroid/os/NativeHandle;",
reinterpret_cast<void*>(acquireSurfaceHandle)},
{"releaseSurfaceHandle", "(Landroid/os/NativeHandle;)V",
reinterpret_cast<void*>(releaseSurfaceHandle)},
};
} // namespace
int register_android_server_FaceService(JNIEnv* env) {
return AndroidRuntime::
registerNativeMethods(env, "com/android/server/biometrics/sensors/face/FaceService",
method_table, NELEM(method_table));
}
} // namespace android
Ref. frameworks/base/core/jni/android_os_NativeHandle.h
#ifndef ANDROID_OS_NATIVE_HANDLE_H
#define ANDROID_OS_NATIVE_HANDLE_H
#include "hwbinder/EphemeralStorage.h"
#include <cutils/native_handle.h>
#include <jni.h>
namespace android {
struct JNativeHandle {
/**
* Returns a Java NativeHandle object representing the parameterized
* native_handle_t instance.
*/
static jobject MakeJavaNativeHandleObj(JNIEnv *env, const native_handle_t *handle);
/**
* Returns a heap-allocated native_handle_t instance representing the
* parameterized Java object. Note that if no valid EphemeralStorage*
* parameter is supplied (storage is nullptr), the return value must
* be explicitly deallocated (using native_handle_delete).
*/
static native_handle_t* MakeCppNativeHandle(JNIEnv *env, jobject jHandle,
EphemeralStorage *storage);
/**
* Returns an (uninitialized) array of Java NativeHandle objects.
*/
static jobjectArray AllocJavaNativeHandleObjArray(JNIEnv *env, jsize length);
};
}
#endif // ANDROID_OS_NATIVE_HANDLE_H
media_status_t AImageReader::getWindowNativeHandle(native_handle **handle) {
if (mWindowHandle != nullptr) {
*handle = mWindowHandle;
return AMEDIA_OK;
}
sp<HGraphicBufferProducer> hgbp =
new TWGraphicBufferProducer<HGraphicBufferProducer>(mProducer);
HalToken halToken;
if (!createHalToken(hgbp, &halToken)) {
return AMEDIA_ERROR_UNKNOWN;
}
mWindowHandle = convertHalTokenToNativeHandle(halToken);
if (!mWindowHandle) {
return AMEDIA_ERROR_UNKNOWN;
}
*handle = mWindowHandle;
return AMEDIA_OK;
}
Ref. frameworks/base/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlNativeHandleUtils.java
package com.android.server.biometrics.sensors.face.aidl;
import android.annotation.Nullable;
import android.os.ParcelFileDescriptor;
import java.io.FileDescriptor;
import java.io.IOException;
/**
* A utility class for the AIDL implementation of NativeHandle - {@link
* android.hardware.common.NativeHandle}.
*/
public final class AidlNativeHandleUtils {
/**
* Converts a {@link android.os.NativeHandle} to a {@link android.hardware.common.NativeHandle}
* by duplicating the underlying file descriptors.
*
* Both the original and new handle must be closed after use.
*
* @param handle {@link android.os.NativeHandle}. Can be null.
* @return a {@link android.hardware.common.NativeHandle} representation of {@code handle}.
* Returns null if {@code handle} is null.
* @throws IOException if any of the underlying calls to {@code dup} fail.
*/
public static @Nullable android.hardware.common.NativeHandle dup(
@Nullable android.os.NativeHandle handle) throws IOException {
if (handle == null) {
return null;
}
android.hardware.common.NativeHandle res = new android.hardware.common.NativeHandle();
final FileDescriptor[] fds = handle.getFileDescriptors();
res.ints = handle.getInts().clone();
res.fds = new ParcelFileDescriptor[fds.length];
for (int i = 0; i < fds.length; ++i) {
res.fds[i] = ParcelFileDescriptor.dup(fds[i]);
}
return res;
}
/**
* Closes the file descriptors contained within a {@link android.hardware.common.NativeHandle}.
* This is a no-op if the handle is null.
*
* This should only be used for handles that own their file descriptors, for example handles
* obtained using {@link #dup(android.os.NativeHandle)}.
*
* @param handle {@link android.hardware.common.NativeHandle}. Can be null.
* @throws IOException if any of the underlying calls to {@code close} fail.
*/
public static void close(@Nullable android.hardware.common.NativeHandle handle)
throws IOException {
if (handle != null) {
for (ParcelFileDescriptor fd : handle.fds) {
if (fd != null) {
fd.close();
}
}
}
}
}
Ref. hardware/interfaces/common/support/NativeHandle.cpp
#include <aidlcommonsupport/NativeHandle.h>
#include <fcntl.h>
namespace android {
using aidl::android::hardware::common::NativeHandle;
/**
* Checks if a NativeHandle is null
*/
bool isAidlNativeHandleEmpty(const NativeHandle& handle) {
return handle.fds.empty() && handle.ints.empty();
}
static native_handle_t* fromAidl(const NativeHandle& handle, bool doDup) {
native_handle_t* to = native_handle_create(handle.fds.size(), handle.ints.size());
if (!to) return nullptr;
for (size_t i = 0; i < handle.fds.size(); i++) {
int fd = handle.fds[i].get();
to->data[i] = doDup ? fcntl(fd, F_DUPFD_CLOEXEC, 0) : fd;
}
memcpy(to->data + handle.fds.size(), handle.ints.data(), handle.ints.size() * sizeof(int));
return to;
}
native_handle_t* makeFromAidl(const NativeHandle& handle) {
return fromAidl(handle, false /* doDup */);
}
native_handle_t* dupFromAidl(const NativeHandle& handle) {
return fromAidl(handle, true /* doDup */);
}
static NativeHandle toAidl(const native_handle_t* handle, bool doDup) {
NativeHandle to;
to.fds = std::vector<ndk::ScopedFileDescriptor>(handle->numFds);
for (size_t i = 0; i < handle->numFds; i++) {
int fd = handle->data[i];
to.fds.at(i).set(doDup ? fcntl(fd, F_DUPFD_CLOEXEC, 0) : fd);
}
to.ints = std::vector<int32_t>(handle->data + handle->numFds,
handle->data + handle->numFds + handle->numInts);
return to;
}
NativeHandle makeToAidl(const native_handle_t* handle) {
return toAidl(handle, false /* doDup */);
}
NativeHandle dupToAidl(const native_handle_t* handle) {
return toAidl(handle, true /* doDup */);
}
} // namespace android
Ref. frameworks/base/media/jni/android_media_MediaCodec.cpp
static jobject android_media_MediaCodec_mapHardwareBuffer(JNIEnv *env, jclass, jobject bufferObj) {
ALOGV("android_media_MediaCodec_mapHardwareBuffer");
AHardwareBuffer *hardwareBuffer = android_hardware_HardwareBuffer_getNativeHardwareBuffer(
env, bufferObj);
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(hardwareBuffer, &desc);
if (desc.format != AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420) {
ALOGI("mapHardwareBuffer: unmappable format: %d", desc.format);
return nullptr;
}
if ((desc.usage & AHARDWAREBUFFER_USAGE_CPU_READ_MASK) == 0) {
ALOGI("mapHardwareBuffer: buffer not CPU readable");
return nullptr;
}
bool readOnly = ((desc.usage & AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK) == 0);
uint64_t cpuUsage = 0;
cpuUsage |= (desc.usage & AHARDWAREBUFFER_USAGE_CPU_READ_MASK);
cpuUsage |= (desc.usage & AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK);
AHardwareBuffer_Planes planes;
int err = AHardwareBuffer_lockPlanes(
hardwareBuffer, cpuUsage, -1 /* fence */, nullptr /* rect */, &planes);
if (err != 0) {
ALOGI("mapHardwareBuffer: Failed to lock planes (err=%d)", err);
return nullptr;
}
if (planes.planeCount != 3) {
ALOGI("mapHardwareBuffer: planeCount expected 3, actual %u", planes.planeCount);
return nullptr;
}
ScopedLocalRef<jobjectArray> buffersArray{
env, env->NewObjectArray(3, gByteBufferInfo.clazz, NULL)};
ScopedLocalRef<jintArray> rowStridesArray{env, env->NewIntArray(3)};
ScopedLocalRef<jintArray> pixelStridesArray{env, env->NewIntArray(3)};
jboolean isCopy = JNI_FALSE;
jint *rowStrides = env->GetIntArrayElements(rowStridesArray.get(), &isCopy);
jint *pixelStrides = env->GetIntArrayElements(rowStridesArray.get(), &isCopy);
// For Y plane
int rowSampling = 1;
int colSampling = 1;
// plane indices are Y-U-V.
for (uint32_t i = 0; i < 3; ++i) {
const AHardwareBuffer_Plane &plane = planes.planes[i];
int maxRowOffset = plane.rowStride * (desc.height / rowSampling - 1);
int maxColOffset = plane.pixelStride * (desc.width / colSampling - 1);
int maxOffset = maxRowOffset + maxColOffset;
ScopedLocalRef<jobject> byteBuffer{env, CreateByteBuffer(
env,
plane.data,
maxOffset + 1,
0,
maxOffset + 1,
readOnly,
true)};
env->SetObjectArrayElement(buffersArray.get(), i, byteBuffer.get());
rowStrides[i] = plane.rowStride;
pixelStrides[i] = plane.pixelStride;
// For U-V planes
rowSampling = 2;
colSampling = 2;
}
env->ReleaseIntArrayElements(rowStridesArray.get(), rowStrides, 0);
env->ReleaseIntArrayElements(pixelStridesArray.get(), pixelStrides, 0);
rowStrides = pixelStrides = nullptr;
ScopedLocalRef<jclass> imageClazz(
env, env->FindClass("android/media/MediaCodec$MediaImage"));
CHECK(imageClazz.get() != NULL);
jmethodID imageConstructID = env->GetMethodID(imageClazz.get(), "<init>",
"([Ljava/nio/ByteBuffer;[I[IIIIZJIILandroid/graphics/Rect;J)V");
jobject img = env->NewObject(imageClazz.get(), imageConstructID,
buffersArray.get(),
rowStridesArray.get(),
pixelStridesArray.get(),
desc.width,
desc.height,
desc.format, // ???
(jboolean)readOnly /* readOnly */,
(jlong)0 /* timestamp */,
(jint)0 /* xOffset */, (jint)0 /* yOffset */, nullptr /* cropRect */,
(jlong)hardwareBuffer);
// if MediaImage creation fails, return null
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
return nullptr;
}
AHardwareBuffer_acquire(hardwareBuffer);
return img;
}
static void android_media_MediaCodec_closeMediaImage(JNIEnv *, jclass, jlong context) {
ALOGV("android_media_MediaCodec_closeMediaImage");
if (context == 0) {
return;
}
AHardwareBuffer *hardwareBuffer = (AHardwareBuffer *)context;
int err = AHardwareBuffer_unlock(hardwareBuffer, nullptr);
if (err != 0) {
ALOGI("closeMediaImage: failed to unlock (err=%d)", err);
// Continue to release the hardwareBuffer
}
AHardwareBuffer_release(hardwareBuffer);
}
template <typename T>
static jobject CreateByteBuffer(
JNIEnv *env, T *base, size_t capacity, size_t offset, size_t size,
bool readOnly, bool clearBuffer) {
jobject byteBuffer =
env->NewDirectByteBuffer(
const_cast<typename std::remove_const<T>::type *>(base),
capacity);
if (readOnly && byteBuffer != NULL) {
jobject readOnlyBuffer = env->CallObjectMethod(
byteBuffer, gByteBufferInfo.asReadOnlyBufferId);
env->DeleteLocalRef(byteBuffer);
byteBuffer = readOnlyBuffer;
}
if (byteBuffer == NULL) {
return nullptr;
}
jobject me = env->CallObjectMethod(
byteBuffer, gByteBufferInfo.orderId, gByteBufferInfo.nativeByteOrder);
env->DeleteLocalRef(me);
me = env->CallObjectMethod(
byteBuffer, gByteBufferInfo.limitId,
clearBuffer ? capacity : offset + size);
env->DeleteLocalRef(me);
me = env->CallObjectMethod(
byteBuffer, gByteBufferInfo.positionId,
clearBuffer ? 0 : offset);
env->DeleteLocalRef(me);
me = NULL;
return byteBuffer;
}