/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <semaphore.h> #include <errno.h> #include "com_android_nfc.h" namespace android { extern void nfc_jni_llcp_transport_socket_err_callback(void* pContext, uint8_t nErrCode); /* * Callbacks */ static void nfc_jni_llcp_accept_socket_callback(void* pContext, NFCSTATUS status) { struct nfc_jni_callback_data * pCallbackData = (struct nfc_jni_callback_data *) pContext; LOG_CALLBACK("nfc_jni_llcp_accept_socket_callback", status); /* Report the callback status and wake up the caller */ pCallbackData->status = status; sem_post(&pCallbackData->sem); } /* * Utils */ static phLibNfc_Handle getIncomingSocket(nfc_jni_native_monitor_t * pMonitor, phLibNfc_Handle hServerSocket) { nfc_jni_listen_data_t * pListenData; phLibNfc_Handle pIncomingSocket = NULL; /* Look for a pending incoming connection on the current server */ LIST_FOREACH(pListenData, &pMonitor->incoming_socket_head, entries) { if (pListenData->pServerSocket == hServerSocket) { pIncomingSocket = pListenData->pIncomingSocket; LIST_REMOVE(pListenData, entries); free(pListenData); break; } } return pIncomingSocket; } /* * Methods */ static jobject com_NativeLlcpServiceSocket_doAccept(JNIEnv *e, jobject o, jint miu, jint rw, jint linearBufferLength) { NFCSTATUS ret = NFCSTATUS_SUCCESS; struct timespec ts; phLibNfc_Llcp_sSocketOptions_t sOptions; phNfc_sData_t sWorkingBuffer; jfieldID f; jclass clsNativeLlcpSocket; jobject clientSocket = NULL; struct nfc_jni_callback_data cb_data; phLibNfc_Handle hIncomingSocket, hServerSocket; nfc_jni_native_monitor_t * pMonitor = nfc_jni_get_monitor(); /* Create the local semaphore */ if (!nfc_cb_data_init(&cb_data, NULL)) { goto clean_and_return; } /* Get server socket */ hServerSocket = nfc_jni_get_nfc_socket_handle(e,o); /* Set socket options with the socket options of the service */ sOptions.miu = miu; sOptions.rw = rw; /* Allocate Working buffer length */ sWorkingBuffer.buffer = (uint8_t*)malloc((miu*rw)+miu+linearBufferLength); sWorkingBuffer.length = (miu*rw)+ miu + linearBufferLength; while(cb_data.status != NFCSTATUS_SUCCESS) { /* Wait for tag Notification */ pthread_mutex_lock(&pMonitor->incoming_socket_mutex); while ((hIncomingSocket = getIncomingSocket(pMonitor, hServerSocket)) == NULL) { pthread_cond_wait(&pMonitor->incoming_socket_cond, &pMonitor->incoming_socket_mutex); } pthread_mutex_unlock(&pMonitor->incoming_socket_mutex); /* Accept the incomming socket */ TRACE("phLibNfc_Llcp_Accept()"); REENTRANCE_LOCK(); ret = phLibNfc_Llcp_Accept( hIncomingSocket, &sOptions, &sWorkingBuffer, nfc_jni_llcp_transport_socket_err_callback, nfc_jni_llcp_accept_socket_callback, (void*)&cb_data); REENTRANCE_UNLOCK(); if(ret != NFCSTATUS_PENDING) { // NOTE: This may happen if link went down since incoming socket detected, then // just drop it and start a new accept loop. LOGD("phLibNfc_Llcp_Accept() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret)); continue; } TRACE("phLibNfc_Llcp_Accept() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret)); /* Wait for callback response */ if(sem_wait(&cb_data.sem)) { LOGE("Failed to wait for semaphore (errno=0x%08x)", errno); goto clean_and_return; } if(cb_data.status != NFCSTATUS_SUCCESS) { /* NOTE: Do not generate an error if the accept failed to avoid error in server application */ LOGD("Failed to accept incoming socket 0x%04x[%s]", cb_data.status, nfc_jni_get_status_name(cb_data.status)); } } /* Create new LlcpSocket object */ if(nfc_jni_cache_object(e,"com/android/nfc/nxp/NativeLlcpSocket",&(clientSocket)) == -1) { LOGD("LLCP Socket creation error"); goto clean_and_return; } /* Get NativeConnectionOriented class object */ clsNativeLlcpSocket = e->GetObjectClass(clientSocket); if(e->ExceptionCheck()) { LOGD("LLCP Socket get class object error"); goto clean_and_return; } /* Set socket handle */ f = e->GetFieldID(clsNativeLlcpSocket, "mHandle", "I"); e->SetIntField(clientSocket, f,(jint)hIncomingSocket); /* Set socket MIU */ f = e->GetFieldID(clsNativeLlcpSocket, "mLocalMiu", "I"); e->SetIntField(clientSocket, f,(jint)miu); /* Set socket RW */ f = e->GetFieldID(clsNativeLlcpSocket, "mLocalRw", "I"); e->SetIntField(clientSocket, f,(jint)rw); TRACE("socket handle 0x%02x: MIU = %d, RW = %d\n",hIncomingSocket, miu, rw); clean_and_return: nfc_cb_data_deinit(&cb_data); return clientSocket; } static jboolean com_NativeLlcpServiceSocket_doClose(JNIEnv *e, jobject o) { NFCSTATUS ret; phLibNfc_Handle hLlcpSocket; nfc_jni_native_monitor_t * pMonitor = nfc_jni_get_monitor(); TRACE("Close Service socket"); /* Retrieve socket handle */ hLlcpSocket = nfc_jni_get_nfc_socket_handle(e,o); pthread_mutex_lock(&pMonitor->incoming_socket_mutex); /* TODO: implement accept abort */ pthread_cond_broadcast(&pMonitor->incoming_socket_cond); pthread_mutex_unlock(&pMonitor->incoming_socket_mutex); REENTRANCE_LOCK(); ret = phLibNfc_Llcp_Close(hLlcpSocket); REENTRANCE_UNLOCK(); if(ret == NFCSTATUS_SUCCESS) { TRACE("Close Service socket OK"); return TRUE; } else { LOGD("Close Service socket KO"); return FALSE; } } /* * JNI registration. */ static JNINativeMethod gMethods[] = { {"doAccept", "(III)Lcom/android/nfc/nxp/NativeLlcpSocket;", (void *)com_NativeLlcpServiceSocket_doAccept}, {"doClose", "()Z", (void *)com_NativeLlcpServiceSocket_doClose}, }; int register_com_android_nfc_NativeLlcpServiceSocket(JNIEnv *e) { return jniRegisterNativeMethods(e, "com/android/nfc/nxp/NativeLlcpServiceSocket", gMethods, NELEM(gMethods)); } } // namespace android