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