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