1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "BLASTBufferQueue"
18 
19 #include <nativehelper/JNIHelp.h>
20 
21 #include <android_runtime/AndroidRuntime.h>
22 #include <android_runtime/android_view_Surface.h>
23 #include <utils/Log.h>
24 #include <utils/RefBase.h>
25 
26 #include <gui/BLASTBufferQueue.h>
27 #include <gui/Surface.h>
28 #include <gui/SurfaceComposerClient.h>
29 #include "core_jni_helpers.h"
30 
31 namespace android {
32 
33 static struct {
34     jclass clazz;
35     jmethodID ctor;
36 } gTransactionClassInfo;
37 
38 struct {
39     jmethodID accept;
40 } gTransactionConsumer;
41 
getenv(JavaVM * vm)42 static JNIEnv* getenv(JavaVM* vm) {
43     JNIEnv* env;
44     auto result = vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
45     if (result == JNI_EDETACHED) {
46         if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
47             LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
48         }
49     } else if (result != JNI_OK) {
50         LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
51     }
52     return env;
53 }
54 
55 struct {
56     jmethodID onTransactionHang;
57 } gTransactionHangCallback;
58 
59 class TransactionHangCallbackWrapper : public LightRefBase<TransactionHangCallbackWrapper> {
60 public:
TransactionHangCallbackWrapper(JNIEnv * env,jobject jobject)61     explicit TransactionHangCallbackWrapper(JNIEnv* env, jobject jobject) {
62         env->GetJavaVM(&mVm);
63         mTransactionHangObject = env->NewGlobalRef(jobject);
64         LOG_ALWAYS_FATAL_IF(!mTransactionHangObject, "Failed to make global ref");
65     }
66 
~TransactionHangCallbackWrapper()67     ~TransactionHangCallbackWrapper() {
68         if (mTransactionHangObject != nullptr) {
69             getenv(mVm)->DeleteGlobalRef(mTransactionHangObject);
70             mTransactionHangObject = nullptr;
71         }
72     }
73 
onTransactionHang(const std::string & reason)74     void onTransactionHang(const std::string& reason) {
75         if (!mTransactionHangObject) {
76             return;
77         }
78         JNIEnv* env = getenv(mVm);
79         ScopedLocalRef<jstring> jReason(env, env->NewStringUTF(reason.c_str()));
80         getenv(mVm)->CallVoidMethod(mTransactionHangObject,
81                                     gTransactionHangCallback.onTransactionHang, jReason.get());
82         DieIfException(env, "Uncaught exception in TransactionHangCallback.");
83     }
84 
85 private:
86     JavaVM* mVm;
87     jobject mTransactionHangObject;
88 };
89 
nativeCreate(JNIEnv * env,jclass clazz,jstring jName,jboolean updateDestinationFrame)90 static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName,
91                           jboolean updateDestinationFrame) {
92     ScopedUtfChars name(env, jName);
93     sp<BLASTBufferQueue> queue = new BLASTBufferQueue(name.c_str(), updateDestinationFrame);
94     queue->incStrong((void*)nativeCreate);
95     return reinterpret_cast<jlong>(queue.get());
96 }
97 
nativeDestroy(JNIEnv * env,jclass clazz,jlong ptr)98 static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
99     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
100     queue->decStrong((void*)nativeCreate);
101 }
102 
nativeGetSurface(JNIEnv * env,jclass clazz,jlong ptr,jboolean includeSurfaceControlHandle)103 static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr,
104                                 jboolean includeSurfaceControlHandle) {
105     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
106     return android_view_Surface_createFromSurface(env,
107                                                   queue->getSurface(includeSurfaceControlHandle));
108 }
109 
110 class JGlobalRefHolder {
111 public:
JGlobalRefHolder(JavaVM * vm,jobject object)112     JGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm), mObject(object) {}
113 
~JGlobalRefHolder()114     virtual ~JGlobalRefHolder() {
115         getenv(mVm)->DeleteGlobalRef(mObject);
116         mObject = nullptr;
117     }
118 
object()119     jobject object() { return mObject; }
vm()120     JavaVM* vm() { return mVm; }
121 
122 private:
123     JGlobalRefHolder(const JGlobalRefHolder&) = delete;
124     void operator=(const JGlobalRefHolder&) = delete;
125 
126     JavaVM* mVm;
127     jobject mObject;
128 };
129 
nativeSyncNextTransaction(JNIEnv * env,jclass clazz,jlong ptr,jobject callback,jboolean acquireSingleBuffer)130 static bool nativeSyncNextTransaction(JNIEnv* env, jclass clazz, jlong ptr, jobject callback,
131                                       jboolean acquireSingleBuffer) {
132     LOG_ALWAYS_FATAL_IF(!callback, "callback passed in to syncNextTransaction must not be NULL");
133 
134     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
135     JavaVM* vm = nullptr;
136     LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
137 
138     auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
139     return queue->syncNextTransaction(
140             [globalCallbackRef](SurfaceComposerClient::Transaction* t) {
141                 JNIEnv* env = getenv(globalCallbackRef->vm());
142                 env->CallVoidMethod(globalCallbackRef->object(), gTransactionConsumer.accept,
143                                     env->NewObject(gTransactionClassInfo.clazz,
144                                                    gTransactionClassInfo.ctor,
145                                                    reinterpret_cast<jlong>(t)));
146             },
147             acquireSingleBuffer);
148 }
149 
nativeStopContinuousSyncTransaction(JNIEnv * env,jclass clazz,jlong ptr)150 static void nativeStopContinuousSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr) {
151     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
152     queue->stopContinuousSyncTransaction();
153 }
154 
nativeClearSyncTransaction(JNIEnv * env,jclass clazz,jlong ptr)155 static void nativeClearSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr) {
156     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
157     queue->clearSyncTransaction();
158 }
159 
nativeUpdate(JNIEnv * env,jclass clazz,jlong ptr,jlong surfaceControl,jlong width,jlong height,jint format)160 static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width,
161                          jlong height, jint format) {
162     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
163     queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height, format);
164 }
165 
nativeMergeWithNextTransaction(JNIEnv *,jclass clazz,jlong ptr,jlong transactionPtr,jlong framenumber)166 static void nativeMergeWithNextTransaction(JNIEnv*, jclass clazz, jlong ptr, jlong transactionPtr,
167                                            jlong framenumber) {
168     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
169     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionPtr);
170     queue->mergeWithNextTransaction(transaction, CC_UNLIKELY(framenumber < 0) ? 0 : framenumber);
171 }
172 
nativeGetLastAcquiredFrameNum(JNIEnv * env,jclass clazz,jlong ptr)173 static jlong nativeGetLastAcquiredFrameNum(JNIEnv* env, jclass clazz, jlong ptr) {
174     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
175     return queue->getLastAcquiredFrameNum();
176 }
177 
nativeApplyPendingTransactions(JNIEnv * env,jclass clazz,jlong ptr,jlong frameNum)178 static void nativeApplyPendingTransactions(JNIEnv* env, jclass clazz, jlong ptr, jlong frameNum) {
179     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
180     queue->applyPendingTransactions(frameNum);
181 }
182 
nativeIsSameSurfaceControl(JNIEnv * env,jclass clazz,jlong ptr,jlong surfaceControl)183 static bool nativeIsSameSurfaceControl(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl) {
184     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
185     return queue->isSameSurfaceControl(reinterpret_cast<SurfaceControl*>(surfaceControl));
186 }
187 
nativeSetTransactionHangCallback(JNIEnv * env,jclass clazz,jlong ptr,jobject transactionHangCallback)188 static void nativeSetTransactionHangCallback(JNIEnv* env, jclass clazz, jlong ptr,
189                                              jobject transactionHangCallback) {
190     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
191     if (transactionHangCallback == nullptr) {
192         queue->setTransactionHangCallback(nullptr);
193     } else {
194         sp<TransactionHangCallbackWrapper> wrapper =
195                 new TransactionHangCallbackWrapper{env, transactionHangCallback};
196         queue->setTransactionHangCallback(
197                 [wrapper](const std::string& reason) { wrapper->onTransactionHang(reason); });
198     }
199 }
200 
nativeGatherPendingTransactions(JNIEnv * env,jclass clazz,jlong ptr,jlong frameNum)201 static jobject nativeGatherPendingTransactions(JNIEnv* env, jclass clazz, jlong ptr,
202                                                jlong frameNum) {
203     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
204     SurfaceComposerClient::Transaction* transaction = queue->gatherPendingTransactions(frameNum);
205     return env->NewObject(gTransactionClassInfo.clazz, gTransactionClassInfo.ctor,
206                           reinterpret_cast<jlong>(transaction));
207 }
208 
209 static const JNINativeMethod gMethods[] = {
210         /* name, signature, funcPtr */
211         // clang-format off
212         {"nativeCreate", "(Ljava/lang/String;Z)J", (void*)nativeCreate},
213         {"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
214         {"nativeDestroy", "(J)V", (void*)nativeDestroy},
215         {"nativeSyncNextTransaction", "(JLjava/util/function/Consumer;Z)Z", (void*)nativeSyncNextTransaction},
216         {"nativeStopContinuousSyncTransaction", "(J)V", (void*)nativeStopContinuousSyncTransaction},
217         {"nativeClearSyncTransaction", "(J)V", (void*)nativeClearSyncTransaction},
218         {"nativeUpdate", "(JJJJI)V", (void*)nativeUpdate},
219         {"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
220         {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
221         {"nativeApplyPendingTransactions", "(JJ)V", (void*)nativeApplyPendingTransactions},
222         {"nativeIsSameSurfaceControl", "(JJ)Z", (void*)nativeIsSameSurfaceControl},
223         {"nativeGatherPendingTransactions", "(JJ)Landroid/view/SurfaceControl$Transaction;", (void*)nativeGatherPendingTransactions},
224         {"nativeSetTransactionHangCallback",
225          "(JLandroid/graphics/BLASTBufferQueue$TransactionHangCallback;)V",
226          (void*)nativeSetTransactionHangCallback},
227         // clang-format on
228 };
229 
register_android_graphics_BLASTBufferQueue(JNIEnv * env)230 int register_android_graphics_BLASTBufferQueue(JNIEnv* env) {
231     int res = jniRegisterNativeMethods(env, "android/graphics/BLASTBufferQueue",
232             gMethods, NELEM(gMethods));
233     LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
234 
235     jclass transactionClazz = FindClassOrDie(env, "android/view/SurfaceControl$Transaction");
236     gTransactionClassInfo.clazz = MakeGlobalRefOrDie(env, transactionClazz);
237     gTransactionClassInfo.ctor =
238             GetMethodIDOrDie(env, gTransactionClassInfo.clazz, "<init>", "(J)V");
239 
240     jclass consumer = FindClassOrDie(env, "java/util/function/Consumer");
241     gTransactionConsumer.accept =
242             GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;)V");
243     jclass transactionHangClass =
244             FindClassOrDie(env, "android/graphics/BLASTBufferQueue$TransactionHangCallback");
245     gTransactionHangCallback.onTransactionHang =
246             GetMethodIDOrDie(env, transactionHangClass, "onTransactionHang",
247                              "(Ljava/lang/String;)V");
248 
249     return 0;
250 }
251 
252 } // namespace android
253