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