/* * Copyright (C) 2019 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 "BLASTBufferQueue" #include #include #include #include #include #include #include #include #include "core_jni_helpers.h" namespace android { static struct { jclass clazz; jmethodID ctor; } gTransactionClassInfo; struct { jmethodID accept; } gTransactionConsumer; static JNIEnv* getenv(JavaVM* vm) { JNIEnv* env; auto result = vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); if (result == JNI_EDETACHED) { if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!"); } } else if (result != JNI_OK) { LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm); } return env; } struct { jmethodID onTransactionHang; } gTransactionHangCallback; class TransactionHangCallbackWrapper : public LightRefBase { public: explicit TransactionHangCallbackWrapper(JNIEnv* env, jobject jobject) { env->GetJavaVM(&mVm); mTransactionHangObject = env->NewGlobalRef(jobject); LOG_ALWAYS_FATAL_IF(!mTransactionHangObject, "Failed to make global ref"); } ~TransactionHangCallbackWrapper() { if (mTransactionHangObject != nullptr) { getenv(mVm)->DeleteGlobalRef(mTransactionHangObject); mTransactionHangObject = nullptr; } } void onTransactionHang(const std::string& reason) { if (!mTransactionHangObject) { return; } JNIEnv* env = getenv(mVm); ScopedLocalRef jReason(env, env->NewStringUTF(reason.c_str())); getenv(mVm)->CallVoidMethod(mTransactionHangObject, gTransactionHangCallback.onTransactionHang, jReason.get()); DieIfException(env, "Uncaught exception in TransactionHangCallback."); } private: JavaVM* mVm; jobject mTransactionHangObject; }; static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName, jboolean updateDestinationFrame) { ScopedUtfChars name(env, jName); sp queue = new BLASTBufferQueue(name.c_str(), updateDestinationFrame); queue->incStrong((void*)nativeCreate); return reinterpret_cast(queue.get()); } static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) { sp queue = reinterpret_cast(ptr); queue->decStrong((void*)nativeCreate); } static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr, jboolean includeSurfaceControlHandle) { sp queue = reinterpret_cast(ptr); return android_view_Surface_createFromSurface(env, queue->getSurface(includeSurfaceControlHandle)); } class JGlobalRefHolder { public: JGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm), mObject(object) {} virtual ~JGlobalRefHolder() { getenv(mVm)->DeleteGlobalRef(mObject); mObject = nullptr; } jobject object() { return mObject; } JavaVM* vm() { return mVm; } private: JGlobalRefHolder(const JGlobalRefHolder&) = delete; void operator=(const JGlobalRefHolder&) = delete; JavaVM* mVm; jobject mObject; }; static bool nativeSyncNextTransaction(JNIEnv* env, jclass clazz, jlong ptr, jobject callback, jboolean acquireSingleBuffer) { LOG_ALWAYS_FATAL_IF(!callback, "callback passed in to syncNextTransaction must not be NULL"); sp queue = reinterpret_cast(ptr); JavaVM* vm = nullptr; LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); auto globalCallbackRef = std::make_shared(vm, env->NewGlobalRef(callback)); return queue->syncNextTransaction( [globalCallbackRef](SurfaceComposerClient::Transaction* t) { JNIEnv* env = getenv(globalCallbackRef->vm()); env->CallVoidMethod(globalCallbackRef->object(), gTransactionConsumer.accept, env->NewObject(gTransactionClassInfo.clazz, gTransactionClassInfo.ctor, reinterpret_cast(t))); }, acquireSingleBuffer); } static void nativeStopContinuousSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr) { sp queue = reinterpret_cast(ptr); queue->stopContinuousSyncTransaction(); } static void nativeClearSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr) { sp queue = reinterpret_cast(ptr); queue->clearSyncTransaction(); } static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width, jlong height, jint format) { sp queue = reinterpret_cast(ptr); queue->update(reinterpret_cast(surfaceControl), width, height, format); } static void nativeMergeWithNextTransaction(JNIEnv*, jclass clazz, jlong ptr, jlong transactionPtr, jlong framenumber) { sp queue = reinterpret_cast(ptr); auto transaction = reinterpret_cast(transactionPtr); queue->mergeWithNextTransaction(transaction, CC_UNLIKELY(framenumber < 0) ? 0 : framenumber); } static jlong nativeGetLastAcquiredFrameNum(JNIEnv* env, jclass clazz, jlong ptr) { sp queue = reinterpret_cast(ptr); return queue->getLastAcquiredFrameNum(); } static void nativeApplyPendingTransactions(JNIEnv* env, jclass clazz, jlong ptr, jlong frameNum) { sp queue = reinterpret_cast(ptr); queue->applyPendingTransactions(frameNum); } static bool nativeIsSameSurfaceControl(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl) { sp queue = reinterpret_cast(ptr); return queue->isSameSurfaceControl(reinterpret_cast(surfaceControl)); } static void nativeSetTransactionHangCallback(JNIEnv* env, jclass clazz, jlong ptr, jobject transactionHangCallback) { sp queue = reinterpret_cast(ptr); if (transactionHangCallback == nullptr) { queue->setTransactionHangCallback(nullptr); } else { sp wrapper = new TransactionHangCallbackWrapper{env, transactionHangCallback}; queue->setTransactionHangCallback( [wrapper](const std::string& reason) { wrapper->onTransactionHang(reason); }); } } static jobject nativeGatherPendingTransactions(JNIEnv* env, jclass clazz, jlong ptr, jlong frameNum) { sp queue = reinterpret_cast(ptr); SurfaceComposerClient::Transaction* transaction = queue->gatherPendingTransactions(frameNum); return env->NewObject(gTransactionClassInfo.clazz, gTransactionClassInfo.ctor, reinterpret_cast(transaction)); } static const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ // clang-format off {"nativeCreate", "(Ljava/lang/String;Z)J", (void*)nativeCreate}, {"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface}, {"nativeDestroy", "(J)V", (void*)nativeDestroy}, {"nativeSyncNextTransaction", "(JLjava/util/function/Consumer;Z)Z", (void*)nativeSyncNextTransaction}, {"nativeStopContinuousSyncTransaction", "(J)V", (void*)nativeStopContinuousSyncTransaction}, {"nativeClearSyncTransaction", "(J)V", (void*)nativeClearSyncTransaction}, {"nativeUpdate", "(JJJJI)V", (void*)nativeUpdate}, {"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction}, {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum}, {"nativeApplyPendingTransactions", "(JJ)V", (void*)nativeApplyPendingTransactions}, {"nativeIsSameSurfaceControl", "(JJ)Z", (void*)nativeIsSameSurfaceControl}, {"nativeGatherPendingTransactions", "(JJ)Landroid/view/SurfaceControl$Transaction;", (void*)nativeGatherPendingTransactions}, {"nativeSetTransactionHangCallback", "(JLandroid/graphics/BLASTBufferQueue$TransactionHangCallback;)V", (void*)nativeSetTransactionHangCallback}, // clang-format on }; int register_android_graphics_BLASTBufferQueue(JNIEnv* env) { int res = jniRegisterNativeMethods(env, "android/graphics/BLASTBufferQueue", gMethods, NELEM(gMethods)); LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); jclass transactionClazz = FindClassOrDie(env, "android/view/SurfaceControl$Transaction"); gTransactionClassInfo.clazz = MakeGlobalRefOrDie(env, transactionClazz); gTransactionClassInfo.ctor = GetMethodIDOrDie(env, gTransactionClassInfo.clazz, "", "(J)V"); jclass consumer = FindClassOrDie(env, "java/util/function/Consumer"); gTransactionConsumer.accept = GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;)V"); jclass transactionHangClass = FindClassOrDie(env, "android/graphics/BLASTBufferQueue$TransactionHangCallback"); gTransactionHangCallback.onTransactionHang = GetMethodIDOrDie(env, transactionHangClass, "onTransactionHang", "(Ljava/lang/String;)V"); return 0; } } // namespace android