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 "android-base/stringprintf.h"
20 #include <nativehelper/JNIHelp.h>
21 #include "nativehelper/scoped_utf_chars.h"
22 #include <android_runtime/AndroidRuntime.h>
23 #include <binder/Parcel.h>
24 #include <utils/Log.h>
25 #include <input/InputTransport.h>
26 #include "android_view_InputChannel.h"
27 #include "android_os_Parcel.h"
28 #include "android_util_Binder.h"
29 
30 #include "core_jni_helpers.h"
31 
32 namespace android {
33 
34 // ----------------------------------------------------------------------------
35 
36 static struct {
37     jclass clazz;
38 
39     jfieldID mPtr;   // native object attached to the DVM InputChannel
40     jmethodID ctor;
41 } gInputChannelClassInfo;
42 
43 // ----------------------------------------------------------------------------
44 
45 class NativeInputChannel {
46 public:
47     explicit NativeInputChannel(const sp<InputChannel>& inputChannel);
48     ~NativeInputChannel();
49 
getInputChannel()50     inline sp<InputChannel> getInputChannel() { return mInputChannel; }
51 
52     void setDisposeCallback(InputChannelObjDisposeCallback callback, void* data);
53     void invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj);
54 
55 private:
56     sp<InputChannel> mInputChannel;
57     InputChannelObjDisposeCallback mDisposeCallback;
58     void* mDisposeData;
59 };
60 
61 // ----------------------------------------------------------------------------
62 
NativeInputChannel(const sp<InputChannel> & inputChannel)63 NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) :
64     mInputChannel(inputChannel), mDisposeCallback(nullptr) {
65 }
66 
~NativeInputChannel()67 NativeInputChannel::~NativeInputChannel() {
68 }
69 
setDisposeCallback(InputChannelObjDisposeCallback callback,void * data)70 void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callback, void* data) {
71     mDisposeCallback = callback;
72     mDisposeData = data;
73 }
74 
invokeAndRemoveDisposeCallback(JNIEnv * env,jobject obj)75 void NativeInputChannel::invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj) {
76     if (mDisposeCallback) {
77         mDisposeCallback(env, obj, mInputChannel, mDisposeData);
78         mDisposeCallback = nullptr;
79         mDisposeData = nullptr;
80     }
81 }
82 
83 // ----------------------------------------------------------------------------
84 
android_view_InputChannel_getNativeInputChannel(JNIEnv * env,jobject inputChannelObj)85 static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
86         jobject inputChannelObj) {
87     jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
88     return reinterpret_cast<NativeInputChannel*>(longPtr);
89 }
90 
android_view_InputChannel_setNativeInputChannel(JNIEnv * env,jobject inputChannelObj,NativeInputChannel * nativeInputChannel)91 static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj,
92         NativeInputChannel* nativeInputChannel) {
93     env->SetLongField(inputChannelObj, gInputChannelClassInfo.mPtr,
94              reinterpret_cast<jlong>(nativeInputChannel));
95 }
96 
android_view_InputChannel_getInputChannel(JNIEnv * env,jobject inputChannelObj)97 sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) {
98     NativeInputChannel* nativeInputChannel =
99             android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
100     return nativeInputChannel != nullptr ? nativeInputChannel->getInputChannel() : nullptr;
101 }
102 
android_view_InputChannel_setDisposeCallback(JNIEnv * env,jobject inputChannelObj,InputChannelObjDisposeCallback callback,void * data)103 void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj,
104         InputChannelObjDisposeCallback callback, void* data) {
105     NativeInputChannel* nativeInputChannel =
106             android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
107     if (nativeInputChannel == nullptr) {
108         ALOGW("Cannot set dispose callback because input channel object has not been initialized.");
109     } else {
110         nativeInputChannel->setDisposeCallback(callback, data);
111     }
112 }
113 
android_view_InputChannel_createInputChannel(JNIEnv * env,sp<InputChannel> inputChannel)114 static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
115         sp<InputChannel> inputChannel) {
116     std::unique_ptr<NativeInputChannel> nativeInputChannel =
117             std::make_unique<NativeInputChannel>(inputChannel);
118     jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
119             gInputChannelClassInfo.ctor);
120     if (inputChannelObj) {
121         android_view_InputChannel_setNativeInputChannel(env, inputChannelObj,
122                  nativeInputChannel.release());
123     }
124     return inputChannelObj;
125 }
126 
android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv * env,jclass clazz,jstring nameObj)127 static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
128         jclass clazz, jstring nameObj) {
129     ScopedUtfChars nameChars(env, nameObj);
130     std::string name = nameChars.c_str();
131 
132     sp<InputChannel> serverChannel;
133     sp<InputChannel> clientChannel;
134     status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
135 
136     if (result) {
137         std::string message = android::base::StringPrintf(
138                 "Could not open input channel pair : %s", strerror(-result));
139         jniThrowRuntimeException(env, message.c_str());
140         return nullptr;
141     }
142 
143     jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, nullptr);
144     if (env->ExceptionCheck()) {
145         return nullptr;
146     }
147 
148     jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, serverChannel);
149     if (env->ExceptionCheck()) {
150         return nullptr;
151     }
152 
153     jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, clientChannel);
154     if (env->ExceptionCheck()) {
155         return nullptr;
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().c_str());
170         }
171 
172         nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj);
173 
174         android_view_InputChannel_setNativeInputChannel(env, obj, nullptr);
175         delete nativeInputChannel;
176     }
177 }
178 
android_view_InputChannel_nativeRelease(JNIEnv * env,jobject obj,jboolean finalized)179 static void android_view_InputChannel_nativeRelease(JNIEnv* env, jobject obj, jboolean finalized) {
180     NativeInputChannel* nativeInputChannel =
181             android_view_InputChannel_getNativeInputChannel(env, obj);
182     if (nativeInputChannel) {
183         android_view_InputChannel_setNativeInputChannel(env, obj, nullptr);
184         delete nativeInputChannel;
185     }
186 }
187 
android_view_InputChannel_nativeTransferTo(JNIEnv * env,jobject obj,jobject otherObj)188 static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
189         jobject otherObj) {
190     if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != nullptr) {
191         jniThrowException(env, "java/lang/IllegalStateException",
192                 "Other object already has a native input channel.");
193         return;
194     }
195 
196     NativeInputChannel* nativeInputChannel =
197             android_view_InputChannel_getNativeInputChannel(env, obj);
198     android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
199     android_view_InputChannel_setNativeInputChannel(env, obj, nullptr);
200 }
201 
android_view_InputChannel_nativeReadFromParcel(JNIEnv * env,jobject obj,jobject parcelObj)202 static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj,
203         jobject parcelObj) {
204     if (android_view_InputChannel_getNativeInputChannel(env, obj) != nullptr) {
205         jniThrowException(env, "java/lang/IllegalStateException",
206                 "This object already has a native input channel.");
207         return;
208     }
209 
210     Parcel* parcel = parcelForJavaObject(env, parcelObj);
211     if (parcel) {
212         bool isInitialized = parcel->readInt32();
213         if (isInitialized) {
214             sp<InputChannel> inputChannel = InputChannel::read(*parcel);
215 
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 == nullptr) {
227         ALOGE("Could not obtain parcel for Java object");
228         return;
229     }
230 
231     NativeInputChannel* nativeInputChannel =
232             android_view_InputChannel_getNativeInputChannel(env, obj);
233     if (!nativeInputChannel) {
234         parcel->writeInt32(0); // not initialized
235         return;
236     }
237     parcel->writeInt32(1); // initialized
238     nativeInputChannel->getInputChannel()->write(*parcel);
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 nullptr;
246     }
247 
248     jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().c_str());
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 == nullptr) {
256         jniThrowRuntimeException(env, "InputChannel has no valid NativeInputChannel");
257         return;
258     }
259 
260     sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
261     if (inputChannel == nullptr) {
262         jniThrowRuntimeException(env, "NativeInputChannel has no corresponding InputChannel");
263         return;
264     }
265     sp<InputChannel> dupInputChannel = inputChannel->dup();
266     if (dupInputChannel == nullptr) {
267         std::string message = android::base::StringPrintf(
268                 "Could not duplicate input channel %s", inputChannel->getName().c_str());
269         jniThrowRuntimeException(env, message.c_str());
270     }
271     android_view_InputChannel_setNativeInputChannel(env, otherObj,
272             new NativeInputChannel(dupInputChannel));
273 }
274 
android_view_InputChannel_nativeGetToken(JNIEnv * env,jobject obj)275 static jobject android_view_InputChannel_nativeGetToken(JNIEnv* env, jobject obj) {
276     NativeInputChannel* nativeInputChannel =
277         android_view_InputChannel_getNativeInputChannel(env, obj);
278     if (nativeInputChannel) {
279         return javaObjectForIBinder(env,
280                 nativeInputChannel->getInputChannel()->getConnectionToken());
281     }
282     return 0;
283 }
284 
285 // ----------------------------------------------------------------------------
286 
287 static const JNINativeMethod gInputChannelMethods[] = {
288     /* name, signature, funcPtr */
289     { "nativeOpenInputChannelPair", "(Ljava/lang/String;)[Landroid/view/InputChannel;",
290             (void*)android_view_InputChannel_nativeOpenInputChannelPair },
291     { "nativeDispose", "(Z)V",
292             (void*)android_view_InputChannel_nativeDispose },
293     { "nativeRelease", "()V",
294             (void*)android_view_InputChannel_nativeRelease },
295     { "nativeTransferTo", "(Landroid/view/InputChannel;)V",
296             (void*)android_view_InputChannel_nativeTransferTo },
297     { "nativeReadFromParcel", "(Landroid/os/Parcel;)V",
298             (void*)android_view_InputChannel_nativeReadFromParcel },
299     { "nativeWriteToParcel", "(Landroid/os/Parcel;)V",
300             (void*)android_view_InputChannel_nativeWriteToParcel },
301     { "nativeGetName", "()Ljava/lang/String;",
302             (void*)android_view_InputChannel_nativeGetName },
303     { "nativeDup", "(Landroid/view/InputChannel;)V",
304             (void*)android_view_InputChannel_nativeDup },
305     { "nativeGetToken", "()Landroid/os/IBinder;",
306             (void*)android_view_InputChannel_nativeGetToken },
307 };
308 
register_android_view_InputChannel(JNIEnv * env)309 int register_android_view_InputChannel(JNIEnv* env) {
310     int res = RegisterMethodsOrDie(env, "android/view/InputChannel", gInputChannelMethods,
311                                    NELEM(gInputChannelMethods));
312 
313     jclass clazz = FindClassOrDie(env, "android/view/InputChannel");
314     gInputChannelClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
315 
316     gInputChannelClassInfo.mPtr = GetFieldIDOrDie(env, gInputChannelClassInfo.clazz, "mPtr", "J");
317 
318     gInputChannelClassInfo.ctor = GetMethodIDOrDie(env, gInputChannelClassInfo.clazz, "<init>",
319                                                    "()V");
320 
321     return res;
322 }
323 
324 } // namespace android
325