| /* |
| * Copyright (C) 2012 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 <android-base/stringprintf.h> |
| #include <base/logging.h> |
| #include <errno.h> |
| #include <malloc.h> |
| #include <nativehelper/ScopedLocalRef.h> |
| #include <nativehelper/ScopedPrimitiveArray.h> |
| #include <semaphore.h> |
| #include <string.h> |
| #include "JavaClassConstants.h" |
| #include "NfcJniUtil.h" |
| #include "nfa_api.h" |
| #include "nfa_p2p_api.h" |
| |
| using android::base::StringPrintf; |
| |
| extern bool nfc_debug_enabled; |
| |
| namespace android { |
| |
| /***************************************************************************** |
| ** |
| ** private variables and functions |
| ** |
| *****************************************************************************/ |
| static sem_t sConnlessRecvSem; |
| static jboolean sConnlessRecvWaitingForData = JNI_FALSE; |
| static uint8_t* sConnlessRecvBuf = NULL; |
| static uint32_t sConnlessRecvLen = 0; |
| static uint32_t sConnlessRecvRemoteSap = 0; |
| |
| /******************************************************************************* |
| ** |
| ** Function: nativeLlcpConnectionlessSocket_doSendTo |
| ** |
| ** Description: Send data to peer. |
| ** e: JVM environment. |
| ** o: Java object. |
| ** nsap: service access point. |
| ** data: buffer for data. |
| ** |
| ** Returns: True if ok. |
| ** |
| *******************************************************************************/ |
| static jboolean nativeLlcpConnectionlessSocket_doSendTo(JNIEnv* e, jobject o, |
| jint nsap, |
| jbyteArray data) { |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("%s: nsap = %d", __func__, nsap); |
| |
| ScopedLocalRef<jclass> c(e, e->GetObjectClass(o)); |
| jfieldID f = e->GetFieldID(c.get(), "mHandle", "I"); |
| jint handle = e->GetIntField(o, f); |
| |
| ScopedByteArrayRO bytes(e, data); |
| if (bytes.get() == NULL) { |
| return JNI_FALSE; |
| } |
| size_t byte_count = bytes.size(); |
| |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("NFA_P2pSendUI: len = %zu", byte_count); |
| uint8_t* raw_ptr = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>( |
| &bytes[0])); // TODO: API bug; NFA_P2pSendUI should take const*! |
| tNFA_STATUS status = |
| NFA_P2pSendUI((tNFA_HANDLE)handle, nsap, byte_count, raw_ptr); |
| |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("%s: NFA_P2pSendUI done, status = %d", __func__, status); |
| if (status != NFA_STATUS_OK) { |
| LOG(ERROR) << StringPrintf("%s: NFA_P2pSendUI failed, status = %d", |
| __func__, status); |
| return JNI_FALSE; |
| } |
| return JNI_TRUE; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function: nativeLlcpConnectionlessSocket_receiveData |
| ** |
| ** Description: Receive data from the stack. |
| ** data: buffer contains data. |
| ** len: length of data. |
| ** remoteSap: remote service access point. |
| ** |
| ** Returns: None |
| ** |
| *******************************************************************************/ |
| void nativeLlcpConnectionlessSocket_receiveData(uint8_t* data, uint32_t len, |
| uint32_t remoteSap) { |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("%s: waiting for data = %d, len = %d", __func__, |
| sConnlessRecvWaitingForData, len); |
| |
| // Sanity... |
| if (sConnlessRecvLen < len) { |
| len = sConnlessRecvLen; |
| } |
| |
| if (sConnlessRecvWaitingForData) { |
| sConnlessRecvWaitingForData = JNI_FALSE; |
| sConnlessRecvLen = len; |
| memcpy(sConnlessRecvBuf, data, len); |
| sConnlessRecvRemoteSap = remoteSap; |
| |
| sem_post(&sConnlessRecvSem); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function: connectionlessCleanup |
| ** |
| ** Description: Free resources. |
| ** |
| ** Returns: None |
| ** |
| *******************************************************************************/ |
| static jobject connectionlessCleanup() { |
| sConnlessRecvWaitingForData = JNI_FALSE; |
| sConnlessRecvLen = 0; |
| if (sConnlessRecvBuf != NULL) { |
| free(sConnlessRecvBuf); |
| sConnlessRecvBuf = NULL; |
| } |
| return NULL; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function: nativeLlcpConnectionlessSocket_abortWait |
| ** |
| ** Description: Abort current operation and unblock threads. |
| ** |
| ** Returns: None |
| ** |
| *******************************************************************************/ |
| void nativeLlcpConnectionlessSocket_abortWait() { sem_post(&sConnlessRecvSem); } |
| |
| /******************************************************************************* |
| ** |
| ** Function: nativeLlcpConnectionlessSocket_doReceiveFrom |
| ** |
| ** Description: Receive data from a peer. |
| ** e: JVM environment. |
| ** o: Java object. |
| ** linkMiu: max info unit |
| ** |
| ** Returns: LlcpPacket Java object. |
| ** |
| *******************************************************************************/ |
| static jobject nativeLlcpConnectionlessSocket_doReceiveFrom(JNIEnv* e, jobject, |
| jint linkMiu) { |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("%s: linkMiu = %d", __func__, linkMiu); |
| jobject llcpPacket = NULL; |
| ScopedLocalRef<jclass> clsLlcpPacket(e, NULL); |
| |
| if (sConnlessRecvWaitingForData != JNI_FALSE) { |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("%s: Already waiting for incoming data", __func__); |
| return NULL; |
| } |
| |
| sConnlessRecvBuf = (uint8_t*)malloc(linkMiu); |
| if (sConnlessRecvBuf == NULL) { |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "%s: Failed to allocate %d bytes memory buffer", __func__, linkMiu); |
| return NULL; |
| } |
| sConnlessRecvLen = linkMiu; |
| |
| // Create the write semaphore |
| if (sem_init(&sConnlessRecvSem, 0, 0) == -1) { |
| LOG(ERROR) << StringPrintf("%s: semaphore creation failed (errno=0x%08x)", |
| __func__, errno); |
| return connectionlessCleanup(); |
| } |
| |
| sConnlessRecvWaitingForData = JNI_TRUE; |
| |
| // Wait for sConnlessRecvSem completion status |
| if (sem_wait(&sConnlessRecvSem)) { |
| LOG(ERROR) << StringPrintf( |
| "%s: Failed to wait for write semaphore (errno=0x%08x)", __func__, |
| errno); |
| goto TheEnd; |
| } |
| |
| // Create new LlcpPacket object |
| if (nfc_jni_cache_object_local(e, "com/android/nfc/LlcpPacket", |
| &(llcpPacket)) == -1) { |
| LOG(ERROR) << StringPrintf("%s: Find LlcpPacket class error", __func__); |
| return connectionlessCleanup(); |
| } |
| |
| // Get NativeConnectionless class object |
| clsLlcpPacket.reset(e->GetObjectClass(llcpPacket)); |
| if (e->ExceptionCheck()) { |
| e->ExceptionClear(); |
| LOG(ERROR) << StringPrintf("%s: Get Object class error", __func__); |
| return connectionlessCleanup(); |
| } |
| |
| // Set Llcp Packet remote SAP |
| jfieldID f; |
| f = e->GetFieldID(clsLlcpPacket.get(), "mRemoteSap", "I"); |
| e->SetIntField(llcpPacket, f, (jbyte)sConnlessRecvRemoteSap); |
| |
| // Set Llcp Packet Buffer |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("%s: Received Llcp packet buffer size = %d\n", __func__, |
| sConnlessRecvLen); |
| f = e->GetFieldID(clsLlcpPacket.get(), "mDataBuffer", "[B"); |
| |
| { |
| ScopedLocalRef<jbyteArray> receivedData(e, |
| e->NewByteArray(sConnlessRecvLen)); |
| e->SetByteArrayRegion(receivedData.get(), 0, sConnlessRecvLen, |
| (jbyte*)sConnlessRecvBuf); |
| e->SetObjectField(llcpPacket, f, receivedData.get()); |
| } |
| |
| TheEnd: // TODO: should all the "return connectionlessCleanup()"s in this |
| // function jump here instead? |
| connectionlessCleanup(); |
| if (sem_destroy(&sConnlessRecvSem)) { |
| LOG(ERROR) << StringPrintf( |
| "%s: Failed to destroy sConnlessRecvSem semaphore (errno=0x%08x)", |
| __func__, errno); |
| } |
| return llcpPacket; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function: nativeLlcpConnectionlessSocket_doClose |
| ** |
| ** Description: Close socket. |
| ** e: JVM environment. |
| ** o: Java object. |
| ** |
| ** Returns: True if ok. |
| ** |
| *******************************************************************************/ |
| static jboolean nativeLlcpConnectionlessSocket_doClose(JNIEnv* e, jobject o) { |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s", __func__); |
| |
| ScopedLocalRef<jclass> c(e, e->GetObjectClass(o)); |
| jfieldID f = e->GetFieldID(c.get(), "mHandle", "I"); |
| jint handle = e->GetIntField(o, f); |
| |
| tNFA_STATUS status = NFA_P2pDisconnect((tNFA_HANDLE)handle, FALSE); |
| if (status != NFA_STATUS_OK) { |
| LOG(ERROR) << StringPrintf("%s: disconnect failed, status = %d", __func__, |
| status); |
| return JNI_FALSE; |
| } |
| return JNI_TRUE; |
| } |
| |
| /***************************************************************************** |
| ** |
| ** Description: JNI functions |
| ** |
| *****************************************************************************/ |
| static JNINativeMethod gMethods[] = { |
| {"doSendTo", "(I[B)Z", (void*)nativeLlcpConnectionlessSocket_doSendTo}, |
| {"doReceiveFrom", "(I)Lcom/android/nfc/LlcpPacket;", |
| (void*)nativeLlcpConnectionlessSocket_doReceiveFrom}, |
| {"doClose", "()Z", (void*)nativeLlcpConnectionlessSocket_doClose}, |
| }; |
| |
| /******************************************************************************* |
| ** |
| ** Function: register_com_android_nfc_NativeLlcpConnectionlessSocket |
| ** |
| ** Description: Regisgter JNI functions with Java Virtual Machine. |
| ** e: Environment of JVM. |
| ** |
| ** Returns: Status of registration. |
| ** |
| *******************************************************************************/ |
| int register_com_android_nfc_NativeLlcpConnectionlessSocket(JNIEnv* e) { |
| return jniRegisterNativeMethods(e, gNativeLlcpConnectionlessSocketClassName, |
| gMethods, NELEM(gMethods)); |
| } |
| |
| } // namespace android |