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