• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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