• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <nativehelper/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     explicit 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,std::unique_ptr<NativeInputChannel> nativeInputChannel)113 static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
114         std::unique_ptr<NativeInputChannel> nativeInputChannel) {
115     jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
116             gInputChannelClassInfo.ctor);
117     if (inputChannelObj) {
118         android_view_InputChannel_setNativeInputChannel(env, inputChannelObj,
119                  nativeInputChannel.release());
120     }
121     return inputChannelObj;
122 }
123 
android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv * env,jclass clazz,jstring nameObj)124 static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
125         jclass clazz, jstring nameObj) {
126     const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
127     String8 name(nameChars);
128     env->ReleaseStringUTFChars(nameObj, nameChars);
129 
130     sp<InputChannel> serverChannel;
131     sp<InputChannel> clientChannel;
132     status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
133 
134     if (result) {
135         String8 message;
136         message.appendFormat("Could not open input channel pair.  status=%d", result);
137         jniThrowRuntimeException(env, message.string());
138         return NULL;
139     }
140 
141     jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
142     if (env->ExceptionCheck()) {
143         return NULL;
144     }
145 
146     jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
147             std::make_unique<NativeInputChannel>(serverChannel));
148     if (env->ExceptionCheck()) {
149         return NULL;
150     }
151 
152     jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
153             std::make_unique<NativeInputChannel>(clientChannel));
154     if (env->ExceptionCheck()) {
155         return NULL;
156     }
157 
158     env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
159     env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
160     return channelPair;
161 }
162 
android_view_InputChannel_nativeDispose(JNIEnv * env,jobject obj,jboolean finalized)163 static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jboolean finalized) {
164     NativeInputChannel* nativeInputChannel =
165             android_view_InputChannel_getNativeInputChannel(env, obj);
166     if (nativeInputChannel) {
167         if (finalized) {
168             ALOGW("Input channel object '%s' was finalized without being disposed!",
169                     nativeInputChannel->getInputChannel()->getName().string());
170         }
171 
172         nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj);
173 
174         android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
175         delete nativeInputChannel;
176     }
177 }
178 
android_view_InputChannel_nativeTransferTo(JNIEnv * env,jobject obj,jobject otherObj)179 static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
180         jobject otherObj) {
181     if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
182         jniThrowException(env, "java/lang/IllegalStateException",
183                 "Other object already has a native input channel.");
184         return;
185     }
186 
187     NativeInputChannel* nativeInputChannel =
188             android_view_InputChannel_getNativeInputChannel(env, obj);
189     android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
190     android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
191 }
192 
android_view_InputChannel_nativeReadFromParcel(JNIEnv * env,jobject obj,jobject parcelObj)193 static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj,
194         jobject parcelObj) {
195     if (android_view_InputChannel_getNativeInputChannel(env, obj) != NULL) {
196         jniThrowException(env, "java/lang/IllegalStateException",
197                 "This object already has a native input channel.");
198         return;
199     }
200 
201     Parcel* parcel = parcelForJavaObject(env, parcelObj);
202     if (parcel) {
203         bool isInitialized = parcel->readInt32();
204         if (isInitialized) {
205             String8 name = parcel->readString8();
206             int rawFd = parcel->readFileDescriptor();
207             int dupFd = dup(rawFd);
208             if (dupFd < 0) {
209                 ALOGE("Error %d dup channel fd %d.", errno, rawFd);
210                 jniThrowRuntimeException(env,
211                         "Could not read input channel file descriptors from parcel.");
212                 return;
213             }
214 
215             InputChannel* inputChannel = new InputChannel(name, dupFd);
216             NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
217 
218             android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel);
219         }
220     }
221 }
222 
android_view_InputChannel_nativeWriteToParcel(JNIEnv * env,jobject obj,jobject parcelObj)223 static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj,
224         jobject parcelObj) {
225     Parcel* parcel = parcelForJavaObject(env, parcelObj);
226     if (parcel) {
227         NativeInputChannel* nativeInputChannel =
228                 android_view_InputChannel_getNativeInputChannel(env, obj);
229         if (nativeInputChannel) {
230             sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
231 
232             parcel->writeInt32(1);
233             parcel->writeString8(inputChannel->getName());
234             parcel->writeDupFileDescriptor(inputChannel->getFd());
235         } else {
236             parcel->writeInt32(0);
237         }
238     }
239 }
240 
android_view_InputChannel_nativeGetName(JNIEnv * env,jobject obj)241 static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) {
242     NativeInputChannel* nativeInputChannel =
243             android_view_InputChannel_getNativeInputChannel(env, obj);
244     if (! nativeInputChannel) {
245         return NULL;
246     }
247 
248     jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().string());
249     return name;
250 }
251 
android_view_InputChannel_nativeDup(JNIEnv * env,jobject obj,jobject otherObj)252 static void android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jobject otherObj) {
253     NativeInputChannel* nativeInputChannel =
254             android_view_InputChannel_getNativeInputChannel(env, obj);
255     if (nativeInputChannel) {
256         android_view_InputChannel_setNativeInputChannel(env, otherObj,
257                 new NativeInputChannel(nativeInputChannel->getInputChannel()->dup()));
258     }
259 }
260 
261 // ----------------------------------------------------------------------------
262 
263 static const JNINativeMethod gInputChannelMethods[] = {
264     /* name, signature, funcPtr */
265     { "nativeOpenInputChannelPair", "(Ljava/lang/String;)[Landroid/view/InputChannel;",
266             (void*)android_view_InputChannel_nativeOpenInputChannelPair },
267     { "nativeDispose", "(Z)V",
268             (void*)android_view_InputChannel_nativeDispose },
269     { "nativeTransferTo", "(Landroid/view/InputChannel;)V",
270             (void*)android_view_InputChannel_nativeTransferTo },
271     { "nativeReadFromParcel", "(Landroid/os/Parcel;)V",
272             (void*)android_view_InputChannel_nativeReadFromParcel },
273     { "nativeWriteToParcel", "(Landroid/os/Parcel;)V",
274             (void*)android_view_InputChannel_nativeWriteToParcel },
275     { "nativeGetName", "()Ljava/lang/String;",
276             (void*)android_view_InputChannel_nativeGetName },
277     { "nativeDup", "(Landroid/view/InputChannel;)V",
278             (void*)android_view_InputChannel_nativeDup },
279 };
280 
register_android_view_InputChannel(JNIEnv * env)281 int register_android_view_InputChannel(JNIEnv* env) {
282     int res = RegisterMethodsOrDie(env, "android/view/InputChannel", gInputChannelMethods,
283                                    NELEM(gInputChannelMethods));
284 
285     jclass clazz = FindClassOrDie(env, "android/view/InputChannel");
286     gInputChannelClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
287 
288     gInputChannelClassInfo.mPtr = GetFieldIDOrDie(env, gInputChannelClassInfo.clazz, "mPtr", "J");
289 
290     gInputChannelClassInfo.ctor = GetMethodIDOrDie(env, gInputChannelClassInfo.clazz, "<init>",
291                                                    "()V");
292 
293     return res;
294 }
295 
296 } // namespace android
297