1 /*
2  * Copyright (C) 2010 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 "InputChannel-JNI"
18 
19 #include "JNIHelp.h"
20 
21 #include <android_runtime/AndroidRuntime.h>
22 #include <binder/Parcel.h>
23 #include <utils/Log.h>
24 #include <input/InputTransport.h>
25 #include "android_view_InputChannel.h"
26 #include "android_os_Parcel.h"
27 #include "android_util_Binder.h"
28 
29 #include "core_jni_helpers.h"
30 
31 namespace android {
32 
33 // ----------------------------------------------------------------------------
34 
35 static struct {
36     jclass clazz;
37 
38     jfieldID mPtr;   // native object attached to the DVM InputChannel
39     jmethodID ctor;
40 } gInputChannelClassInfo;
41 
42 // ----------------------------------------------------------------------------
43 
44 class NativeInputChannel {
45 public:
46     NativeInputChannel(const sp<InputChannel>& inputChannel);
47     ~NativeInputChannel();
48 
getInputChannel()49     inline sp<InputChannel> getInputChannel() { return mInputChannel; }
50 
51     void setDisposeCallback(InputChannelObjDisposeCallback callback, void* data);
52     void invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj);
53 
54 private:
55     sp<InputChannel> mInputChannel;
56     InputChannelObjDisposeCallback mDisposeCallback;
57     void* mDisposeData;
58 };
59 
60 // ----------------------------------------------------------------------------
61 
NativeInputChannel(const sp<InputChannel> & inputChannel)62 NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) :
63     mInputChannel(inputChannel), mDisposeCallback(NULL) {
64 }
65 
~NativeInputChannel()66 NativeInputChannel::~NativeInputChannel() {
67 }
68 
setDisposeCallback(InputChannelObjDisposeCallback callback,void * data)69 void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callback, void* data) {
70     mDisposeCallback = callback;
71     mDisposeData = data;
72 }
73 
invokeAndRemoveDisposeCallback(JNIEnv * env,jobject obj)74 void NativeInputChannel::invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj) {
75     if (mDisposeCallback) {
76         mDisposeCallback(env, obj, mInputChannel, mDisposeData);
77         mDisposeCallback = NULL;
78         mDisposeData = NULL;
79     }
80 }
81 
82 // ----------------------------------------------------------------------------
83 
android_view_InputChannel_getNativeInputChannel(JNIEnv * env,jobject inputChannelObj)84 static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
85         jobject inputChannelObj) {
86     jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
87     return reinterpret_cast<NativeInputChannel*>(longPtr);
88 }
89 
android_view_InputChannel_setNativeInputChannel(JNIEnv * env,jobject inputChannelObj,NativeInputChannel * nativeInputChannel)90 static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj,
91         NativeInputChannel* nativeInputChannel) {
92     env->SetLongField(inputChannelObj, gInputChannelClassInfo.mPtr,
93              reinterpret_cast<jlong>(nativeInputChannel));
94 }
95 
android_view_InputChannel_getInputChannel(JNIEnv * env,jobject inputChannelObj)96 sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) {
97     NativeInputChannel* nativeInputChannel =
98             android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
99     return nativeInputChannel != NULL ? nativeInputChannel->getInputChannel() : NULL;
100 }
101 
android_view_InputChannel_setDisposeCallback(JNIEnv * env,jobject inputChannelObj,InputChannelObjDisposeCallback callback,void * data)102 void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj,
103         InputChannelObjDisposeCallback callback, void* data) {
104     NativeInputChannel* nativeInputChannel =
105             android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
106     if (nativeInputChannel == NULL) {
107         ALOGW("Cannot set dispose callback because input channel object has not been initialized.");
108     } else {
109         nativeInputChannel->setDisposeCallback(callback, data);
110     }
111 }
112 
android_view_InputChannel_createInputChannel(JNIEnv * env,NativeInputChannel * nativeInputChannel)113 static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
114         NativeInputChannel* nativeInputChannel) {
115     jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
116             gInputChannelClassInfo.ctor);
117     if (inputChannelObj) {
118         android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
119     }
120     return inputChannelObj;
121 }
122 
android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv * env,jclass clazz,jstring nameObj)123 static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
124         jclass clazz, jstring nameObj) {
125     const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
126     String8 name(nameChars);
127     env->ReleaseStringUTFChars(nameObj, nameChars);
128 
129     sp<InputChannel> serverChannel;
130     sp<InputChannel> clientChannel;
131     status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
132 
133     if (result) {
134         String8 message;
135         message.appendFormat("Could not open input channel pair.  status=%d", result);
136         jniThrowRuntimeException(env, message.string());
137         return NULL;
138     }
139 
140     jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
141     if (env->ExceptionCheck()) {
142         return NULL;
143     }
144 
145     jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
146             new NativeInputChannel(serverChannel));
147     if (env->ExceptionCheck()) {
148         return NULL;
149     }
150 
151     jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
152             new NativeInputChannel(clientChannel));
153     if (env->ExceptionCheck()) {
154         return NULL;
155     }
156 
157     env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
158     env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
159     return channelPair;
160 }
161 
android_view_InputChannel_nativeDispose(JNIEnv * env,jobject obj,jboolean finalized)162 static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jboolean finalized) {
163     NativeInputChannel* nativeInputChannel =
164             android_view_InputChannel_getNativeInputChannel(env, obj);
165     if (nativeInputChannel) {
166         if (finalized) {
167             ALOGW("Input channel object '%s' was finalized without being disposed!",
168                     nativeInputChannel->getInputChannel()->getName().string());
169         }
170 
171         nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj);
172 
173         android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
174         delete nativeInputChannel;
175     }
176 }
177 
android_view_InputChannel_nativeTransferTo(JNIEnv * env,jobject obj,jobject otherObj)178 static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
179         jobject otherObj) {
180     if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
181         jniThrowException(env, "java/lang/IllegalStateException",
182                 "Other object already has a native input channel.");
183         return;
184     }
185 
186     NativeInputChannel* nativeInputChannel =
187             android_view_InputChannel_getNativeInputChannel(env, obj);
188     android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
189     android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
190 }
191 
android_view_InputChannel_nativeReadFromParcel(JNIEnv * env,jobject obj,jobject parcelObj)192 static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj,
193         jobject parcelObj) {
194     if (android_view_InputChannel_getNativeInputChannel(env, obj) != NULL) {
195         jniThrowException(env, "java/lang/IllegalStateException",
196                 "This object already has a native input channel.");
197         return;
198     }
199 
200     Parcel* parcel = parcelForJavaObject(env, parcelObj);
201     if (parcel) {
202         bool isInitialized = parcel->readInt32();
203         if (isInitialized) {
204             String8 name = parcel->readString8();
205             int rawFd = parcel->readFileDescriptor();
206             int dupFd = dup(rawFd);
207             if (dupFd < 0) {
208                 ALOGE("Error %d dup channel fd %d.", errno, rawFd);
209                 jniThrowRuntimeException(env,
210                         "Could not read input channel file descriptors from parcel.");
211                 return;
212             }
213 
214             InputChannel* inputChannel = new InputChannel(name, dupFd);
215             NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
216 
217             android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel);
218         }
219     }
220 }
221 
android_view_InputChannel_nativeWriteToParcel(JNIEnv * env,jobject obj,jobject parcelObj)222 static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj,
223         jobject parcelObj) {
224     Parcel* parcel = parcelForJavaObject(env, parcelObj);
225     if (parcel) {
226         NativeInputChannel* nativeInputChannel =
227                 android_view_InputChannel_getNativeInputChannel(env, obj);
228         if (nativeInputChannel) {
229             sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
230 
231             parcel->writeInt32(1);
232             parcel->writeString8(inputChannel->getName());
233             parcel->writeDupFileDescriptor(inputChannel->getFd());
234         } else {
235             parcel->writeInt32(0);
236         }
237     }
238 }
239 
android_view_InputChannel_nativeGetName(JNIEnv * env,jobject obj)240 static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) {
241     NativeInputChannel* nativeInputChannel =
242             android_view_InputChannel_getNativeInputChannel(env, obj);
243     if (! nativeInputChannel) {
244         return NULL;
245     }
246 
247     jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().string());
248     return name;
249 }
250 
android_view_InputChannel_nativeDup(JNIEnv * env,jobject obj,jobject otherObj)251 static void android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jobject otherObj) {
252     NativeInputChannel* nativeInputChannel =
253             android_view_InputChannel_getNativeInputChannel(env, obj);
254     if (nativeInputChannel) {
255         android_view_InputChannel_setNativeInputChannel(env, otherObj,
256                 new NativeInputChannel(nativeInputChannel->getInputChannel()->dup()));
257     }
258 }
259 
260 // ----------------------------------------------------------------------------
261 
262 static const JNINativeMethod gInputChannelMethods[] = {
263     /* name, signature, funcPtr */
264     { "nativeOpenInputChannelPair", "(Ljava/lang/String;)[Landroid/view/InputChannel;",
265             (void*)android_view_InputChannel_nativeOpenInputChannelPair },
266     { "nativeDispose", "(Z)V",
267             (void*)android_view_InputChannel_nativeDispose },
268     { "nativeTransferTo", "(Landroid/view/InputChannel;)V",
269             (void*)android_view_InputChannel_nativeTransferTo },
270     { "nativeReadFromParcel", "(Landroid/os/Parcel;)V",
271             (void*)android_view_InputChannel_nativeReadFromParcel },
272     { "nativeWriteToParcel", "(Landroid/os/Parcel;)V",
273             (void*)android_view_InputChannel_nativeWriteToParcel },
274     { "nativeGetName", "()Ljava/lang/String;",
275             (void*)android_view_InputChannel_nativeGetName },
276     { "nativeDup", "(Landroid/view/InputChannel;)V",
277             (void*)android_view_InputChannel_nativeDup },
278 };
279 
register_android_view_InputChannel(JNIEnv * env)280 int register_android_view_InputChannel(JNIEnv* env) {
281     int res = RegisterMethodsOrDie(env, "android/view/InputChannel", gInputChannelMethods,
282                                    NELEM(gInputChannelMethods));
283 
284     jclass clazz = FindClassOrDie(env, "android/view/InputChannel");
285     gInputChannelClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
286 
287     gInputChannelClassInfo.mPtr = GetFieldIDOrDie(env, gInputChannelClassInfo.clazz, "mPtr", "J");
288 
289     gInputChannelClassInfo.ctor = GetMethodIDOrDie(env, gInputChannelClassInfo.clazz, "<init>",
290                                                    "()V");
291 
292     return res;
293 }
294 
295 } // namespace android
296