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