1 /*
2 * Copyright (C) 2022 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 "ScreenCapture"
18 // #define LOG_NDEBUG 0
19
20 #include <android/gui/BnScreenCaptureListener.h>
21 #include <android_runtime/android_hardware_HardwareBuffer.h>
22 #include <gui/SurfaceComposerClient.h>
23 #include <jni.h>
24 #include <nativehelper/JNIHelp.h>
25 #include <nativehelper/ScopedPrimitiveArray.h>
26 #include <ui/GraphicTypes.h>
27
28 #include "android_os_Parcel.h"
29 #include "android_util_Binder.h"
30 #include "core_jni_helpers.h"
31 #include "jni_common.h"
32
33 // ----------------------------------------------------------------------------
34
35 namespace android {
36
37 using gui::CaptureArgs;
38
39 static struct {
40 jfieldID pixelFormat;
41 jfieldID sourceCrop;
42 jfieldID frameScaleX;
43 jfieldID frameScaleY;
44 jfieldID captureSecureLayers;
45 jfieldID allowProtected;
46 jfieldID uid;
47 jfieldID grayscale;
48 jmethodID getNativeExcludeLayers;
49 jfieldID hintForSeamlessTransition;
50 } gCaptureArgsClassInfo;
51
52 static struct {
53 jfieldID displayToken;
54 jfieldID width;
55 jfieldID height;
56 } gDisplayCaptureArgsClassInfo;
57
58 static struct {
59 jfieldID layer;
60 jfieldID childrenOnly;
61 } gLayerCaptureArgsClassInfo;
62
63 static struct {
64 jmethodID accept;
65 } gConsumerClassInfo;
66
67 static struct {
68 jclass clazz;
69 jmethodID builder;
70 } gScreenshotHardwareBufferClassInfo;
71
checkAndClearException(JNIEnv * env,const char * methodName)72 static void checkAndClearException(JNIEnv* env, const char* methodName) {
73 if (env->ExceptionCheck()) {
74 ALOGE("An exception was thrown by callback '%s'.", methodName);
75 env->ExceptionClear();
76 }
77 }
78
79 class ScreenCaptureListenerWrapper : public gui::BnScreenCaptureListener {
80 public:
ScreenCaptureListenerWrapper(JNIEnv * env,jobject jobject)81 explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) {
82 env->GetJavaVM(&mVm);
83 mConsumerWeak = env->NewWeakGlobalRef(jobject);
84 }
85
~ScreenCaptureListenerWrapper()86 ~ScreenCaptureListenerWrapper() {
87 if (mConsumerWeak) {
88 getenv()->DeleteWeakGlobalRef(mConsumerWeak);
89 mConsumerWeak = nullptr;
90 }
91 }
92
onScreenCaptureCompleted(const gui::ScreenCaptureResults & captureResults)93 binder::Status onScreenCaptureCompleted(
94 const gui::ScreenCaptureResults& captureResults) override {
95 JNIEnv* env = getenv();
96
97 ScopedLocalRef<jobject> consumer{env, env->NewLocalRef(mConsumerWeak)};
98 if (consumer == nullptr) {
99 ALOGE("ScreenCaptureListenerWrapper consumer not alive.");
100 return binder::Status::ok();
101 }
102
103 if (!captureResults.fenceResult.ok() || captureResults.buffer == nullptr) {
104 env->CallVoidMethod(consumer.get(), gConsumerClassInfo.accept, nullptr,
105 fenceStatus(captureResults.fenceResult));
106 checkAndClearException(env, "accept");
107 return binder::Status::ok();
108 }
109 captureResults.fenceResult.value()->waitForever(LOG_TAG);
110 jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
111 env, captureResults.buffer->toAHardwareBuffer());
112 jobject screenshotHardwareBuffer =
113 env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
114 gScreenshotHardwareBufferClassInfo.builder,
115 jhardwareBuffer,
116 static_cast<jint>(captureResults.capturedDataspace),
117 captureResults.capturedSecureLayers,
118 captureResults.capturedHdrLayers);
119 checkAndClearException(env, "builder");
120 env->CallVoidMethod(consumer.get(), gConsumerClassInfo.accept, screenshotHardwareBuffer,
121 fenceStatus(captureResults.fenceResult));
122 checkAndClearException(env, "accept");
123 env->DeleteLocalRef(jhardwareBuffer);
124 env->DeleteLocalRef(screenshotHardwareBuffer);
125 return binder::Status::ok();
126 }
127
128 private:
129 jweak mConsumerWeak;
130 JavaVM* mVm;
131
getenv()132 JNIEnv* getenv() {
133 JNIEnv* env;
134 if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
135 if (mVm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
136 LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
137 }
138 }
139 return env;
140 }
141 };
142
getCaptureArgs(JNIEnv * env,jobject captureArgsObject,CaptureArgs & captureArgs)143 static void getCaptureArgs(JNIEnv* env, jobject captureArgsObject, CaptureArgs& captureArgs) {
144 captureArgs.pixelFormat = static_cast<ui::PixelFormat>(
145 env->GetIntField(captureArgsObject, gCaptureArgsClassInfo.pixelFormat));
146 captureArgs.sourceCrop =
147 JNICommon::rectFromObj(env,
148 env->GetObjectField(captureArgsObject,
149 gCaptureArgsClassInfo.sourceCrop));
150 captureArgs.frameScaleX =
151 env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScaleX);
152 captureArgs.frameScaleY =
153 env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScaleY);
154 captureArgs.captureSecureLayers =
155 env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.captureSecureLayers);
156 captureArgs.allowProtected =
157 env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.allowProtected);
158 captureArgs.uid = env->GetLongField(captureArgsObject, gCaptureArgsClassInfo.uid);
159 captureArgs.grayscale =
160 env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.grayscale);
161
162 jlongArray excludeObjectArray = reinterpret_cast<jlongArray>(
163 env->CallObjectMethod(captureArgsObject, gCaptureArgsClassInfo.getNativeExcludeLayers));
164 if (excludeObjectArray != nullptr) {
165 ScopedLongArrayRO excludeArray(env, excludeObjectArray);
166 const jsize len = excludeArray.size();
167 captureArgs.excludeHandles.reserve(len);
168
169 for (jsize i = 0; i < len; i++) {
170 auto excludeObject = reinterpret_cast<SurfaceControl*>(excludeArray[i]);
171 if (excludeObject == nullptr) {
172 jniThrowNullPointerException(env, "Exclude layer is null");
173 return;
174 }
175 captureArgs.excludeHandles.emplace(excludeObject->getHandle());
176 }
177 }
178 captureArgs.hintForSeamlessTransition =
179 env->GetBooleanField(captureArgsObject,
180 gCaptureArgsClassInfo.hintForSeamlessTransition);
181 }
182
displayCaptureArgsFromObject(JNIEnv * env,jobject displayCaptureArgsObject)183 static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env,
184 jobject displayCaptureArgsObject) {
185 DisplayCaptureArgs captureArgs;
186 getCaptureArgs(env, displayCaptureArgsObject, captureArgs);
187
188 captureArgs.displayToken =
189 ibinderForJavaObject(env,
190 env->GetObjectField(displayCaptureArgsObject,
191 gDisplayCaptureArgsClassInfo.displayToken));
192 captureArgs.width =
193 env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.width);
194 captureArgs.height =
195 env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.height);
196 return captureArgs;
197 }
198
nativeCaptureDisplay(JNIEnv * env,jclass clazz,jobject displayCaptureArgsObject,jlong screenCaptureListenerObject)199 static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject,
200 jlong screenCaptureListenerObject) {
201 const DisplayCaptureArgs captureArgs =
202 displayCaptureArgsFromObject(env, displayCaptureArgsObject);
203
204 if (captureArgs.displayToken == nullptr) {
205 return BAD_VALUE;
206 }
207
208 sp<gui::IScreenCaptureListener> captureListener =
209 reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject);
210 return ScreenshotClient::captureDisplay(captureArgs, captureListener);
211 }
212
nativeCaptureLayers(JNIEnv * env,jclass clazz,jobject layerCaptureArgsObject,jlong screenCaptureListenerObject,jboolean sync)213 static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject,
214 jlong screenCaptureListenerObject, jboolean sync) {
215 LayerCaptureArgs captureArgs;
216 getCaptureArgs(env, layerCaptureArgsObject, captureArgs);
217
218 SurfaceControl* layer = reinterpret_cast<SurfaceControl*>(
219 env->GetLongField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.layer));
220 if (layer == nullptr) {
221 return BAD_VALUE;
222 }
223
224 captureArgs.layerHandle = layer->getHandle();
225 captureArgs.childrenOnly =
226 env->GetBooleanField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.childrenOnly);
227
228 sp<gui::IScreenCaptureListener> captureListener =
229 reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject);
230 return ScreenshotClient::captureLayers(captureArgs, captureListener, sync);
231 }
232
nativeCreateScreenCaptureListener(JNIEnv * env,jclass clazz,jobject consumerObj)233 static jlong nativeCreateScreenCaptureListener(JNIEnv* env, jclass clazz, jobject consumerObj) {
234 sp<gui::IScreenCaptureListener> listener =
235 sp<ScreenCaptureListenerWrapper>::make(env, consumerObj);
236 listener->incStrong((void*)nativeCreateScreenCaptureListener);
237 return reinterpret_cast<jlong>(listener.get());
238 }
239
nativeWriteListenerToParcel(JNIEnv * env,jclass clazz,jlong nativeObject,jobject parcelObj)240 static void nativeWriteListenerToParcel(JNIEnv* env, jclass clazz, jlong nativeObject,
241 jobject parcelObj) {
242 Parcel* parcel = parcelForJavaObject(env, parcelObj);
243 if (parcel == NULL) {
244 jniThrowNullPointerException(env, NULL);
245 return;
246 }
247 ScreenCaptureListenerWrapper* const self =
248 reinterpret_cast<ScreenCaptureListenerWrapper*>(nativeObject);
249 if (self != nullptr) {
250 parcel->writeStrongBinder(IInterface::asBinder(self));
251 }
252 }
253
nativeReadListenerFromParcel(JNIEnv * env,jclass clazz,jobject parcelObj)254 static jlong nativeReadListenerFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
255 Parcel* parcel = parcelForJavaObject(env, parcelObj);
256 if (parcel == NULL) {
257 jniThrowNullPointerException(env, NULL);
258 return 0;
259 }
260 sp<gui::IScreenCaptureListener> listener =
261 interface_cast<gui::IScreenCaptureListener>(parcel->readStrongBinder());
262 if (listener == nullptr) {
263 return 0;
264 }
265 listener->incStrong((void*)nativeCreateScreenCaptureListener);
266 return reinterpret_cast<jlong>(listener.get());
267 }
268
destroyNativeListener(void * ptr)269 void destroyNativeListener(void* ptr) {
270 ScreenCaptureListenerWrapper* listener = reinterpret_cast<ScreenCaptureListenerWrapper*>(ptr);
271 listener->decStrong((void*)nativeCreateScreenCaptureListener);
272 }
273
getNativeListenerFinalizer(JNIEnv * env,jclass clazz)274 static jlong getNativeListenerFinalizer(JNIEnv* env, jclass clazz) {
275 return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeListener));
276 }
277
278 // ----------------------------------------------------------------------------
279
280 static const JNINativeMethod sScreenCaptureMethods[] = {
281 // clang-format off
282 {"nativeCaptureDisplay", "(Landroid/window/ScreenCapture$DisplayCaptureArgs;J)I",
283 (void*)nativeCaptureDisplay },
284 {"nativeCaptureLayers", "(Landroid/window/ScreenCapture$LayerCaptureArgs;JZ)I",
285 (void*)nativeCaptureLayers },
286 {"nativeCreateScreenCaptureListener", "(Ljava/util/function/ObjIntConsumer;)J",
287 (void*)nativeCreateScreenCaptureListener },
288 {"nativeWriteListenerToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteListenerToParcel },
289 {"nativeReadListenerFromParcel", "(Landroid/os/Parcel;)J",
290 (void*)nativeReadListenerFromParcel },
291 {"getNativeListenerFinalizer", "()J", (void*)getNativeListenerFinalizer },
292 // clang-format on
293 };
294
register_android_window_ScreenCapture(JNIEnv * env)295 int register_android_window_ScreenCapture(JNIEnv* env) {
296 int err = RegisterMethodsOrDie(env, "android/window/ScreenCapture", sScreenCaptureMethods,
297 NELEM(sScreenCaptureMethods));
298
299 jclass captureArgsClazz = FindClassOrDie(env, "android/window/ScreenCapture$CaptureArgs");
300 gCaptureArgsClassInfo.pixelFormat = GetFieldIDOrDie(env, captureArgsClazz, "mPixelFormat", "I");
301 gCaptureArgsClassInfo.sourceCrop =
302 GetFieldIDOrDie(env, captureArgsClazz, "mSourceCrop", "Landroid/graphics/Rect;");
303 gCaptureArgsClassInfo.frameScaleX = GetFieldIDOrDie(env, captureArgsClazz, "mFrameScaleX", "F");
304 gCaptureArgsClassInfo.frameScaleY = GetFieldIDOrDie(env, captureArgsClazz, "mFrameScaleY", "F");
305 gCaptureArgsClassInfo.captureSecureLayers =
306 GetFieldIDOrDie(env, captureArgsClazz, "mCaptureSecureLayers", "Z");
307 gCaptureArgsClassInfo.allowProtected =
308 GetFieldIDOrDie(env, captureArgsClazz, "mAllowProtected", "Z");
309 gCaptureArgsClassInfo.uid = GetFieldIDOrDie(env, captureArgsClazz, "mUid", "J");
310 gCaptureArgsClassInfo.grayscale = GetFieldIDOrDie(env, captureArgsClazz, "mGrayscale", "Z");
311 gCaptureArgsClassInfo.getNativeExcludeLayers =
312 GetMethodIDOrDie(env, captureArgsClazz, "getNativeExcludeLayers", "()[J");
313 gCaptureArgsClassInfo.hintForSeamlessTransition =
314 GetFieldIDOrDie(env, captureArgsClazz, "mHintForSeamlessTransition", "Z");
315
316 jclass displayCaptureArgsClazz =
317 FindClassOrDie(env, "android/window/ScreenCapture$DisplayCaptureArgs");
318 gDisplayCaptureArgsClassInfo.displayToken =
319 GetFieldIDOrDie(env, displayCaptureArgsClazz, "mDisplayToken", "Landroid/os/IBinder;");
320 gDisplayCaptureArgsClassInfo.width =
321 GetFieldIDOrDie(env, displayCaptureArgsClazz, "mWidth", "I");
322 gDisplayCaptureArgsClassInfo.height =
323 GetFieldIDOrDie(env, displayCaptureArgsClazz, "mHeight", "I");
324
325 jclass layerCaptureArgsClazz =
326 FindClassOrDie(env, "android/window/ScreenCapture$LayerCaptureArgs");
327 gLayerCaptureArgsClassInfo.layer =
328 GetFieldIDOrDie(env, layerCaptureArgsClazz, "mNativeLayer", "J");
329 gLayerCaptureArgsClassInfo.childrenOnly =
330 GetFieldIDOrDie(env, layerCaptureArgsClazz, "mChildrenOnly", "Z");
331
332 jclass consumer = FindClassOrDie(env, "java/util/function/ObjIntConsumer");
333 gConsumerClassInfo.accept = GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;I)V");
334
335 jclass screenshotGraphicsBufferClazz =
336 FindClassOrDie(env, "android/window/ScreenCapture$ScreenshotHardwareBuffer");
337 gScreenshotHardwareBufferClassInfo.clazz =
338 MakeGlobalRefOrDie(env, screenshotGraphicsBufferClazz);
339 gScreenshotHardwareBufferClassInfo.builder =
340 GetStaticMethodIDOrDie(env, screenshotGraphicsBufferClazz, "createFromNative",
341 "(Landroid/hardware/HardwareBuffer;IZZ)Landroid/window/"
342 "ScreenCapture$ScreenshotHardwareBuffer;");
343
344 return err;
345 }
346
347 } // namespace android
348