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 "DisplayEventReceiver"
18 
19 //#define LOG_NDEBUG 0
20 
21 #include <nativehelper/JNIHelp.h>
22 
23 #include <inttypes.h>
24 
25 #include <android_runtime/AndroidRuntime.h>
26 #include <gui/DisplayEventDispatcher.h>
27 #include <utils/Log.h>
28 #include <utils/Looper.h>
29 #include <utils/threads.h>
30 #include "android_os_MessageQueue.h"
31 
32 #include <nativehelper/ScopedLocalRef.h>
33 
34 #include "core_jni_helpers.h"
35 
36 namespace android {
37 
38 static struct {
39     jclass clazz;
40 
41     jmethodID dispatchVsync;
42     jmethodID dispatchHotplug;
43     jmethodID dispatchConfigChanged;
44 } gDisplayEventReceiverClassInfo;
45 
46 
47 class NativeDisplayEventReceiver : public DisplayEventDispatcher {
48 public:
49     NativeDisplayEventReceiver(JNIEnv* env,
50             jobject receiverWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource,
51             jint configChanged);
52 
53     void dispose();
54 
55 protected:
56     virtual ~NativeDisplayEventReceiver();
57 
58 private:
59     jobject mReceiverWeakGlobal;
60     sp<MessageQueue> mMessageQueue;
61 
62     void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
63     void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
64     void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
65                                int32_t configId, nsecs_t vsyncPeriod) override;
66 };
67 
68 
NativeDisplayEventReceiver(JNIEnv * env,jobject receiverWeak,const sp<MessageQueue> & messageQueue,jint vsyncSource,jint configChanged)69 NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
70         jobject receiverWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource,
71         jint configChanged) :
72         DisplayEventDispatcher(messageQueue->getLooper(),
73                 static_cast<ISurfaceComposer::VsyncSource>(vsyncSource),
74                 static_cast<ISurfaceComposer::ConfigChanged>(configChanged)),
75         mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
76         mMessageQueue(messageQueue) {
77     ALOGV("receiver %p ~ Initializing display event receiver.", this);
78 }
79 
~NativeDisplayEventReceiver()80 NativeDisplayEventReceiver::~NativeDisplayEventReceiver() {
81     JNIEnv* env = AndroidRuntime::getJNIEnv();
82     env->DeleteGlobalRef(mReceiverWeakGlobal);
83     ALOGV("receiver %p ~ dtor display event receiver.", this);
84 }
85 
dispose()86 void NativeDisplayEventReceiver::dispose() {
87     ALOGV("receiver %p ~ Disposing display event receiver.", this);
88     DisplayEventDispatcher::dispose();
89 }
90 
dispatchVsync(nsecs_t timestamp,PhysicalDisplayId displayId,uint32_t count)91 void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,
92                                                uint32_t count) {
93     JNIEnv* env = AndroidRuntime::getJNIEnv();
94 
95     ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
96     if (receiverObj.get()) {
97         ALOGV("receiver %p ~ Invoking vsync handler.", this);
98         env->CallVoidMethod(receiverObj.get(),
99                 gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId, count);
100         ALOGV("receiver %p ~ Returned from vsync handler.", this);
101     }
102 
103     mMessageQueue->raiseAndClearException(env, "dispatchVsync");
104 }
105 
dispatchHotplug(nsecs_t timestamp,PhysicalDisplayId displayId,bool connected)106 void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
107                                                  bool connected) {
108     JNIEnv* env = AndroidRuntime::getJNIEnv();
109 
110     ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
111     if (receiverObj.get()) {
112         ALOGV("receiver %p ~ Invoking hotplug handler.", this);
113         env->CallVoidMethod(receiverObj.get(),
114                 gDisplayEventReceiverClassInfo.dispatchHotplug, timestamp, displayId, connected);
115         ALOGV("receiver %p ~ Returned from hotplug handler.", this);
116     }
117 
118     mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
119 }
120 
dispatchConfigChanged(nsecs_t timestamp,PhysicalDisplayId displayId,int32_t configId,nsecs_t)121 void NativeDisplayEventReceiver::dispatchConfigChanged(
122     nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId, nsecs_t) {
123   JNIEnv* env = AndroidRuntime::getJNIEnv();
124 
125   ScopedLocalRef<jobject> receiverObj(env,
126                                       jniGetReferent(env, mReceiverWeakGlobal));
127   if (receiverObj.get()) {
128     ALOGV("receiver %p ~ Invoking config changed handler.", this);
129     env->CallVoidMethod(receiverObj.get(),
130                         gDisplayEventReceiverClassInfo.dispatchConfigChanged,
131                         timestamp, displayId, configId);
132     ALOGV("receiver %p ~ Returned from config changed handler.", this);
133   }
134 
135   mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged");
136 }
137 
nativeInit(JNIEnv * env,jclass clazz,jobject receiverWeak,jobject messageQueueObj,jint vsyncSource,jint configChanged)138 static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
139         jobject messageQueueObj, jint vsyncSource, jint configChanged) {
140     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
141     if (messageQueue == NULL) {
142         jniThrowRuntimeException(env, "MessageQueue is not initialized.");
143         return 0;
144     }
145 
146     sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
147             receiverWeak, messageQueue, vsyncSource, configChanged);
148     status_t status = receiver->initialize();
149     if (status) {
150         String8 message;
151         message.appendFormat("Failed to initialize display event receiver.  status=%d", status);
152         jniThrowRuntimeException(env, message.string());
153         return 0;
154     }
155 
156     receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object
157     return reinterpret_cast<jlong>(receiver.get());
158 }
159 
nativeDispose(JNIEnv * env,jclass clazz,jlong receiverPtr)160 static void nativeDispose(JNIEnv* env, jclass clazz, jlong receiverPtr) {
161     NativeDisplayEventReceiver* receiver =
162             reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
163     receiver->dispose();
164     receiver->decStrong(gDisplayEventReceiverClassInfo.clazz); // drop reference held by the object
165 }
166 
nativeScheduleVsync(JNIEnv * env,jclass clazz,jlong receiverPtr)167 static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
168     sp<NativeDisplayEventReceiver> receiver =
169             reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
170     status_t status = receiver->scheduleVsync();
171     if (status) {
172         String8 message;
173         message.appendFormat("Failed to schedule next vertical sync pulse.  status=%d", status);
174         jniThrowRuntimeException(env, message.string());
175     }
176 }
177 
178 
179 static const JNINativeMethod gMethods[] = {
180     /* name, signature, funcPtr */
181     { "nativeInit",
182             "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;II)J",
183             (void*)nativeInit },
184     { "nativeDispose",
185             "(J)V",
186             (void*)nativeDispose },
187     // @FastNative
188     { "nativeScheduleVsync", "(J)V",
189             (void*)nativeScheduleVsync }
190 };
191 
register_android_view_DisplayEventReceiver(JNIEnv * env)192 int register_android_view_DisplayEventReceiver(JNIEnv* env) {
193     int res = RegisterMethodsOrDie(env, "android/view/DisplayEventReceiver", gMethods,
194                                    NELEM(gMethods));
195 
196     jclass clazz = FindClassOrDie(env, "android/view/DisplayEventReceiver");
197     gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
198 
199     gDisplayEventReceiverClassInfo.dispatchVsync = GetMethodIDOrDie(env,
200             gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V");
201     gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
202             gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
203     gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env,
204            gDisplayEventReceiverClassInfo.clazz, "dispatchConfigChanged", "(JJI)V");
205 
206     return res;
207 }
208 
209 } // namespace android
210