1 /*
2  * Copyright 2021 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 "WindowInfosListener"
18 
19 #include <android/graphics/matrix.h>
20 #include <android_runtime/AndroidRuntime.h>
21 #include <android_runtime/Log.h>
22 #include <gui/DisplayInfo.h>
23 #include <gui/SurfaceComposerClient.h>
24 #include <gui/WindowInfosUpdate.h>
25 #include <nativehelper/JNIHelp.h>
26 #include <nativehelper/ScopedLocalFrame.h>
27 #include <utils/Log.h>
28 
29 #include "android_hardware_input_InputWindowHandle.h"
30 #include "core_jni_helpers.h"
31 
32 namespace android {
33 
34 using gui::DisplayInfo;
35 using gui::WindowInfo;
36 
37 namespace {
38 
39 static struct {
40     jclass clazz;
41     jmethodID onWindowInfosChanged;
42 } gListenerClassInfo;
43 
44 static struct {
45     jclass clazz;
46     jmethodID ctor;
47 } gDisplayInfoClassInfo;
48 
49 static struct {
50     jclass clazz;
51     jmethodID ctor;
52 } gPairClassInfo;
53 
54 static jclass gInputWindowHandleClass;
55 
fromDisplayInfo(JNIEnv * env,gui::DisplayInfo displayInfo)56 jobject fromDisplayInfo(JNIEnv* env, gui::DisplayInfo displayInfo) {
57     float transformValues[9];
58     for (int i = 0; i < 9; i++) {
59         transformValues[i] = displayInfo.transform[i % 3][i / 3];
60     }
61     ScopedLocalRef<jobject> matrixObj(env, AMatrix_newInstance(env, transformValues));
62     return env->NewObject(gDisplayInfoClassInfo.clazz, gDisplayInfoClassInfo.ctor,
63                           displayInfo.displayId, displayInfo.logicalWidth,
64                           displayInfo.logicalHeight, matrixObj.get());
65 }
66 
fromWindowInfos(JNIEnv * env,const std::vector<WindowInfo> & windowInfos)67 static jobjectArray fromWindowInfos(JNIEnv* env, const std::vector<WindowInfo>& windowInfos) {
68     jobjectArray jWindowHandlesArray =
69             env->NewObjectArray(windowInfos.size(), gInputWindowHandleClass, nullptr);
70     for (int i = 0; i < windowInfos.size(); i++) {
71         ScopedLocalRef<jobject>
72                 jWindowHandle(env,
73                               android_view_InputWindowHandle_fromWindowInfo(env, windowInfos[i]));
74         env->SetObjectArrayElement(jWindowHandlesArray, i, jWindowHandle.get());
75     }
76 
77     return jWindowHandlesArray;
78 }
79 
fromDisplayInfos(JNIEnv * env,const std::vector<DisplayInfo> & displayInfos)80 static jobjectArray fromDisplayInfos(JNIEnv* env, const std::vector<DisplayInfo>& displayInfos) {
81     jobjectArray jDisplayInfoArray =
82             env->NewObjectArray(displayInfos.size(), gDisplayInfoClassInfo.clazz, nullptr);
83     for (int i = 0; i < displayInfos.size(); i++) {
84         ScopedLocalRef<jobject> jDisplayInfo(env, fromDisplayInfo(env, displayInfos[i]));
85         env->SetObjectArrayElement(jDisplayInfoArray, i, jDisplayInfo.get());
86     }
87 
88     return jDisplayInfoArray;
89 }
90 
91 struct WindowInfosListener : public gui::WindowInfosListener {
WindowInfosListenerandroid::__anonfce0f6940110::WindowInfosListener92     WindowInfosListener(JNIEnv* env, jobject listener)
93           : mListener(env->NewWeakGlobalRef(listener)) {}
94 
onWindowInfosChangedandroid::__anonfce0f6940110::WindowInfosListener95     void onWindowInfosChanged(const gui::WindowInfosUpdate& update) override {
96         JNIEnv* env = AndroidRuntime::getJNIEnv();
97         LOG_ALWAYS_FATAL_IF(env == nullptr, "Unable to retrieve JNIEnv in onWindowInfoChanged.");
98 
99         ScopedLocalFrame localFrame(env);
100         jobject listener = env->NewGlobalRef(mListener);
101         if (listener == nullptr) {
102             // Weak reference went out of scope
103             return;
104         }
105 
106         ScopedLocalRef<jobjectArray> jWindowHandlesArray(env,
107                                                          fromWindowInfos(env, update.windowInfos));
108         ScopedLocalRef<jobjectArray> jDisplayInfoArray(env,
109                                                        fromDisplayInfos(env, update.displayInfos));
110 
111         env->CallVoidMethod(listener, gListenerClassInfo.onWindowInfosChanged,
112                             jWindowHandlesArray.get(), jDisplayInfoArray.get());
113         env->DeleteGlobalRef(listener);
114 
115         if (env->ExceptionCheck()) {
116             ALOGE("WindowInfosListener.onWindowInfosChanged() failed.");
117             LOGE_EX(env);
118             env->ExceptionClear();
119         }
120     }
121 
~WindowInfosListenerandroid::__anonfce0f6940110::WindowInfosListener122     ~WindowInfosListener() override {
123         JNIEnv* env = AndroidRuntime::getJNIEnv();
124         env->DeleteWeakGlobalRef(mListener);
125     }
126 
127 private:
128     jweak mListener;
129 };
130 
nativeCreate(JNIEnv * env,jclass clazz,jobject obj)131 jlong nativeCreate(JNIEnv* env, jclass clazz, jobject obj) {
132     WindowInfosListener* listener = new WindowInfosListener(env, obj);
133     listener->incStrong((void*)nativeCreate);
134     return reinterpret_cast<jlong>(listener);
135 }
136 
destroyNativeService(void * ptr)137 void destroyNativeService(void* ptr) {
138     WindowInfosListener* listener = reinterpret_cast<WindowInfosListener*>(ptr);
139     SurfaceComposerClient::getDefault()->removeWindowInfosListener(listener);
140     listener->decStrong((void*)nativeCreate);
141 }
142 
nativeRegister(JNIEnv * env,jclass clazz,jlong ptr)143 jobject nativeRegister(JNIEnv* env, jclass clazz, jlong ptr) {
144     sp<WindowInfosListener> listener = reinterpret_cast<WindowInfosListener*>(ptr);
145     std::pair<std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>> initialInfo;
146     SurfaceComposerClient::getDefault()->addWindowInfosListener(listener, &initialInfo);
147 
148     ScopedLocalRef<jobjectArray> jWindowHandlesArray(env, fromWindowInfos(env, initialInfo.first));
149     ScopedLocalRef<jobjectArray> jDisplayInfoArray(env, fromDisplayInfos(env, initialInfo.second));
150 
151     return env->NewObject(gPairClassInfo.clazz, gPairClassInfo.ctor, jWindowHandlesArray.get(),
152                           jDisplayInfoArray.get());
153 }
154 
nativeUnregister(JNIEnv * env,jclass clazz,jlong ptr)155 void nativeUnregister(JNIEnv* env, jclass clazz, jlong ptr) {
156     sp<WindowInfosListener> listener = reinterpret_cast<WindowInfosListener*>(ptr);
157     SurfaceComposerClient::getDefault()->removeWindowInfosListener(listener);
158 }
159 
nativeGetFinalizer(JNIEnv *,jclass)160 static jlong nativeGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
161     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeService));
162 }
163 
164 const JNINativeMethod gMethods[] = {
165         /* name, signature, funcPtr */
166         {"nativeCreate", "(Landroid/window/WindowInfosListener;)J", (void*)nativeCreate},
167         {"nativeRegister", "(J)Landroid/util/Pair;", (void*)nativeRegister},
168         {"nativeUnregister", "(J)V", (void*)nativeUnregister},
169         {"nativeGetFinalizer", "()J", (void*)nativeGetFinalizer}};
170 
171 } // namespace
172 
register_android_window_WindowInfosListener(JNIEnv * env)173 int register_android_window_WindowInfosListener(JNIEnv* env) {
174     int res = jniRegisterNativeMethods(env, "android/window/WindowInfosListener", gMethods,
175                                        NELEM(gMethods));
176     LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
177 
178     jclass clazz = env->FindClass("android/window/WindowInfosListener");
179     gListenerClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
180     gListenerClassInfo.onWindowInfosChanged =
181             env->GetMethodID(gListenerClassInfo.clazz, "onWindowInfosChanged",
182                              "([Landroid/view/InputWindowHandle;[Landroid/window/"
183                              "WindowInfosListener$DisplayInfo;)V");
184 
185     clazz = env->FindClass("android/view/InputWindowHandle");
186     gInputWindowHandleClass = MakeGlobalRefOrDie(env, clazz);
187 
188     clazz = env->FindClass("android/window/WindowInfosListener$DisplayInfo");
189     gDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
190     gDisplayInfoClassInfo.ctor = env->GetMethodID(gDisplayInfoClassInfo.clazz, "<init>",
191                                                   "(IIILandroid/graphics/Matrix;)V");
192 
193     clazz = env->FindClass("android/util/Pair");
194     gPairClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
195     gPairClassInfo.ctor = env->GetMethodID(gPairClassInfo.clazz, "<init>",
196                                            "(Ljava/lang/Object;Ljava/lang/Object;)V");
197 
198     return 0;
199 }
200 
201 } // namespace android
202