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