blob: 4474d4cabacba10485443cdb548478999e2a4296 [file] [log] [blame]
/*
* 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 <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_view_Surface.h>
#include <utils/Log.h>
#include <utils/RefBase.h>
#include <gui/BLASTBufferQueue.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#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<void**>(&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<TransactionHangCallbackWrapper> {
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<jstring> 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<BLASTBufferQueue> queue = new BLASTBufferQueue(name.c_str(), updateDestinationFrame);
queue->incStrong((void*)nativeCreate);
return reinterpret_cast<jlong>(queue.get());
}
static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
queue->decStrong((void*)nativeCreate);
}
static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr,
jboolean includeSurfaceControlHandle) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(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<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
JavaVM* vm = nullptr;
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(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<jlong>(t)));
},
acquireSingleBuffer);
}
static void nativeStopContinuousSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
queue->stopContinuousSyncTransaction();
}
static void nativeClearSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
queue->clearSyncTransaction();
}
static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width,
jlong height, jint format) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height, format);
}
static void nativeMergeWithNextTransaction(JNIEnv*, jclass clazz, jlong ptr, jlong transactionPtr,
jlong framenumber) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionPtr);
queue->mergeWithNextTransaction(transaction, CC_UNLIKELY(framenumber < 0) ? 0 : framenumber);
}
static jlong nativeGetLastAcquiredFrameNum(JNIEnv* env, jclass clazz, jlong ptr) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
return queue->getLastAcquiredFrameNum();
}
static void nativeApplyPendingTransactions(JNIEnv* env, jclass clazz, jlong ptr, jlong frameNum) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
queue->applyPendingTransactions(frameNum);
}
static bool nativeIsSameSurfaceControl(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
return queue->isSameSurfaceControl(reinterpret_cast<SurfaceControl*>(surfaceControl));
}
static void nativeSetTransactionHangCallback(JNIEnv* env, jclass clazz, jlong ptr,
jobject transactionHangCallback) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
if (transactionHangCallback == nullptr) {
queue->setTransactionHangCallback(nullptr);
} else {
sp<TransactionHangCallbackWrapper> 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<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
SurfaceComposerClient::Transaction* transaction = queue->gatherPendingTransactions(frameNum);
return env->NewObject(gTransactionClassInfo.clazz, gTransactionClassInfo.ctor,
reinterpret_cast<jlong>(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, "<init>", "(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