/*
* 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"
extern uint8_t device_connected_flag;
namespace android {
extern void nfc_jni_restart_discovery_locked(struct nfc_jni_native_data *nat);
/*
* Callbacks
*/
static void nfc_jni_presence_check_callback(void* pContext, NFCSTATUS status)
{
struct nfc_jni_callback_data * pCallbackData = (struct nfc_jni_callback_data *) pContext;
LOG_CALLBACK("nfc_jni_presence_check_callback", status);
/* Report the callback status and wake up the caller */
pCallbackData->status = status;
sem_post(&pCallbackData->sem);
}
static void nfc_jni_connect_callback(void *pContext,
phLibNfc_Handle hRemoteDev,
phLibNfc_sRemoteDevInformation_t *psRemoteDevInfo, NFCSTATUS status)
{
struct nfc_jni_callback_data * pCallbackData = (struct nfc_jni_callback_data *) pContext;
phNfc_sData_t * psGeneralBytes = (phNfc_sData_t *)pCallbackData->pContext;
LOG_CALLBACK("nfc_jni_connect_callback", status);
if(status == NFCSTATUS_SUCCESS)
{
psGeneralBytes->length = psRemoteDevInfo->RemoteDevInfo.NfcIP_Info.ATRInfo_Length;
psGeneralBytes->buffer = (uint8_t*)malloc(psRemoteDevInfo->RemoteDevInfo.NfcIP_Info.ATRInfo_Length);
psGeneralBytes->buffer = psRemoteDevInfo->RemoteDevInfo.NfcIP_Info.ATRInfo;
}
/* Report the callback status and wake up the caller */
pCallbackData->status = status;
sem_post(&pCallbackData->sem);
}
static void nfc_jni_disconnect_callback(void *pContext, phLibNfc_Handle hRemoteDev, NFCSTATUS status)
{
struct nfc_jni_callback_data * pCallbackData = (struct nfc_jni_callback_data *) pContext;
LOG_CALLBACK("nfc_jni_disconnect_callback", status);
/* Report the callback status and wake up the caller */
pCallbackData->status = status;
sem_post(&pCallbackData->sem);
}
static void nfc_jni_receive_callback(void *pContext, phNfc_sData_t *data, NFCSTATUS status)
{
struct nfc_jni_callback_data * pCallbackData = (struct nfc_jni_callback_data *) pContext;
phNfc_sData_t **ptr = (phNfc_sData_t **)pCallbackData->pContext;
LOG_CALLBACK("nfc_jni_receive_callback", status);
if(status == NFCSTATUS_SUCCESS)
{
*ptr = data;
}
else
{
*ptr = NULL;
}
/* Report the callback status and wake up the caller */
pCallbackData->status = status;
sem_post(&pCallbackData->sem);
}
static void nfc_jni_send_callback(void *pContext, NFCSTATUS status)
{
struct nfc_jni_callback_data * pCallbackData = (struct nfc_jni_callback_data *) pContext;
LOG_CALLBACK("nfc_jni_send_callback", status);
/* Report the callback status and wake up the caller */
pCallbackData->status = status;
sem_post(&pCallbackData->sem);
}
/*
* Functions
*/
static void nfc_jni_transceive_callback(void *pContext,
phLibNfc_Handle handle, phNfc_sData_t *pResBuffer, NFCSTATUS status)
{
struct nfc_jni_callback_data * pCallbackData = (struct nfc_jni_callback_data *) pContext;
LOG_CALLBACK("nfc_jni_transceive_callback", status);
/* Report the callback data and wake up the caller */
pCallbackData->pContext = pResBuffer;
pCallbackData->status = status;
sem_post(&pCallbackData->sem);
}
static jboolean com_android_nfc_NativeP2pDevice_doConnect(JNIEnv *e, jobject o)
{
phLibNfc_Handle handle = 0;
NFCSTATUS status;
jboolean result = JNI_FALSE;
struct nfc_jni_callback_data cb_data;
jclass target_cls = NULL;
jobject tag;
jmethodID ctor;
jfieldID f;
jbyteArray generalBytes = NULL;
phNfc_sData_t sGeneralBytes;
unsigned int i;
CONCURRENCY_LOCK();
handle = nfc_jni_get_p2p_device_handle(e, o);
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, (void*)&sGeneralBytes))
{
goto clean_and_return;
}
TRACE("phLibNfc_RemoteDev_Connect(P2P)");
REENTRANCE_LOCK();
status = phLibNfc_RemoteDev_Connect(handle, nfc_jni_connect_callback, (void*)&cb_data);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_PENDING)
{
LOGE("phLibNfc_RemoteDev_Connect(P2P) returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
goto clean_and_return;
}
TRACE("phLibNfc_RemoteDev_Connect(P2P) returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
/* 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)
{
goto clean_and_return;
}
/* Set General Bytes */
target_cls = e->GetObjectClass(o);
f = e->GetFieldID(target_cls, "mGeneralBytes", "[B");
TRACE("General Bytes Length = %d", sGeneralBytes.length);
TRACE("General Bytes =");
for(i=0;i<sGeneralBytes.length;i++)
{
TRACE("0x%02x ", sGeneralBytes.buffer[i]);
}
generalBytes = e->NewByteArray(sGeneralBytes.length);
e->SetByteArrayRegion(generalBytes, 0,
sGeneralBytes.length,
(jbyte *)sGeneralBytes.buffer);
e->SetObjectField(o, f, generalBytes);
result = JNI_TRUE;
clean_and_return:
if (result != JNI_TRUE)
{
/* Restart the polling loop if the connection failed */
nfc_jni_restart_discovery_locked(nfc_jni_get_nat_ext(e));
}
nfc_cb_data_deinit(&cb_data);
CONCURRENCY_UNLOCK();
return result;
}
static jboolean com_android_nfc_NativeP2pDevice_doDisconnect(JNIEnv *e, jobject o)
{
phLibNfc_Handle handle = 0;
jboolean result = JNI_FALSE;
NFCSTATUS status;
struct nfc_jni_callback_data cb_data;
CONCURRENCY_LOCK();
handle = nfc_jni_get_p2p_device_handle(e, o);
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, NULL))
{
goto clean_and_return;
}
/* Disconnect */
TRACE("Disconnecting from target (handle = 0x%x)", handle);
/* NativeNfcTag waits for tag to leave the field here with presence check.
* We do not in P2P path because presence check is not safe while transceive may be
* in progress.
*/
TRACE("phLibNfc_RemoteDev_Disconnect()");
REENTRANCE_LOCK();
status = phLibNfc_RemoteDev_Disconnect(handle, NFC_DISCOVERY_CONTINUE,nfc_jni_disconnect_callback, (void *)&cb_data);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_PENDING)
{
LOGE("phLibNfc_RemoteDev_Disconnect() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
if(status == NFCSTATUS_TARGET_NOT_CONNECTED)
{
LOGE("phLibNfc_RemoteDev_Disconnect() failed: Target not connected");
}
else
{
LOGE("phLibNfc_RemoteDev_Disconnect() failed");
nfc_jni_restart_discovery_locked(nfc_jni_get_nat_ext(e));
}
goto clean_and_return;
}
TRACE("phLibNfc_RemoteDev_Disconnect() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
/* Wait for callback response */
if(sem_wait(&cb_data.sem))
{
LOGE("Failed to wait for semaphore (errno=0x%08x)", errno);
goto clean_and_return;
}
/* Disconnect Status */
if(cb_data.status != NFCSTATUS_SUCCESS)
{
goto clean_and_return;
}
result = JNI_TRUE;
clean_and_return:
/* Reset device connected flag */
device_connected_flag = 0;
nfc_cb_data_deinit(&cb_data);
CONCURRENCY_UNLOCK();
return result;
}
static jbyteArray com_android_nfc_NativeP2pDevice_doTransceive(JNIEnv *e,
jobject o, jbyteArray data)
{
NFCSTATUS status;
uint8_t offset = 2;
uint8_t *buf;
uint32_t buflen;
phLibNfc_sTransceiveInfo_t transceive_info;
jbyteArray result = NULL;
phLibNfc_Handle handle = nfc_jni_get_p2p_device_handle(e, o);
phNfc_sData_t * receive_buffer = NULL;
struct nfc_jni_callback_data cb_data;
CONCURRENCY_LOCK();
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, (void*)receive_buffer))
{
goto clean_and_return;
}
/* Transceive*/
TRACE("Transceive data to target (handle = 0x%x)", handle);
buf = (uint8_t *)e->GetByteArrayElements(data, NULL);
buflen = (uint32_t)e->GetArrayLength(data);
TRACE("Buffer Length = %d\n", buflen);
transceive_info.sSendData.buffer = buf; //+ offset;
transceive_info.sSendData.length = buflen; //- offset;
transceive_info.sRecvData.buffer = (uint8_t*)malloc(1024);
transceive_info.sRecvData.length = 1024;
if(transceive_info.sRecvData.buffer == NULL)
{
goto clean_and_return;
}
TRACE("phLibNfc_RemoteDev_Transceive(P2P)");
REENTRANCE_LOCK();
status = phLibNfc_RemoteDev_Transceive(handle, &transceive_info, nfc_jni_transceive_callback, (void *)&cb_data);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_PENDING)
{
LOGE("phLibNfc_RemoteDev_Transceive(P2P) returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
goto clean_and_return;
}
TRACE("phLibNfc_RemoteDev_Transceive(P2P) returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
/* 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)
{
goto clean_and_return;
}
/* Copy results back to Java */
result = e->NewByteArray(receive_buffer->length);
if(result != NULL)
e->SetByteArrayRegion(result, 0,
receive_buffer->length,
(jbyte *)receive_buffer->buffer);
clean_and_return:
if(transceive_info.sRecvData.buffer != NULL)
{
free(transceive_info.sRecvData.buffer);
}
e->ReleaseByteArrayElements(data,
(jbyte *)transceive_info.sSendData.buffer, JNI_ABORT);
nfc_cb_data_deinit(&cb_data);
CONCURRENCY_UNLOCK();
return result;
}
static jbyteArray com_android_nfc_NativeP2pDevice_doReceive(
JNIEnv *e, jobject o)
{
NFCSTATUS status;
struct timespec ts;
phLibNfc_Handle handle;
jbyteArray buf = NULL;
static phNfc_sData_t *data;
struct nfc_jni_callback_data cb_data;
CONCURRENCY_LOCK();
handle = nfc_jni_get_p2p_device_handle(e, o);
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, (void*)data))
{
goto clean_and_return;
}
/* Receive */
TRACE("phLibNfc_RemoteDev_Receive()");
REENTRANCE_LOCK();
status = phLibNfc_RemoteDev_Receive(handle, nfc_jni_receive_callback,(void *)&cb_data);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_PENDING)
{
LOGE("phLibNfc_RemoteDev_Receive() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
goto clean_and_return;
}
TRACE("phLibNfc_RemoteDev_Receive() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
/* 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(data == NULL)
{
goto clean_and_return;
}
buf = e->NewByteArray(data->length);
e->SetByteArrayRegion(buf, 0, data->length, (jbyte *)data->buffer);
clean_and_return:
nfc_cb_data_deinit(&cb_data);
CONCURRENCY_UNLOCK();
return buf;
}
static jboolean com_android_nfc_NativeP2pDevice_doSend(
JNIEnv *e, jobject o, jbyteArray buf)
{
NFCSTATUS status;
phNfc_sData_t data;
jboolean result = JNI_FALSE;
struct nfc_jni_callback_data cb_data;
phLibNfc_Handle handle = nfc_jni_get_p2p_device_handle(e, o);
CONCURRENCY_LOCK();
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, NULL))
{
goto clean_and_return;
}
/* Send */
TRACE("Send data to the Initiator (handle = 0x%x)", handle);
data.length = (uint32_t)e->GetArrayLength(buf);
data.buffer = (uint8_t *)e->GetByteArrayElements(buf, NULL);
TRACE("phLibNfc_RemoteDev_Send()");
REENTRANCE_LOCK();
status = phLibNfc_RemoteDev_Send(handle, &data, nfc_jni_send_callback,(void *)&cb_data);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_PENDING)
{
LOGE("phLibNfc_RemoteDev_Send() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
goto clean_and_return;
}
TRACE("phLibNfc_RemoteDev_Send() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
/* 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)
{
goto clean_and_return;
}
result = JNI_TRUE;
clean_and_return:
if (result != JNI_TRUE)
{
e->ReleaseByteArrayElements(buf, (jbyte *)data.buffer, JNI_ABORT);
}
nfc_cb_data_deinit(&cb_data);
CONCURRENCY_UNLOCK();
return result;
}
/*
* JNI registration.
*/
static JNINativeMethod gMethods[] =
{
{"doConnect", "()Z",
(void *)com_android_nfc_NativeP2pDevice_doConnect},
{"doDisconnect", "()Z",
(void *)com_android_nfc_NativeP2pDevice_doDisconnect},
{"doTransceive", "([B)[B",
(void *)com_android_nfc_NativeP2pDevice_doTransceive},
{"doReceive", "()[B",
(void *)com_android_nfc_NativeP2pDevice_doReceive},
{"doSend", "([B)Z",
(void *)com_android_nfc_NativeP2pDevice_doSend},
};
int register_com_android_nfc_NativeP2pDevice(JNIEnv *e)
{
return jniRegisterNativeMethods(e,
"com/android/nfc/nxp/NativeP2pDevice",
gMethods, NELEM(gMethods));
}
} // namepspace android