Home | History | Annotate | Download | only in jni
      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 <ui/InputTransport.h>
     25 #include "android_view_InputChannel.h"
     26 #include "android_util_Binder.h"
     27 
     28 namespace android {
     29 
     30 // ----------------------------------------------------------------------------
     31 
     32 static struct {
     33     jclass clazz;
     34 
     35     jfieldID mPtr;   // native object attached to the DVM InputChannel
     36     jmethodID ctor;
     37 } gInputChannelClassInfo;
     38 
     39 // ----------------------------------------------------------------------------
     40 
     41 class NativeInputChannel {
     42 public:
     43     NativeInputChannel(const sp<InputChannel>& inputChannel);
     44     ~NativeInputChannel();
     45 
     46     inline sp<InputChannel> getInputChannel() { return mInputChannel; }
     47 
     48     void setDisposeCallback(InputChannelObjDisposeCallback callback, void* data);
     49     void invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj);
     50 
     51 private:
     52     sp<InputChannel> mInputChannel;
     53     InputChannelObjDisposeCallback mDisposeCallback;
     54     void* mDisposeData;
     55 };
     56 
     57 // ----------------------------------------------------------------------------
     58 
     59 NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) :
     60     mInputChannel(inputChannel), mDisposeCallback(NULL) {
     61 }
     62 
     63 NativeInputChannel::~NativeInputChannel() {
     64 }
     65 
     66 void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callback, void* data) {
     67     mDisposeCallback = callback;
     68     mDisposeData = data;
     69 }
     70 
     71 void NativeInputChannel::invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj) {
     72     if (mDisposeCallback) {
     73         mDisposeCallback(env, obj, mInputChannel, mDisposeData);
     74         mDisposeCallback = NULL;
     75         mDisposeData = NULL;
     76     }
     77 }
     78 
     79 // ----------------------------------------------------------------------------
     80 
     81 static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
     82         jobject inputChannelObj) {
     83     jint intPtr = env->GetIntField(inputChannelObj, gInputChannelClassInfo.mPtr);
     84     return reinterpret_cast<NativeInputChannel*>(intPtr);
     85 }
     86 
     87 static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj,
     88         NativeInputChannel* nativeInputChannel) {
     89     env->SetIntField(inputChannelObj, gInputChannelClassInfo.mPtr,
     90              reinterpret_cast<jint>(nativeInputChannel));
     91 }
     92 
     93 sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) {
     94     NativeInputChannel* nativeInputChannel =
     95             android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
     96     return nativeInputChannel != NULL ? nativeInputChannel->getInputChannel() : NULL;
     97 }
     98 
     99 void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj,
    100         InputChannelObjDisposeCallback callback, void* data) {
    101     NativeInputChannel* nativeInputChannel =
    102             android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
    103     if (nativeInputChannel == NULL) {
    104         LOGW("Cannot set dispose callback because input channel object has not been initialized.");
    105     } else {
    106         nativeInputChannel->setDisposeCallback(callback, data);
    107     }
    108 }
    109 
    110 static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
    111         NativeInputChannel* nativeInputChannel) {
    112     jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
    113             gInputChannelClassInfo.ctor);
    114     if (inputChannelObj) {
    115         android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
    116     }
    117     return inputChannelObj;
    118 }
    119 
    120 static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
    121         jclass clazz, jstring nameObj) {
    122     const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
    123     String8 name(nameChars);
    124     env->ReleaseStringUTFChars(nameObj, nameChars);
    125 
    126     sp<InputChannel> serverChannel;
    127     sp<InputChannel> clientChannel;
    128     status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
    129 
    130     if (result) {
    131         String8 message;
    132         message.appendFormat("Could not open input channel pair.  status=%d", result);
    133         jniThrowRuntimeException(env, message.string());
    134         return NULL;
    135     }
    136 
    137     jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
    138     if (env->ExceptionCheck()) {
    139         return NULL;
    140     }
    141 
    142     jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
    143             new NativeInputChannel(serverChannel));
    144     if (env->ExceptionCheck()) {
    145         return NULL;
    146     }
    147 
    148     jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
    149             new NativeInputChannel(clientChannel));
    150     if (env->ExceptionCheck()) {
    151         return NULL;
    152     }
    153 
    154     env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    155     env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    156     return channelPair;
    157 }
    158 
    159 static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jboolean finalized) {
    160     NativeInputChannel* nativeInputChannel =
    161             android_view_InputChannel_getNativeInputChannel(env, obj);
    162     if (nativeInputChannel) {
    163         if (finalized) {
    164             LOGW("Input channel object '%s' was finalized without being disposed!",
    165                     nativeInputChannel->getInputChannel()->getName().string());
    166         }
    167 
    168         nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj);
    169 
    170         android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
    171         delete nativeInputChannel;
    172     }
    173 }
    174 
    175 static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
    176         jobject otherObj) {
    177     if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
    178         jniThrowException(env, "java/lang/IllegalStateException",
    179                 "Other object already has a native input channel.");
    180         return;
    181     }
    182 
    183     NativeInputChannel* nativeInputChannel =
    184             android_view_InputChannel_getNativeInputChannel(env, obj);
    185     android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
    186     android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
    187 }
    188 
    189 static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj,
    190         jobject parcelObj) {
    191     if (android_view_InputChannel_getNativeInputChannel(env, obj) != NULL) {
    192         jniThrowException(env, "java/lang/IllegalStateException",
    193                 "This object already has a native input channel.");
    194         return;
    195     }
    196 
    197     Parcel* parcel = parcelForJavaObject(env, parcelObj);
    198     if (parcel) {
    199         bool isInitialized = parcel->readInt32();
    200         if (isInitialized) {
    201             String8 name = parcel->readString8();
    202             int32_t parcelAshmemFd = parcel->readFileDescriptor();
    203             int32_t ashmemFd = dup(parcelAshmemFd);
    204             if (ashmemFd < 0) {
    205                 LOGE("Error %d dup ashmem fd %d.", errno, parcelAshmemFd);
    206             }
    207             int32_t parcelReceivePipeFd = parcel->readFileDescriptor();
    208             int32_t receivePipeFd = dup(parcelReceivePipeFd);
    209             if (receivePipeFd < 0) {
    210                 LOGE("Error %d dup receive pipe fd %d.", errno, parcelReceivePipeFd);
    211             }
    212             int32_t parcelSendPipeFd = parcel->readFileDescriptor();
    213             int32_t sendPipeFd = dup(parcelSendPipeFd);
    214             if (sendPipeFd < 0) {
    215                 LOGE("Error %d dup send pipe fd %d.", errno, parcelSendPipeFd);
    216             }
    217             if (ashmemFd < 0 || receivePipeFd < 0 || sendPipeFd < 0) {
    218                 if (ashmemFd >= 0) ::close(ashmemFd);
    219                 if (receivePipeFd >= 0) ::close(receivePipeFd);
    220                 if (sendPipeFd >= 0) ::close(sendPipeFd);
    221                 jniThrowRuntimeException(env,
    222                         "Could not read input channel file descriptors from parcel.");
    223                 return;
    224             }
    225 
    226             InputChannel* inputChannel = new InputChannel(name, ashmemFd,
    227                     receivePipeFd, sendPipeFd);
    228             NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
    229 
    230             android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel);
    231         }
    232     }
    233 }
    234 
    235 static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj,
    236         jobject parcelObj) {
    237     Parcel* parcel = parcelForJavaObject(env, parcelObj);
    238     if (parcel) {
    239         NativeInputChannel* nativeInputChannel =
    240                 android_view_InputChannel_getNativeInputChannel(env, obj);
    241         if (nativeInputChannel) {
    242             sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
    243 
    244             parcel->writeInt32(1);
    245             parcel->writeString8(inputChannel->getName());
    246             parcel->writeDupFileDescriptor(inputChannel->getAshmemFd());
    247             parcel->writeDupFileDescriptor(inputChannel->getReceivePipeFd());
    248             parcel->writeDupFileDescriptor(inputChannel->getSendPipeFd());
    249         } else {
    250             parcel->writeInt32(0);
    251         }
    252     }
    253 }
    254 
    255 static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) {
    256     NativeInputChannel* nativeInputChannel =
    257             android_view_InputChannel_getNativeInputChannel(env, obj);
    258     if (! nativeInputChannel) {
    259         return NULL;
    260     }
    261 
    262     jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().string());
    263     return name;
    264 }
    265 
    266 // ----------------------------------------------------------------------------
    267 
    268 static JNINativeMethod gInputChannelMethods[] = {
    269     /* name, signature, funcPtr */
    270     { "nativeOpenInputChannelPair", "(Ljava/lang/String;)[Landroid/view/InputChannel;",
    271             (void*)android_view_InputChannel_nativeOpenInputChannelPair },
    272     { "nativeDispose", "(Z)V",
    273             (void*)android_view_InputChannel_nativeDispose },
    274     { "nativeTransferTo", "(Landroid/view/InputChannel;)V",
    275             (void*)android_view_InputChannel_nativeTransferTo },
    276     { "nativeReadFromParcel", "(Landroid/os/Parcel;)V",
    277             (void*)android_view_InputChannel_nativeReadFromParcel },
    278     { "nativeWriteToParcel", "(Landroid/os/Parcel;)V",
    279             (void*)android_view_InputChannel_nativeWriteToParcel },
    280     { "nativeGetName", "()Ljava/lang/String;",
    281             (void*)android_view_InputChannel_nativeGetName },
    282 };
    283 
    284 #define FIND_CLASS(var, className) \
    285         var = env->FindClass(className); \
    286         LOG_FATAL_IF(! var, "Unable to find class " className); \
    287         var = jclass(env->NewGlobalRef(var));
    288 
    289 #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
    290         var = env->GetMethodID(clazz, methodName, methodDescriptor); \
    291         LOG_FATAL_IF(! var, "Unable to find method " methodName);
    292 
    293 #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
    294         var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
    295         LOG_FATAL_IF(! var, "Unable to find field " fieldName);
    296 
    297 int register_android_view_InputChannel(JNIEnv* env) {
    298     int res = jniRegisterNativeMethods(env, "android/view/InputChannel",
    299             gInputChannelMethods, NELEM(gInputChannelMethods));
    300     LOG_FATAL_IF(res < 0, "Unable to register native methods.");
    301 
    302     FIND_CLASS(gInputChannelClassInfo.clazz, "android/view/InputChannel");
    303 
    304     GET_FIELD_ID(gInputChannelClassInfo.mPtr, gInputChannelClassInfo.clazz,
    305             "mPtr", "I");
    306 
    307     GET_METHOD_ID(gInputChannelClassInfo.ctor, gInputChannelClassInfo.clazz,
    308             "<init>", "()V");
    309 
    310     return 0;
    311 }
    312 
    313 } // namespace android
    314