1 /*
2  * Copyright (C) 2011 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 "InputWindowHandle"
18 
19 #include "android_hardware_input_InputWindowHandle.h"
20 
21 #include <android/graphics/matrix.h>
22 #include <android/graphics/region.h>
23 #include <android_runtime/AndroidRuntime.h>
24 #include <android_runtime/Log.h>
25 #include <binder/IPCThreadState.h>
26 #include <ftl/flags.h>
27 #include <gui/SurfaceControl.h>
28 #include <gui/WindowInfo.h>
29 #include <nativehelper/JNIHelp.h>
30 #include <ui/Region.h>
31 #include <utils/threads.h>
32 
33 #include "SkRegion.h"
34 #include "android_hardware_input_InputApplicationHandle.h"
35 #include "android_util_Binder.h"
36 #include "core_jni_helpers.h"
37 #include "jni.h"
38 #include "jni_common.h"
39 
40 namespace android {
41 
42 using gui::TouchOcclusionMode;
43 using gui::WindowInfo;
44 
45 struct WeakRefHandleField {
46     jfieldID ctrl;
47     jmethodID get;
48     jfieldID mNativeObject;
49 };
50 
51 static struct {
52     jclass clazz;
53     jmethodID ctor;
54     jfieldID ptr;
55     jfieldID inputApplicationHandle;
56     jfieldID token;
57     jfieldID name;
58     jfieldID layoutParamsFlags;
59     jfieldID layoutParamsType;
60     jfieldID dispatchingTimeoutMillis;
61     jfieldID frame;
62     jfieldID contentSize;
63     jfieldID surfaceInset;
64     jfieldID scaleFactor;
65     jfieldID touchableRegion;
66     jfieldID touchOcclusionMode;
67     jfieldID ownerPid;
68     jfieldID ownerUid;
69     jfieldID packageName;
70     jfieldID inputConfig;
71     jfieldID displayId;
72     jfieldID replaceTouchableRegionWithCrop;
73     WeakRefHandleField touchableRegionSurfaceControl;
74     jfieldID transform;
75     jfieldID windowToken;
76     jfieldID focusTransferTarget;
77     jfieldID alpha;
78     jfieldID canOccludePresentation;
79 } gInputWindowHandleClassInfo;
80 
81 static struct {
82     jclass clazz;
83     jmethodID ctor;
84 } gRegionClassInfo;
85 
86 static Mutex gHandleMutex;
87 
88 
89 // --- NativeInputWindowHandle ---
90 
NativeInputWindowHandle(jweak objWeak)91 NativeInputWindowHandle::NativeInputWindowHandle(jweak objWeak) :
92         mObjWeak(objWeak) {
93 }
94 
~NativeInputWindowHandle()95 NativeInputWindowHandle::~NativeInputWindowHandle() {
96     JNIEnv* env = AndroidRuntime::getJNIEnv();
97     env->DeleteWeakGlobalRef(mObjWeak);
98 
99     // Clear the weak reference to the layer handle and flush any binder ref count operations so we
100     // do not hold on to any binder references.
101     // TODO(b/139697085) remove this after it can be flushed automatically
102     mInfo.touchableRegionCropHandle.clear();
103     IPCThreadState::self()->flushCommands();
104 }
105 
getInputWindowHandleObjLocalRef(JNIEnv * env)106 jobject NativeInputWindowHandle::getInputWindowHandleObjLocalRef(JNIEnv* env) {
107     return env->NewLocalRef(mObjWeak);
108 }
109 
updateInfo()110 bool NativeInputWindowHandle::updateInfo() {
111     JNIEnv* env = AndroidRuntime::getJNIEnv();
112     jobject obj = env->NewLocalRef(mObjWeak);
113     if (!obj) {
114         releaseChannel();
115         return false;
116     }
117 
118     mInfo.touchableRegion.clear();
119 
120     jobject tokenObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.token);
121     if (tokenObj) {
122         mInfo.token = ibinderForJavaObject(env, tokenObj);
123         env->DeleteLocalRef(tokenObj);
124     } else {
125         mInfo.token.clear();
126     }
127 
128     mInfo.name = getStringField(env, obj, gInputWindowHandleClassInfo.name, "<null>");
129 
130     mInfo.dispatchingTimeout = std::chrono::milliseconds(
131             env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutMillis));
132 
133     ScopedLocalRef<jobject> frameObj(env,
134                                      env->GetObjectField(obj, gInputWindowHandleClassInfo.frame));
135     mInfo.frame = JNICommon::rectFromObj(env, frameObj.get());
136 
137     mInfo.surfaceInset = env->GetIntField(obj,
138             gInputWindowHandleClassInfo.surfaceInset);
139     mInfo.globalScaleFactor = env->GetFloatField(obj,
140             gInputWindowHandleClassInfo.scaleFactor);
141 
142     jobject regionObj = env->GetObjectField(obj,
143             gInputWindowHandleClassInfo.touchableRegion);
144     if (regionObj) {
145         for (graphics::RegionIterator it(env, regionObj); !it.isDone(); it.next()) {
146             ARect rect = it.getRect();
147             mInfo.addTouchableRegion(Rect(rect.left, rect.top, rect.right, rect.bottom));
148         }
149         env->DeleteLocalRef(regionObj);
150     }
151 
152     const auto flags = ftl::Flags<WindowInfo::Flag>(
153             env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsFlags));
154     const auto type = static_cast<WindowInfo::Type>(
155             env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsType));
156     mInfo.layoutParamsFlags = flags;
157     mInfo.layoutParamsType = type;
158 
159     mInfo.inputConfig = static_cast<gui::WindowInfo::InputConfig>(
160             env->GetIntField(obj, gInputWindowHandleClassInfo.inputConfig));
161 
162     mInfo.touchOcclusionMode = static_cast<TouchOcclusionMode>(
163             env->GetIntField(obj, gInputWindowHandleClassInfo.touchOcclusionMode));
164     mInfo.ownerPid = gui::Pid{env->GetIntField(obj, gInputWindowHandleClassInfo.ownerPid)};
165     mInfo.ownerUid = gui::Uid{
166             static_cast<uid_t>(env->GetIntField(obj, gInputWindowHandleClassInfo.ownerUid))};
167     mInfo.packageName = getStringField(env, obj, gInputWindowHandleClassInfo.packageName, "<null>");
168     mInfo.displayId =
169             ui::LogicalDisplayId{env->GetIntField(obj, gInputWindowHandleClassInfo.displayId)};
170 
171     jobject inputApplicationHandleObj = env->GetObjectField(obj,
172             gInputWindowHandleClassInfo.inputApplicationHandle);
173     if (inputApplicationHandleObj) {
174         std::shared_ptr<InputApplicationHandle> inputApplicationHandle =
175                 android_view_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);
176         if (inputApplicationHandle != nullptr) {
177             inputApplicationHandle->updateInfo();
178             mInfo.applicationInfo = *(inputApplicationHandle->getInfo());
179         }
180         env->DeleteLocalRef(inputApplicationHandleObj);
181     }
182 
183     mInfo.replaceTouchableRegionWithCrop = env->GetBooleanField(obj,
184             gInputWindowHandleClassInfo.replaceTouchableRegionWithCrop);
185 
186     jobject weakSurfaceCtrl = env->GetObjectField(obj,
187             gInputWindowHandleClassInfo.touchableRegionSurfaceControl.ctrl);
188     bool touchableRegionCropHandleSet = false;
189     if (weakSurfaceCtrl) {
190         // Promote java weak reference.
191         jobject strongSurfaceCtrl = env->CallObjectMethod(weakSurfaceCtrl,
192                 gInputWindowHandleClassInfo.touchableRegionSurfaceControl.get);
193         if (strongSurfaceCtrl) {
194             jlong mNativeObject = env->GetLongField(strongSurfaceCtrl,
195                     gInputWindowHandleClassInfo.touchableRegionSurfaceControl.mNativeObject);
196             if (mNativeObject) {
197                 auto ctrl = reinterpret_cast<SurfaceControl *>(mNativeObject);
198                 mInfo.touchableRegionCropHandle = ctrl->getHandle();
199                 touchableRegionCropHandleSet = true;
200             }
201             env->DeleteLocalRef(strongSurfaceCtrl);
202         }
203         env->DeleteLocalRef(weakSurfaceCtrl);
204     }
205     if (!touchableRegionCropHandleSet) {
206         mInfo.touchableRegionCropHandle.clear();
207     }
208 
209     jobject windowTokenObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.windowToken);
210     if (windowTokenObj) {
211         mInfo.windowToken = ibinderForJavaObject(env, windowTokenObj);
212         env->DeleteLocalRef(windowTokenObj);
213     } else {
214         mInfo.windowToken.clear();
215     }
216 
217     ScopedLocalRef<jobject>
218             focusTransferTargetObj(env,
219                                    env->GetObjectField(obj,
220                                                        gInputWindowHandleClassInfo
221                                                                .focusTransferTarget));
222     if (focusTransferTargetObj.get()) {
223         mInfo.focusTransferTarget = ibinderForJavaObject(env, focusTransferTargetObj.get());
224     } else {
225         mInfo.focusTransferTarget.clear();
226     }
227 
228     env->DeleteLocalRef(obj);
229     return true;
230 }
231 
232 
233 // --- Global functions ---
234 
android_view_InputWindowHandle_getHandle(JNIEnv * env,jobject inputWindowHandleObj)235 sp<NativeInputWindowHandle> android_view_InputWindowHandle_getHandle(
236         JNIEnv* env, jobject inputWindowHandleObj) {
237     if (!inputWindowHandleObj) {
238         return NULL;
239     }
240 
241     AutoMutex _l(gHandleMutex);
242 
243     jlong ptr = env->GetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr);
244     NativeInputWindowHandle* handle;
245     if (ptr) {
246         handle = reinterpret_cast<NativeInputWindowHandle*>(ptr);
247     } else {
248         jweak objWeak = env->NewWeakGlobalRef(inputWindowHandleObj);
249         handle = new NativeInputWindowHandle(objWeak);
250         handle->incStrong((void*)android_view_InputWindowHandle_getHandle);
251         env->SetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr,
252                 reinterpret_cast<jlong>(handle));
253     }
254     return handle;
255 }
256 
android_view_InputWindowHandle_fromWindowInfo(JNIEnv * env,gui::WindowInfo windowInfo)257 jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, gui::WindowInfo windowInfo) {
258     ScopedLocalRef<jobject>
259             applicationHandle(env,
260                               android_view_InputApplicationHandle_fromInputApplicationInfo(
261                                       env, windowInfo.applicationInfo));
262 
263     jobject inputWindowHandle =
264             env->NewObject(gInputWindowHandleClassInfo.clazz, gInputWindowHandleClassInfo.ctor,
265                            applicationHandle.get(), windowInfo.displayId);
266     if (env->ExceptionCheck()) {
267         LOGE_EX(env);
268         env->ExceptionClear();
269     }
270     LOG_ALWAYS_FATAL_IF(inputWindowHandle == nullptr,
271                         "Failed to create new InputWindowHandle object.");
272     env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.token,
273                         javaObjectForIBinder(env, windowInfo.token));
274     ScopedLocalRef<jstring> name(env, env->NewStringUTF(windowInfo.name.data()));
275     env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.name, name.get());
276     env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.layoutParamsFlags,
277                      static_cast<uint32_t>(windowInfo.layoutParamsFlags.get()));
278     env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.layoutParamsType,
279                      static_cast<int32_t>(windowInfo.layoutParamsType));
280     env->SetLongField(inputWindowHandle, gInputWindowHandleClassInfo.dispatchingTimeoutMillis,
281                       std::chrono::duration_cast<std::chrono::milliseconds>(
282                               windowInfo.dispatchingTimeout)
283                               .count());
284     ScopedLocalRef<jobject> rectObj(env, JNICommon::objFromRect(env, windowInfo.frame));
285     env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.frame, rectObj.get());
286 
287     ScopedLocalRef<jobject> sizeObj(env, JNICommon::objFromSize(env, windowInfo.contentSize));
288     env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.contentSize, sizeObj.get());
289 
290     env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.surfaceInset,
291                      windowInfo.surfaceInset);
292     env->SetFloatField(inputWindowHandle, gInputWindowHandleClassInfo.scaleFactor,
293                        windowInfo.globalScaleFactor);
294 
295     SkRegion* region = new SkRegion();
296     for (const auto& r : windowInfo.touchableRegion) {
297         region->op({r.left, r.top, r.right, r.bottom}, SkRegion::kUnion_Op);
298     }
299     ScopedLocalRef<jobject> regionObj(env,
300                                       env->NewObject(gRegionClassInfo.clazz, gRegionClassInfo.ctor,
301                                                      reinterpret_cast<jlong>(region)));
302     env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.touchableRegion,
303                         regionObj.get());
304 
305     env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.touchOcclusionMode,
306                      static_cast<int32_t>(windowInfo.touchOcclusionMode));
307     env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.ownerPid,
308                      windowInfo.ownerPid.val());
309     env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.ownerUid,
310                      windowInfo.ownerUid.val());
311     ScopedLocalRef<jstring> packageName(env, env->NewStringUTF(windowInfo.packageName.data()));
312     env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.packageName,
313                         packageName.get());
314 
315     const auto inputConfig = windowInfo.inputConfig.get();
316     static_assert(sizeof(inputConfig) == sizeof(int32_t));
317     env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.inputConfig,
318                      static_cast<int32_t>(inputConfig));
319 
320     float transformVals[9];
321     for (int i = 0; i < 9; i++) {
322         transformVals[i] = windowInfo.transform[i % 3][i / 3];
323     }
324     ScopedLocalRef<jobject> matrixObj(env, AMatrix_newInstance(env, transformVals));
325     env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.transform, matrixObj.get());
326 
327     env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.windowToken,
328                         javaObjectForIBinder(env, windowInfo.windowToken));
329 
330     env->SetFloatField(inputWindowHandle, gInputWindowHandleClassInfo.alpha, windowInfo.alpha);
331     env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.canOccludePresentation,
332                          windowInfo.canOccludePresentation);
333 
334     return inputWindowHandle;
335 }
336 
337 // --- JNI ---
338 
android_view_InputWindowHandle_nativeDispose(JNIEnv * env,jobject obj)339 static void android_view_InputWindowHandle_nativeDispose(JNIEnv* env, jobject obj) {
340     AutoMutex _l(gHandleMutex);
341 
342     jlong ptr = env->GetLongField(obj, gInputWindowHandleClassInfo.ptr);
343     if (ptr) {
344         env->SetLongField(obj, gInputWindowHandleClassInfo.ptr, 0);
345 
346         NativeInputWindowHandle* handle = reinterpret_cast<NativeInputWindowHandle*>(ptr);
347         handle->decStrong((void*)android_view_InputWindowHandle_getHandle);
348     }
349 }
350 
351 
352 static const JNINativeMethod gInputWindowHandleMethods[] = {
353     /* name, signature, funcPtr */
354     { "nativeDispose", "()V",
355             (void*) android_view_InputWindowHandle_nativeDispose },
356 };
357 
358 #define FIND_CLASS(var, className) \
359         var = env->FindClass(className); \
360         LOG_FATAL_IF(! (var), "Unable to find class " className);
361 
362 #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
363         var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
364         LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
365 
366 #define GET_METHOD_ID(var, clazz, methodName, methodSignature) \
367         var = env->GetMethodID(clazz, methodName, methodSignature); \
368         LOG_FATAL_IF(! (var), "Unable to find method " methodName);
369 
register_android_view_InputWindowHandle(JNIEnv * env)370 int register_android_view_InputWindowHandle(JNIEnv* env) {
371     int res = jniRegisterNativeMethods(env, "android/view/InputWindowHandle",
372             gInputWindowHandleMethods, NELEM(gInputWindowHandleMethods));
373     (void) res;  // Faked use when LOG_NDEBUG.
374     LOG_FATAL_IF(res < 0, "Unable to register native methods.");
375 
376     jclass clazz;
377     FIND_CLASS(clazz, "android/view/InputWindowHandle");
378     gInputWindowHandleClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
379 
380     GET_METHOD_ID(gInputWindowHandleClassInfo.ctor, clazz, "<init>",
381                   "(Landroid/view/InputApplicationHandle;I)V");
382 
383     GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, clazz,
384             "ptr", "J");
385 
386     GET_FIELD_ID(gInputWindowHandleClassInfo.inputApplicationHandle, clazz,
387             "inputApplicationHandle", "Landroid/view/InputApplicationHandle;");
388 
389     GET_FIELD_ID(gInputWindowHandleClassInfo.token, clazz,
390             "token", "Landroid/os/IBinder;");
391 
392     GET_FIELD_ID(gInputWindowHandleClassInfo.name, clazz,
393             "name", "Ljava/lang/String;");
394 
395     GET_FIELD_ID(gInputWindowHandleClassInfo.layoutParamsFlags, clazz,
396             "layoutParamsFlags", "I");
397 
398     GET_FIELD_ID(gInputWindowHandleClassInfo.layoutParamsType, clazz,
399             "layoutParamsType", "I");
400 
401     GET_FIELD_ID(gInputWindowHandleClassInfo.dispatchingTimeoutMillis, clazz,
402                  "dispatchingTimeoutMillis", "J");
403 
404     GET_FIELD_ID(gInputWindowHandleClassInfo.frame, clazz, "frame", "Landroid/graphics/Rect;");
405 
406     GET_FIELD_ID(gInputWindowHandleClassInfo.contentSize, clazz, "contentSize",
407                  "Landroid/util/Size;");
408 
409     GET_FIELD_ID(gInputWindowHandleClassInfo.surfaceInset, clazz,
410             "surfaceInset", "I");
411 
412     GET_FIELD_ID(gInputWindowHandleClassInfo.scaleFactor, clazz,
413             "scaleFactor", "F");
414 
415     GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegion, clazz,
416             "touchableRegion", "Landroid/graphics/Region;");
417 
418     GET_FIELD_ID(gInputWindowHandleClassInfo.touchOcclusionMode, clazz, "touchOcclusionMode", "I");
419 
420     GET_FIELD_ID(gInputWindowHandleClassInfo.ownerPid, clazz,
421             "ownerPid", "I");
422 
423     GET_FIELD_ID(gInputWindowHandleClassInfo.ownerUid, clazz,
424             "ownerUid", "I");
425 
426     GET_FIELD_ID(gInputWindowHandleClassInfo.packageName, clazz, "packageName",
427                  "Ljava/lang/String;");
428 
429     GET_FIELD_ID(gInputWindowHandleClassInfo.inputConfig, clazz, "inputConfig", "I");
430 
431     GET_FIELD_ID(gInputWindowHandleClassInfo.displayId, clazz,
432             "displayId", "I");
433 
434     GET_FIELD_ID(gInputWindowHandleClassInfo.replaceTouchableRegionWithCrop, clazz,
435             "replaceTouchableRegionWithCrop", "Z");
436 
437     GET_FIELD_ID(gInputWindowHandleClassInfo.transform, clazz, "transform",
438                  "Landroid/graphics/Matrix;");
439 
440     GET_FIELD_ID(gInputWindowHandleClassInfo.windowToken, clazz, "windowToken",
441                  "Landroid/os/IBinder;");
442 
443     GET_FIELD_ID(gInputWindowHandleClassInfo.focusTransferTarget, clazz, "focusTransferTarget",
444                  "Landroid/os/IBinder;");
445 
446     jclass weakRefClazz;
447     FIND_CLASS(weakRefClazz, "java/lang/ref/Reference");
448 
449     GET_METHOD_ID(gInputWindowHandleClassInfo.touchableRegionSurfaceControl.get, weakRefClazz,
450              "get", "()Ljava/lang/Object;")
451 
452     GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegionSurfaceControl.ctrl, clazz,
453             "touchableRegionSurfaceControl", "Ljava/lang/ref/WeakReference;");
454 
455     GET_FIELD_ID(gInputWindowHandleClassInfo.alpha, clazz, "alpha", "F");
456 
457     GET_FIELD_ID(gInputWindowHandleClassInfo.canOccludePresentation, clazz,
458                  "canOccludePresentation", "Z");
459 
460     jclass surfaceControlClazz;
461     FIND_CLASS(surfaceControlClazz, "android/view/SurfaceControl");
462     GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegionSurfaceControl.mNativeObject,
463         surfaceControlClazz, "mNativeObject", "J");
464 
465     jclass regionClazz;
466     FIND_CLASS(regionClazz, "android/graphics/Region");
467     gRegionClassInfo.clazz = MakeGlobalRefOrDie(env, regionClazz);
468     GET_METHOD_ID(gRegionClassInfo.ctor, gRegionClassInfo.clazz, "<init>", "(J)V");
469     return 0;
470 }
471 
472 } /* namespace android */
473