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     jfieldID useIdentityTransform;
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             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         checkAndClearException(env, "accept");
122         env->DeleteLocalRef(jhardwareBuffer);
123         env->DeleteLocalRef(screenshotHardwareBuffer);
124         return binder::Status::ok();
125     }
126 
127 private:
128     jweak mConsumerWeak;
129     JavaVM* mVm;
130 
getenv()131     JNIEnv* getenv() {
132         JNIEnv* env;
133         if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
134             if (mVm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
135                 LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
136             }
137         }
138         return env;
139     }
140 };
141 
getCaptureArgs(JNIEnv * env,jobject captureArgsObject,CaptureArgs & captureArgs)142 static void getCaptureArgs(JNIEnv* env, jobject captureArgsObject, CaptureArgs& captureArgs) {
143     captureArgs.pixelFormat = static_cast<ui::PixelFormat>(
144             env->GetIntField(captureArgsObject, gCaptureArgsClassInfo.pixelFormat));
145     captureArgs.sourceCrop =
146             JNICommon::rectFromObj(env,
147                                    env->GetObjectField(captureArgsObject,
148                                                        gCaptureArgsClassInfo.sourceCrop));
149     captureArgs.frameScaleX =
150             env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScaleX);
151     captureArgs.frameScaleY =
152             env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScaleY);
153     captureArgs.captureSecureLayers =
154             env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.captureSecureLayers);
155     captureArgs.allowProtected =
156             env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.allowProtected);
157     captureArgs.uid = env->GetLongField(captureArgsObject, gCaptureArgsClassInfo.uid);
158     captureArgs.grayscale =
159             env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.grayscale);
160 
161     jlongArray excludeObjectArray = reinterpret_cast<jlongArray>(
162             env->CallObjectMethod(captureArgsObject, gCaptureArgsClassInfo.getNativeExcludeLayers));
163     if (excludeObjectArray != nullptr) {
164         ScopedLongArrayRO excludeArray(env, excludeObjectArray);
165         const jsize len = excludeArray.size();
166         captureArgs.excludeHandles.reserve(len);
167 
168         for (jsize i = 0; i < len; i++) {
169             auto excludeObject = reinterpret_cast<SurfaceControl*>(excludeArray[i]);
170             if (excludeObject == nullptr) {
171                 jniThrowNullPointerException(env, "Exclude layer is null");
172                 return;
173             }
174             captureArgs.excludeHandles.emplace(excludeObject->getHandle());
175         }
176     }
177     captureArgs.hintForSeamlessTransition =
178             env->GetBooleanField(captureArgsObject,
179                                  gCaptureArgsClassInfo.hintForSeamlessTransition);
180 }
181 
displayCaptureArgsFromObject(JNIEnv * env,jobject displayCaptureArgsObject)182 static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env,
183                                                        jobject displayCaptureArgsObject) {
184     DisplayCaptureArgs captureArgs;
185     getCaptureArgs(env, displayCaptureArgsObject, captureArgs);
186 
187     captureArgs.displayToken =
188             ibinderForJavaObject(env,
189                                  env->GetObjectField(displayCaptureArgsObject,
190                                                      gDisplayCaptureArgsClassInfo.displayToken));
191     captureArgs.width =
192             env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.width);
193     captureArgs.height =
194             env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.height);
195     captureArgs.useIdentityTransform =
196             env->GetBooleanField(displayCaptureArgsObject,
197                                  gDisplayCaptureArgsClassInfo.useIdentityTransform);
198     return captureArgs;
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)215 static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject,
216                                 jlong screenCaptureListenerObject) {
217     LayerCaptureArgs captureArgs;
218     getCaptureArgs(env, layerCaptureArgsObject, 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     captureArgs.layerHandle = layer->getHandle();
227     captureArgs.childrenOnly =
228             env->GetBooleanField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.childrenOnly);
229 
230     sp<gui::IScreenCaptureListener> captureListener =
231             reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject);
232     return ScreenshotClient::captureLayers(captureArgs, captureListener);
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;J)I",
287             (void*)nativeCaptureLayers },
288     {"nativeCreateScreenCaptureListener", "(Ljava/util/function/Consumer;)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     gDisplayCaptureArgsClassInfo.useIdentityTransform =
327             GetFieldIDOrDie(env, displayCaptureArgsClazz, "mUseIdentityTransform", "Z");
328 
329     jclass layerCaptureArgsClazz =
330             FindClassOrDie(env, "android/window/ScreenCapture$LayerCaptureArgs");
331     gLayerCaptureArgsClassInfo.layer =
332             GetFieldIDOrDie(env, layerCaptureArgsClazz, "mNativeLayer", "J");
333     gLayerCaptureArgsClassInfo.childrenOnly =
334             GetFieldIDOrDie(env, layerCaptureArgsClazz, "mChildrenOnly", "Z");
335 
336     jclass consumer = FindClassOrDie(env, "java/util/function/Consumer");
337     gConsumerClassInfo.accept = GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;)V");
338 
339     jclass screenshotGraphicsBufferClazz =
340             FindClassOrDie(env, "android/window/ScreenCapture$ScreenshotHardwareBuffer");
341     gScreenshotHardwareBufferClassInfo.clazz =
342             MakeGlobalRefOrDie(env, screenshotGraphicsBufferClazz);
343     gScreenshotHardwareBufferClassInfo.builder =
344             GetStaticMethodIDOrDie(env, screenshotGraphicsBufferClazz, "createFromNative",
345                                    "(Landroid/hardware/HardwareBuffer;IZZ)Landroid/window/"
346                                    "ScreenCapture$ScreenshotHardwareBuffer;");
347 
348     return err;
349 }
350 
351 } // namespace android
352