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     android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
    115     return inputChannelObj;
    116 }
    117 
    118 static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
    119         jclass clazz, jstring nameObj) {
    120     const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
    121     String8 name(nameChars);
    122     env->ReleaseStringUTFChars(nameObj, nameChars);
    123 
    124     sp<InputChannel> serverChannel;
    125     sp<InputChannel> clientChannel;
    126     status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
    127 
    128     if (result) {
    129         LOGE("Could not open input channel pair.  status=%d", result);
    130         jniThrowRuntimeException(env, "Could not open input channel pair.");
    131         return NULL;
    132     }
    133 
    134     // TODO more robust error checking
    135     jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
    136             new NativeInputChannel(serverChannel));
    137     jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
    138             new NativeInputChannel(clientChannel));
    139 
    140     jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
    141     env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    142     env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    143     return channelPair;
    144 }
    145 
    146 static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jboolean finalized) {
    147     NativeInputChannel* nativeInputChannel =
    148             android_view_InputChannel_getNativeInputChannel(env, obj);
    149     if (nativeInputChannel) {
    150         if (finalized) {
    151             LOGW("Input channel object '%s' was finalized without being disposed!",
    152                     nativeInputChannel->getInputChannel()->getName().string());
    153         }
    154 
    155         nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj);
    156 
    157         android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
    158         delete nativeInputChannel;
    159     }
    160 }
    161 
    162 static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
    163         jobject otherObj) {
    164     if (android_view_InputChannel_getInputChannel(env, otherObj) != NULL) {
    165         jniThrowException(env, "java/lang/IllegalStateException",
    166                 "Other object already has a native input channel.");
    167         return;
    168     }
    169 
    170     NativeInputChannel* nativeInputChannel =
    171             android_view_InputChannel_getNativeInputChannel(env, obj);
    172     android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
    173     android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
    174 }
    175 
    176 static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj,
    177         jobject parcelObj) {
    178     if (android_view_InputChannel_getInputChannel(env, obj) != NULL) {
    179         jniThrowException(env, "java/lang/IllegalStateException",
    180                 "This object already has a native input channel.");
    181         return;
    182     }
    183 
    184     Parcel* parcel = parcelForJavaObject(env, parcelObj);
    185     if (parcel) {
    186         bool isInitialized = parcel->readInt32();
    187         if (isInitialized) {
    188             String8 name = parcel->readString8();
    189             int32_t ashmemFd = dup(parcel->readFileDescriptor());
    190             int32_t receivePipeFd = dup(parcel->readFileDescriptor());
    191             int32_t sendPipeFd = dup(parcel->readFileDescriptor());
    192             if (ashmemFd < 0 || receivePipeFd < 0 || sendPipeFd < 0) {
    193                 if (ashmemFd >= 0) ::close(ashmemFd);
    194                 if (receivePipeFd >= 0) ::close(receivePipeFd);
    195                 if (sendPipeFd >= 0) ::close(sendPipeFd);
    196                 jniThrowRuntimeException(env,
    197                         "Could not read input channel file descriptors from parcel.");
    198                 return;
    199             }
    200 
    201             InputChannel* inputChannel = new InputChannel(name, ashmemFd,
    202                     receivePipeFd, sendPipeFd);
    203             NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
    204 
    205             android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel);
    206         }
    207     }
    208 }
    209 
    210 static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj,
    211         jobject parcelObj) {
    212     Parcel* parcel = parcelForJavaObject(env, parcelObj);
    213     if (parcel) {
    214         NativeInputChannel* nativeInputChannel =
    215                 android_view_InputChannel_getNativeInputChannel(env, obj);
    216         if (nativeInputChannel) {
    217             sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
    218 
    219             parcel->writeInt32(1);
    220             parcel->writeString8(inputChannel->getName());
    221             parcel->writeDupFileDescriptor(inputChannel->getAshmemFd());
    222             parcel->writeDupFileDescriptor(inputChannel->getReceivePipeFd());
    223             parcel->writeDupFileDescriptor(inputChannel->getSendPipeFd());
    224         } else {
    225             parcel->writeInt32(0);
    226         }
    227     }
    228 }
    229 
    230 static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) {
    231     NativeInputChannel* nativeInputChannel =
    232             android_view_InputChannel_getNativeInputChannel(env, obj);
    233     if (! nativeInputChannel) {
    234         return NULL;
    235     }
    236 
    237     jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().string());
    238     return name;
    239 }
    240 
    241 // ----------------------------------------------------------------------------
    242 
    243 static JNINativeMethod gInputChannelMethods[] = {
    244     /* name, signature, funcPtr */
    245     { "nativeOpenInputChannelPair", "(Ljava/lang/String;)[Landroid/view/InputChannel;",
    246             (void*)android_view_InputChannel_nativeOpenInputChannelPair },
    247     { "nativeDispose", "(Z)V",
    248             (void*)android_view_InputChannel_nativeDispose },
    249     { "nativeTransferTo", "(Landroid/view/InputChannel;)V",
    250             (void*)android_view_InputChannel_nativeTransferTo },
    251     { "nativeReadFromParcel", "(Landroid/os/Parcel;)V",
    252             (void*)android_view_InputChannel_nativeReadFromParcel },
    253     { "nativeWriteToParcel", "(Landroid/os/Parcel;)V",
    254             (void*)android_view_InputChannel_nativeWriteToParcel },
    255     { "nativeGetName", "()Ljava/lang/String;",
    256             (void*)android_view_InputChannel_nativeGetName },
    257 };
    258 
    259 #define FIND_CLASS(var, className) \
    260         var = env->FindClass(className); \
    261         LOG_FATAL_IF(! var, "Unable to find class " className); \
    262         var = jclass(env->NewGlobalRef(var));
    263 
    264 #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
    265         var = env->GetMethodID(clazz, methodName, methodDescriptor); \
    266         LOG_FATAL_IF(! var, "Unable to find method " methodName);
    267 
    268 #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
    269         var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
    270         LOG_FATAL_IF(! var, "Unable to find field " fieldName);
    271 
    272 int register_android_view_InputChannel(JNIEnv* env) {
    273     int res = jniRegisterNativeMethods(env, "android/view/InputChannel",
    274             gInputChannelMethods, NELEM(gInputChannelMethods));
    275     LOG_FATAL_IF(res < 0, "Unable to register native methods.");
    276 
    277     FIND_CLASS(gInputChannelClassInfo.clazz, "android/view/InputChannel");
    278 
    279     GET_FIELD_ID(gInputChannelClassInfo.mPtr, gInputChannelClassInfo.clazz,
    280             "mPtr", "I");
    281 
    282     GET_METHOD_ID(gInputChannelClassInfo.ctor, gInputChannelClassInfo.clazz,
    283             "<init>", "()V");
    284 
    285     return 0;
    286 }
    287 
    288 } // namespace android
    289