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