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