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     jmethodID mCtor;
40     jmethodID mSetNativeInputChannel;
41 
42     jfieldID mPtr;   // native object attached to the DVM InputChannel
43 } gInputChannelClassInfo;
44 
45 // ----------------------------------------------------------------------------
46 
47 class NativeInputChannel {
48 public:
49     explicit NativeInputChannel(std::unique_ptr<InputChannel> inputChannel);
50     ~NativeInputChannel();
51 
getInputChannel()52     inline std::shared_ptr<InputChannel> getInputChannel() { return mInputChannel; }
53 
54     void setDisposeCallback(InputChannelObjDisposeCallback callback, void* data);
55     void dispose(JNIEnv* env, jobject obj);
56 
57 private:
58     std::shared_ptr<InputChannel> mInputChannel;
59     InputChannelObjDisposeCallback mDisposeCallback;
60     void* mDisposeData;
61 };
62 
63 // ----------------------------------------------------------------------------
64 
NativeInputChannel(std::unique_ptr<InputChannel> inputChannel)65 NativeInputChannel::NativeInputChannel(std::unique_ptr<InputChannel> inputChannel)
66       : mInputChannel(std::move(inputChannel)), mDisposeCallback(nullptr) {}
67 
~NativeInputChannel()68 NativeInputChannel::~NativeInputChannel() {
69 }
70 
setDisposeCallback(InputChannelObjDisposeCallback callback,void * data)71 void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callback, void* data) {
72     mDisposeCallback = callback;
73     mDisposeData = data;
74 }
75 
dispose(JNIEnv * env,jobject obj)76 void NativeInputChannel::dispose(JNIEnv* env, jobject obj) {
77     if (!mInputChannel) {
78         return;
79     }
80 
81     if (mDisposeCallback) {
82         mDisposeCallback(env, obj, mInputChannel, mDisposeData);
83         mDisposeCallback = nullptr;
84         mDisposeData = nullptr;
85     }
86     mInputChannel.reset();
87 }
88 
89 // ----------------------------------------------------------------------------
90 
android_view_InputChannel_getNativeInputChannel(JNIEnv * env,jobject inputChannelObj)91 static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
92         jobject inputChannelObj) {
93     jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
94     return reinterpret_cast<NativeInputChannel*>(longPtr);
95 }
96 
android_view_InputChannel_getInputChannel(JNIEnv * env,jobject inputChannelObj)97 std::shared_ptr<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env,
98                                                                         jobject inputChannelObj) {
99     NativeInputChannel* nativeInputChannel =
100             android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
101     return nativeInputChannel != nullptr ? nativeInputChannel->getInputChannel() : nullptr;
102 }
103 
android_view_InputChannel_setDisposeCallback(JNIEnv * env,jobject inputChannelObj,InputChannelObjDisposeCallback callback,void * data)104 void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj,
105         InputChannelObjDisposeCallback callback, void* data) {
106     NativeInputChannel* nativeInputChannel =
107             android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
108     if (!nativeInputChannel || !nativeInputChannel->getInputChannel()) {
109         ALOGW("Cannot set dispose callback because input channel object has not been initialized.");
110     } else {
111         nativeInputChannel->setDisposeCallback(callback, data);
112     }
113 }
114 
android_view_InputChannel_createInputChannel(JNIEnv * env,std::unique_ptr<InputChannel> inputChannel)115 static jlong android_view_InputChannel_createInputChannel(
116         JNIEnv* env, std::unique_ptr<InputChannel> inputChannel) {
117     std::unique_ptr<NativeInputChannel> nativeInputChannel =
118             std::make_unique<NativeInputChannel>(std::move(inputChannel));
119 
120     return reinterpret_cast<jlong>(nativeInputChannel.release());
121 }
122 
android_view_InputChannel_createJavaObject(JNIEnv * env,std::unique_ptr<InputChannel> inputChannel)123 jobject android_view_InputChannel_createJavaObject(JNIEnv* env,
124                                                    std::unique_ptr<InputChannel> inputChannel) {
125     std::string name = inputChannel->getName();
126     jlong ptr = android_view_InputChannel_createInputChannel(env, std::move(inputChannel));
127     jobject javaInputChannel =
128             env->NewObject(gInputChannelClassInfo.clazz, gInputChannelClassInfo.mCtor);
129     if (!javaInputChannel) {
130         ALOGE("Failed to create a Java InputChannel for channel %s.", name.c_str());
131         return nullptr;
132     }
133 
134     env->CallVoidMethod(javaInputChannel, gInputChannelClassInfo.mSetNativeInputChannel, ptr);
135     if (env->ExceptionOccurred()) {
136         ALOGE("Failed to set native ptr to the Java InputChannel for channel %s.",
137               inputChannel->getName().c_str());
138         return nullptr;
139     }
140     return javaInputChannel;
141 }
142 
android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv * env,jclass clazz,jstring nameObj)143 static jlongArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
144         jclass clazz, jstring nameObj) {
145     ScopedUtfChars nameChars(env, nameObj);
146     std::string name = nameChars.c_str();
147 
148     std::unique_ptr<InputChannel> serverChannel;
149     std::unique_ptr<InputChannel> clientChannel;
150     status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
151 
152     if (result) {
153         std::string message = android::base::StringPrintf(
154                 "Could not open input channel pair : %s", strerror(-result));
155         jniThrowRuntimeException(env, message.c_str());
156         return nullptr;
157     }
158 
159     jlongArray channelPair = env->NewLongArray(2);
160     if (channelPair == nullptr) {
161         return nullptr;
162     }
163 
164     jlong* outArray = env->GetLongArrayElements(channelPair, 0);
165     outArray[0] = android_view_InputChannel_createInputChannel(env, std::move(serverChannel));
166     if (env->ExceptionCheck()) {
167         return nullptr;
168     }
169 
170     outArray[1] = android_view_InputChannel_createInputChannel(env, std::move(clientChannel));
171     if (env->ExceptionCheck()) {
172         return nullptr;
173     }
174     env->ReleaseLongArrayElements(channelPair, outArray, 0);
175 
176     return channelPair;
177 }
178 
InputChannel_nativeDestroy(void * rawInputChannel)179 static void InputChannel_nativeDestroy(void *rawInputChannel) {
180     NativeInputChannel* nativeInputChannel =
181             reinterpret_cast<NativeInputChannel *>(rawInputChannel);
182     if (nativeInputChannel) {
183         delete nativeInputChannel;
184     }
185 }
186 
android_view_InputChannel_getNativeFinalizer(JNIEnv * env,jobject obj)187 static jlong android_view_InputChannel_getNativeFinalizer(JNIEnv* env, jobject obj) {
188     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&InputChannel_nativeDestroy));
189 }
190 
android_view_InputChannel_nativeDispose(JNIEnv * env,jobject obj,jlong channel)191 static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jlong channel) {
192     NativeInputChannel* nativeInputChannel =
193             reinterpret_cast<NativeInputChannel*>(channel);
194 
195     if (nativeInputChannel) {
196         nativeInputChannel->dispose(env, obj);
197     }
198 }
199 
android_view_InputChannel_nativeReadFromParcel(JNIEnv * env,jobject obj,jobject parcelObj)200 static jlong android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj,
201         jobject parcelObj) {
202     Parcel* parcel = parcelForJavaObject(env, parcelObj);
203     if (parcel) {
204         bool isInitialized = parcel->readInt32();
205         if (isInitialized) {
206             android::os::InputChannelCore parcelableChannel;
207             parcelableChannel.readFromParcel(parcel);
208             std::unique_ptr<InputChannel> inputChannel =
209                     InputChannel::create(std::move(parcelableChannel));
210             NativeInputChannel* nativeInputChannel =
211                     new NativeInputChannel(std::move(inputChannel));
212             return reinterpret_cast<jlong>(nativeInputChannel);
213         }
214     }
215     return 0;
216 }
217 
android_view_InputChannel_nativeWriteToParcel(JNIEnv * env,jobject obj,jobject parcelObj,jlong channel)218 static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj,
219         jobject parcelObj, jlong channel) {
220     Parcel* parcel = parcelForJavaObject(env, parcelObj);
221     if (parcel == nullptr) {
222         ALOGE("Could not obtain parcel for Java object");
223         return;
224     }
225     NativeInputChannel* nativeInputChannel =
226                 reinterpret_cast<NativeInputChannel*>(channel);
227 
228     if (!nativeInputChannel || !nativeInputChannel->getInputChannel()) {
229         parcel->writeInt32(0); // not initialized
230         return;
231     }
232     parcel->writeInt32(1); // initialized
233     android::os::InputChannelCore parcelableChannel;
234     nativeInputChannel->getInputChannel()->copyTo(parcelableChannel);
235     parcelableChannel.writeToParcel(parcel);
236 }
237 
android_view_InputChannel_nativeGetName(JNIEnv * env,jobject obj,jlong channel)238 static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj, jlong channel) {
239     NativeInputChannel* nativeInputChannel =
240                 reinterpret_cast<NativeInputChannel*>(channel);
241     if (!nativeInputChannel || !nativeInputChannel->getInputChannel()) {
242         return nullptr;
243     }
244 
245     jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().c_str());
246     return name;
247 }
248 
android_view_InputChannel_nativeDup(JNIEnv * env,jobject obj,jlong channel)249 static jlong android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jlong channel) {
250     NativeInputChannel* nativeInputChannel =
251                 reinterpret_cast<NativeInputChannel*>(channel);
252 
253     if (nativeInputChannel == nullptr) {
254         jniThrowRuntimeException(env, "InputChannel has no valid NativeInputChannel");
255         return 0;
256     }
257 
258     std::shared_ptr<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
259     if (inputChannel == nullptr) {
260         jniThrowRuntimeException(env, "NativeInputChannel has no corresponding InputChannel");
261         return 0;
262     }
263 
264     std::unique_ptr<InputChannel> dupInputChannel = inputChannel->dup();
265     if (dupInputChannel == nullptr) {
266         std::string message = android::base::StringPrintf(
267                 "Could not duplicate input channel %s", inputChannel->getName().c_str());
268         jniThrowRuntimeException(env, message.c_str());
269     }
270     return reinterpret_cast<jlong>(new NativeInputChannel(std::move(dupInputChannel)));
271 }
272 
android_view_InputChannel_nativeGetToken(JNIEnv * env,jobject obj,jlong channel)273 static jobject android_view_InputChannel_nativeGetToken(JNIEnv* env, jobject obj, jlong channel) {
274     NativeInputChannel* nativeInputChannel =
275                 reinterpret_cast<NativeInputChannel*>(channel);
276     if (nativeInputChannel && nativeInputChannel->getInputChannel()) {
277         return javaObjectForIBinder(env,
278                 nativeInputChannel->getInputChannel()->getConnectionToken());
279     }
280     return 0;
281 }
282 
283 // ----------------------------------------------------------------------------
284 
285 static const JNINativeMethod gInputChannelMethods[] = {
286     /* name, signature, funcPtr */
287     { "nativeOpenInputChannelPair", "(Ljava/lang/String;)[J",
288             (void*)android_view_InputChannel_nativeOpenInputChannelPair },
289     { "nativeGetFinalizer", "()J",
290             (void*)android_view_InputChannel_getNativeFinalizer },
291     { "nativeDispose", "(J)V",
292             (void*)android_view_InputChannel_nativeDispose },
293     { "nativeReadFromParcel", "(Landroid/os/Parcel;)J",
294             (void*)android_view_InputChannel_nativeReadFromParcel },
295     { "nativeWriteToParcel", "(Landroid/os/Parcel;J)V",
296             (void*)android_view_InputChannel_nativeWriteToParcel },
297     { "nativeGetName", "(J)Ljava/lang/String;",
298             (void*)android_view_InputChannel_nativeGetName },
299     { "nativeDup", "(J)J",
300             (void*)android_view_InputChannel_nativeDup },
301     { "nativeGetToken", "(J)Landroid/os/IBinder;",
302             (void*)android_view_InputChannel_nativeGetToken },
303 };
304 
register_android_view_InputChannel(JNIEnv * env)305 int register_android_view_InputChannel(JNIEnv* env) {
306     int res = RegisterMethodsOrDie(env, "android/view/InputChannel", gInputChannelMethods,
307                                    NELEM(gInputChannelMethods));
308 
309     jclass clazz = FindClassOrDie(env, "android/view/InputChannel");
310     gInputChannelClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
311 
312     gInputChannelClassInfo.mCtor =
313             GetMethodIDOrDie(env, gInputChannelClassInfo.clazz, "<init>", "()V");
314     gInputChannelClassInfo.mSetNativeInputChannel =
315             GetMethodIDOrDie(env, gInputChannelClassInfo.clazz, "setNativeInputChannel", "(J)V");
316 
317     gInputChannelClassInfo.mPtr = GetFieldIDOrDie(env, gInputChannelClassInfo.clazz, "mPtr", "J");
318 
319     return res;
320 }
321 
322 } // namespace android
323