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 <android_runtime/AndroidRuntime.h>
20 #include <android_runtime/android_view_Surface.h>
21 #include <android_util_Binder.h>
22 #include <gui/BLASTBufferQueue.h>
23 #include <gui/Surface.h>
24 #include <gui/SurfaceComposerClient.h>
25 #include <nativehelper/JNIHelp.h>
26 #include <utils/Log.h>
27 #include <utils/RefBase.h>
28
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 ScopedLocalRef<jobject>
143 transactionObject(env,
144 env->NewObject(gTransactionClassInfo.clazz,
145 gTransactionClassInfo.ctor,
146 reinterpret_cast<jlong>(t)));
147 env->CallVoidMethod(globalCallbackRef->object(), gTransactionConsumer.accept,
148 transactionObject.get());
149 },
150 acquireSingleBuffer);
151 }
152
nativeStopContinuousSyncTransaction(JNIEnv * env,jclass clazz,jlong ptr)153 static void nativeStopContinuousSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr) {
154 sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
155 queue->stopContinuousSyncTransaction();
156 }
157
nativeClearSyncTransaction(JNIEnv * env,jclass clazz,jlong ptr)158 static void nativeClearSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr) {
159 sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
160 queue->clearSyncTransaction();
161 }
162
nativeUpdate(JNIEnv * env,jclass clazz,jlong ptr,jlong surfaceControl,jlong width,jlong height,jint format)163 static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width,
164 jlong height, jint format) {
165 sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
166 queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height, format);
167 }
168
nativeMergeWithNextTransaction(JNIEnv *,jclass clazz,jlong ptr,jlong transactionPtr,jlong framenumber)169 static void nativeMergeWithNextTransaction(JNIEnv*, jclass clazz, jlong ptr, jlong transactionPtr,
170 jlong framenumber) {
171 sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
172 auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionPtr);
173 queue->mergeWithNextTransaction(transaction, CC_UNLIKELY(framenumber < 0) ? 0 : framenumber);
174 }
175
nativeGetLastAcquiredFrameNum(JNIEnv * env,jclass clazz,jlong ptr)176 static jlong nativeGetLastAcquiredFrameNum(JNIEnv* env, jclass clazz, jlong ptr) {
177 sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
178 return queue->getLastAcquiredFrameNum();
179 }
180
nativeApplyPendingTransactions(JNIEnv * env,jclass clazz,jlong ptr,jlong frameNum)181 static void nativeApplyPendingTransactions(JNIEnv* env, jclass clazz, jlong ptr, jlong frameNum) {
182 sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
183 queue->applyPendingTransactions(frameNum);
184 }
185
nativeIsSameSurfaceControl(JNIEnv * env,jclass clazz,jlong ptr,jlong surfaceControl)186 static bool nativeIsSameSurfaceControl(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl) {
187 sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
188 return queue->isSameSurfaceControl(reinterpret_cast<SurfaceControl*>(surfaceControl));
189 }
190
nativeSetTransactionHangCallback(JNIEnv * env,jclass clazz,jlong ptr,jobject transactionHangCallback)191 static void nativeSetTransactionHangCallback(JNIEnv* env, jclass clazz, jlong ptr,
192 jobject transactionHangCallback) {
193 sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
194 if (transactionHangCallback == nullptr) {
195 queue->setTransactionHangCallback(nullptr);
196 } else {
197 sp<TransactionHangCallbackWrapper> wrapper =
198 new TransactionHangCallbackWrapper{env, transactionHangCallback};
199 queue->setTransactionHangCallback(
200 [wrapper](const std::string& reason) { wrapper->onTransactionHang(reason); });
201 }
202 }
203
nativeGatherPendingTransactions(JNIEnv * env,jclass clazz,jlong ptr,jlong frameNum)204 static jobject nativeGatherPendingTransactions(JNIEnv* env, jclass clazz, jlong ptr,
205 jlong frameNum) {
206 sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
207 SurfaceComposerClient::Transaction* transaction = queue->gatherPendingTransactions(frameNum);
208 return env->NewObject(gTransactionClassInfo.clazz, gTransactionClassInfo.ctor,
209 reinterpret_cast<jlong>(transaction));
210 }
211
nativeSetApplyToken(JNIEnv * env,jclass clazz,jlong ptr,jobject applyTokenObject)212 static void nativeSetApplyToken(JNIEnv* env, jclass clazz, jlong ptr, jobject applyTokenObject) {
213 sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
214 sp<IBinder> token(ibinderForJavaObject(env, applyTokenObject));
215 return queue->setApplyToken(std::move(token));
216 }
217
218 static const JNINativeMethod gMethods[] = {
219 /* name, signature, funcPtr */
220 // clang-format off
221 {"nativeCreate", "(Ljava/lang/String;Z)J", (void*)nativeCreate},
222 {"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
223 {"nativeDestroy", "(J)V", (void*)nativeDestroy},
224 {"nativeSyncNextTransaction", "(JLjava/util/function/Consumer;Z)Z", (void*)nativeSyncNextTransaction},
225 {"nativeStopContinuousSyncTransaction", "(J)V", (void*)nativeStopContinuousSyncTransaction},
226 {"nativeClearSyncTransaction", "(J)V", (void*)nativeClearSyncTransaction},
227 {"nativeUpdate", "(JJJJI)V", (void*)nativeUpdate},
228 {"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
229 {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
230 {"nativeApplyPendingTransactions", "(JJ)V", (void*)nativeApplyPendingTransactions},
231 {"nativeIsSameSurfaceControl", "(JJ)Z", (void*)nativeIsSameSurfaceControl},
232 {"nativeGatherPendingTransactions", "(JJ)Landroid/view/SurfaceControl$Transaction;", (void*)nativeGatherPendingTransactions},
233 {"nativeSetTransactionHangCallback",
234 "(JLandroid/graphics/BLASTBufferQueue$TransactionHangCallback;)V",
235 (void*)nativeSetTransactionHangCallback},
236 {"nativeSetApplyToken", "(JLandroid/os/IBinder;)V", (void*)nativeSetApplyToken},
237 // clang-format on
238 };
239
register_android_graphics_BLASTBufferQueue(JNIEnv * env)240 int register_android_graphics_BLASTBufferQueue(JNIEnv* env) {
241 int res = jniRegisterNativeMethods(env, "android/graphics/BLASTBufferQueue",
242 gMethods, NELEM(gMethods));
243 LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
244
245 jclass transactionClazz = FindClassOrDie(env, "android/view/SurfaceControl$Transaction");
246 gTransactionClassInfo.clazz = MakeGlobalRefOrDie(env, transactionClazz);
247 gTransactionClassInfo.ctor =
248 GetMethodIDOrDie(env, gTransactionClassInfo.clazz, "<init>", "(J)V");
249
250 jclass consumer = FindClassOrDie(env, "java/util/function/Consumer");
251 gTransactionConsumer.accept =
252 GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;)V");
253 jclass transactionHangClass =
254 FindClassOrDie(env, "android/graphics/BLASTBufferQueue$TransactionHangCallback");
255 gTransactionHangCallback.onTransactionHang =
256 GetMethodIDOrDie(env, transactionHangClass, "onTransactionHang",
257 "(Ljava/lang/String;)V");
258
259 return 0;
260 }
261
262 } // namespace android
263