/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "Camera-SurfaceUtils-JNI" // #define LOG_NDEBUG 0 #include #include #include #include #include #include "android_runtime/android_graphics_SurfaceTexture.h" #include "android_runtime/android_view_Surface.h" #include "core_jni_helpers.h" #include "jni.h" #include #include #include #include #include #include #include using namespace android; // fully-qualified class name #define CAMERA_UTILS_CLASS_NAME "android/hardware/camera2/utils/SurfaceUtils" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) #define OVERRIDE_SURFACE_ERROR(err) \ do { \ if (err == -ENODEV) { \ err = BAD_VALUE; \ } \ } while (0) static sp getNativeWindow(JNIEnv* env, jobject surface) { sp anw; if (surface) { anw = android_view_Surface_getNativeWindow(env, surface); if (env->ExceptionCheck()) { return NULL; } } else { jniThrowNullPointerException(env, "surface"); return NULL; } if (anw == NULL) { ALOGE("%s: Surface had no valid native window.", __FUNCTION__); return NULL; } return anw; } static sp getSurface(JNIEnv* env, jobject surface) { sp s; if (surface) { s = android_view_Surface_getSurface(env, surface); if (env->ExceptionCheck()) { return NULL; } } else { jniThrowNullPointerException(env, "surface"); return NULL; } if (s == NULL) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Surface had no valid native Surface."); return NULL; } return s; } extern "C" { static jint SurfaceUtils_nativeDetectSurfaceType(JNIEnv* env, jobject thiz, jobject surface) { ALOGV("nativeDetectSurfaceType"); sp anw; if ((anw = getNativeWindow(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); return BAD_VALUE; } int32_t fmt = 0; status_t err = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &fmt); if (err != NO_ERROR) { ALOGE("%s: Error while querying surface pixel format %s (%d).", __FUNCTION__, strerror(-err), err); OVERRIDE_SURFACE_ERROR(err); return err; } return fmt; } static jint SurfaceUtils_nativeDetectSurfaceDataspace(JNIEnv* env, jobject thiz, jobject surface) { ALOGV("nativeDetectSurfaceDataspace"); sp anw; if ((anw = getNativeWindow(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); return BAD_VALUE; } int32_t fmt = 0; status_t err = anw->query(anw.get(), NATIVE_WINDOW_DEFAULT_DATASPACE, &fmt); if (err != NO_ERROR) { ALOGE("%s: Error while querying surface dataspace %s (%d).", __FUNCTION__, strerror(-err), err); OVERRIDE_SURFACE_ERROR(err); return err; } return fmt; } static jint SurfaceUtils_nativeDetectSurfaceDimens(JNIEnv* env, jobject thiz, jobject surface, jintArray dimens) { ALOGV("nativeGetSurfaceDimens"); if (dimens == NULL) { ALOGE("%s: Null dimens argument passed to nativeDetectSurfaceDimens", __FUNCTION__); return BAD_VALUE; } if (env->GetArrayLength(dimens) < 2) { ALOGE("%s: Invalid length of dimens argument in nativeDetectSurfaceDimens", __FUNCTION__); return BAD_VALUE; } sp anw; if ((anw = getNativeWindow(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); return BAD_VALUE; } int32_t dimenBuf[2]; status_t err = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, dimenBuf); if (err != NO_ERROR) { ALOGE("%s: Error while querying surface width %s (%d).", __FUNCTION__, strerror(-err), err); OVERRIDE_SURFACE_ERROR(err); return err; } err = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, dimenBuf + 1); if (err != NO_ERROR) { ALOGE("%s: Error while querying surface height %s (%d).", __FUNCTION__, strerror(-err), err); OVERRIDE_SURFACE_ERROR(err); return err; } env->SetIntArrayRegion(dimens, /*start*/ 0, /*length*/ ARRAY_SIZE(dimenBuf), dimenBuf); return NO_ERROR; } static jlong SurfaceUtils_nativeDetectSurfaceUsageFlags(JNIEnv* env, jobject thiz, jobject surface) { ALOGV("nativeDetectSurfaceUsageFlags"); sp anw; if ((anw = getNativeWindow(env, surface)) == NULL) { jniThrowException(env, "java/lang/UnsupportedOperationException", "Could not retrieve native window from surface."); return BAD_VALUE; } uint64_t usage = 0; status_t err = native_window_get_consumer_usage(anw.get(), &usage); if (err != NO_ERROR) { jniThrowException(env, "java/lang/UnsupportedOperationException", "Error while querying surface usage bits"); OVERRIDE_SURFACE_ERROR(err); return err; } return usage; } static jlong SurfaceUtils_nativeGetSurfaceId(JNIEnv* env, jobject thiz, jobject surface) { ALOGV("nativeGetSurfaceId"); sp s; if ((s = getSurface(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native Surface from surface.", __FUNCTION__); return 0; } sp gbp = s->getIGraphicBufferProducer(); if (gbp == NULL) { ALOGE("%s: Could not retrieve IGraphicBufferProducer from surface.", __FUNCTION__); return 0; } sp b = IInterface::asBinder(gbp); if (b == NULL) { ALOGE("%s: Could not retrieve IBinder from surface.", __FUNCTION__); return 0; } /* * FIXME: Use better unique ID for surfaces than native IBinder pointer. Fix also in the camera * service (CameraDeviceClient.h). */ return reinterpret_cast(b.get()); } } // extern "C" static const JNINativeMethod gCameraSurfaceUtilsMethods[] = { {"nativeDetectSurfaceType", "(Landroid/view/Surface;)I", (void*)SurfaceUtils_nativeDetectSurfaceType}, {"nativeDetectSurfaceDataspace", "(Landroid/view/Surface;)I", (void*)SurfaceUtils_nativeDetectSurfaceDataspace}, {"nativeDetectSurfaceDimens", "(Landroid/view/Surface;[I)I", (void*)SurfaceUtils_nativeDetectSurfaceDimens}, {"nativeDetectSurfaceUsageFlags", "(Landroid/view/Surface;)J", (void*)SurfaceUtils_nativeDetectSurfaceUsageFlags}, {"nativeGetSurfaceId", "(Landroid/view/Surface;)J", (void*)SurfaceUtils_nativeGetSurfaceId}, }; // Get all the required offsets in java class and register native functions int register_android_hardware_camera2_utils_SurfaceUtils(JNIEnv* env) { // Register native functions return RegisterMethodsOrDie(env, CAMERA_UTILS_CLASS_NAME, gCameraSurfaceUtilsMethods, NELEM(gCameraSurfaceUtilsMethods)); }