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