blob: f4a54df342767654d24ea994ece032428fa69c23 [file] [log] [blame]
/*
* 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"
static phLibNfc_Data_t nfc_jni_ndef_rw;
static phLibNfc_Handle handle;
uint8_t *nfc_jni_ndef_buf = NULL;
uint32_t nfc_jni_ndef_buf_len = 0;
namespace android {
extern phLibNfc_Handle storedHandle;
extern void nfc_jni_restart_discovery_locked(struct nfc_jni_native_data *nat);
/*
* Callbacks
*/
static void nfc_jni_tag_rw_callback(void *pContext, NFCSTATUS status)
{
struct nfc_jni_callback_data * pCallbackData = (struct nfc_jni_callback_data *) pContext;
LOG_CALLBACK("nfc_jni_tag_rw_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;
LOG_CALLBACK("nfc_jni_connect_callback", status);
/* Report the callback status and wake up the caller */
pCallbackData->status = status;
sem_post(&pCallbackData->sem);
}
static void nfc_jni_checkndef_callback(void *pContext,
phLibNfc_ChkNdef_Info_t info, NFCSTATUS status)
{
struct nfc_jni_callback_data * pCallbackData = (struct nfc_jni_callback_data *) pContext;
LOG_CALLBACK("nfc_jni_checkndef_callback", status);
if(status == NFCSTATUS_OK)
{
if(nfc_jni_ndef_buf)
{
free(nfc_jni_ndef_buf);
}
nfc_jni_ndef_buf_len = info.MaxNdefMsgLength;
nfc_jni_ndef_buf = (uint8_t*)malloc(nfc_jni_ndef_buf_len);
}
/* 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);
if(nfc_jni_ndef_buf)
{
free(nfc_jni_ndef_buf);
}
nfc_jni_ndef_buf = NULL;
nfc_jni_ndef_buf_len = 0;
/* Report the callback status and wake up the caller */
pCallbackData->status = status;
sem_post(&pCallbackData->sem);
}
static void nfc_jni_async_disconnect_callback(void *pContext,
phLibNfc_Handle hRemoteDev, NFCSTATUS status)
{
LOG_CALLBACK("nfc_jni_async_disconnect_callback", status);
if(nfc_jni_ndef_buf)
{
free(nfc_jni_ndef_buf);
}
nfc_jni_ndef_buf = NULL;
nfc_jni_ndef_buf_len = 0;
}
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_async_presence_check_callback(void* pContext,NFCSTATUS status)
{
NFCSTATUS ret;
JNIEnv* env = (JNIEnv*)pContext;
LOG_CALLBACK("nfc_jni_async_presence_check_callback", status);
if(status != NFCSTATUS_SUCCESS)
{
/* Disconnect & Restart Polling loop */
TRACE("Tag removed from the RF Field\n");
TRACE("phLibNfc_RemoteDev_Disconnect(async)");
REENTRANCE_LOCK();
ret = phLibNfc_RemoteDev_Disconnect(handle, NFC_DISCOVERY_CONTINUE, nfc_jni_async_disconnect_callback,(void*)handle);
REENTRANCE_UNLOCK();
if(ret != NFCSTATUS_PENDING)
{
LOGE("phLibNfc_RemoteDev_Disconnect() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
/* concurrency lock held while in callback */
nfc_jni_restart_discovery_locked(nfc_jni_get_nat_ext(env));
return;
}
TRACE("phLibNfc_RemoteDev_Disconnect() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
}
else
{
TRACE("phLibNfc_RemoteDev_CheckPresence(async)");
/* Presence Check */
REENTRANCE_LOCK();
ret = phLibNfc_RemoteDev_CheckPresence(handle,nfc_jni_async_presence_check_callback, (void*)env);
REENTRANCE_UNLOCK();
if(ret != NFCSTATUS_PENDING)
{
LOGE("phLibNfc_RemoteDev_CheckPresence() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
return;
}
TRACE("phLibNfc_RemoteDev_CheckPresence() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
}
}
static phNfc_sData_t *nfc_jni_transceive_buffer;
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);
nfc_jni_transceive_buffer = pResBuffer;
/* Report the callback status and wake up the caller */
pCallbackData->status = status;
sem_post(&pCallbackData->sem);
}
static void nfc_jni_presencecheck_callback(void *pContext, NFCSTATUS status)
{
struct nfc_jni_callback_data * pCallbackData = (struct nfc_jni_callback_data *) pContext;
LOG_CALLBACK("nfc_jni_presencecheck_callback", status);
/* Report the callback status and wake up the caller */
pCallbackData->status = status;
sem_post(&pCallbackData->sem);
}
/* Functions */
static jbyteArray com_android_nfc_NativeNfcTag_doRead(JNIEnv *e,
jobject o)
{
NFCSTATUS status;
phLibNfc_Handle handle = 0;
jbyteArray buf = NULL;
struct nfc_jni_callback_data cb_data;
CONCURRENCY_LOCK();
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, NULL))
{
goto clean_and_return;
}
handle = nfc_jni_get_nfc_tag_handle(e, o);
nfc_jni_ndef_rw.length = nfc_jni_ndef_buf_len;
nfc_jni_ndef_rw.buffer = nfc_jni_ndef_buf;
TRACE("phLibNfc_Ndef_Read()");
REENTRANCE_LOCK();
status = phLibNfc_Ndef_Read(handle, &nfc_jni_ndef_rw,
phLibNfc_Ndef_EBegin,
nfc_jni_tag_rw_callback,
(void *)&cb_data);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_PENDING)
{
LOGE("phLibNfc_Ndef_Read() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
goto clean_and_return;
}
TRACE("phLibNfc_Ndef_Read() 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;
}
buf = e->NewByteArray(nfc_jni_ndef_rw.length);
e->SetByteArrayRegion(buf, 0, nfc_jni_ndef_rw.length,
(jbyte *)nfc_jni_ndef_rw.buffer);
clean_and_return:
nfc_cb_data_deinit(&cb_data);
CONCURRENCY_UNLOCK();
return buf;
}
static jboolean com_android_nfc_NativeNfcTag_doWrite(JNIEnv *e,
jobject o, jbyteArray buf)
{
NFCSTATUS status;
jboolean result = JNI_FALSE;
struct nfc_jni_callback_data cb_data;
phLibNfc_Handle handle = nfc_jni_get_nfc_tag_handle(e, o);
CONCURRENCY_LOCK();
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, NULL))
{
goto clean_and_return;
}
nfc_jni_ndef_rw.length = (uint32_t)e->GetArrayLength(buf);
nfc_jni_ndef_rw.buffer = (uint8_t *)e->GetByteArrayElements(buf, NULL);
TRACE("phLibNfc_Ndef_Write()");
TRACE("Ndef Handle :0x%x\n",handle);
TRACE("Ndef buffer length : %d", nfc_jni_ndef_rw.length);
REENTRANCE_LOCK();
status = phLibNfc_Ndef_Write(handle, &nfc_jni_ndef_rw,nfc_jni_tag_rw_callback, (void *)&cb_data);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_PENDING)
{
LOGE("phLibNfc_Ndef_Write() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
goto clean_and_return;
}
TRACE("phLibNfc_Ndef_Write() 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 *)nfc_jni_ndef_rw.buffer, JNI_ABORT);
}
nfc_cb_data_deinit(&cb_data);
CONCURRENCY_UNLOCK();
return result;
}
static jboolean com_android_nfc_NativeNfcTag_doConnect(JNIEnv *e,
jobject o)
{
phLibNfc_Handle handle = 0;
jclass cls;
jfieldID f;
NFCSTATUS status;
jboolean result = JNI_FALSE;
struct nfc_jni_callback_data cb_data;
CONCURRENCY_LOCK();
handle = nfc_jni_get_nfc_tag_handle(e, o);
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, NULL))
{
goto clean_and_return;
}
TRACE("phLibNfc_RemoteDev_Connect(RW)");
REENTRANCE_LOCK();
status = phLibNfc_RemoteDev_Connect(handle, nfc_jni_connect_callback,(void *)&cb_data);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_PENDING)
{
LOGE("phLibNfc_RemoteDev_Connect(RW) returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
goto clean_and_return;
}
TRACE("phLibNfc_RemoteDev_Connect(RW) 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;
}
/* Connect Status */
if(cb_data.status != NFCSTATUS_SUCCESS)
{
goto clean_and_return;
}
result = JNI_TRUE;
clean_and_return:
nfc_cb_data_deinit(&cb_data);
CONCURRENCY_UNLOCK();
return result;
}
static jboolean com_android_nfc_NativeNfcTag_doDisconnect(JNIEnv *e, jobject o)
{
phLibNfc_Handle handle = 0;
jclass cls;
jfieldID f;
NFCSTATUS status;
jboolean result = JNI_FALSE;
struct nfc_jni_callback_data cb_data;
CONCURRENCY_LOCK();
handle = nfc_jni_get_nfc_tag_handle(e, o);
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, NULL))
{
goto clean_and_return;
}
/* Reset the stored handle */
storedHandle = 0;
/* Disconnect */
TRACE("Disconnecting from tag (%x)", handle);
/* Presence Check */
do
{
TRACE("phLibNfc_RemoteDev_CheckPresence(%x)", handle);
REENTRANCE_LOCK();
status = phLibNfc_RemoteDev_CheckPresence(handle,nfc_jni_presence_check_callback,(void *)&cb_data);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_PENDING)
{
LOGE("phLibNfc_RemoteDev_CheckPresence(%x) returned 0x%04x[%s]", handle, status, nfc_jni_get_status_name(status));
/* Disconnect Tag */
break;
}
TRACE("phLibNfc_RemoteDev_CheckPresence(%x) returned 0x%04x[%s]", handle, 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;
}
} while(cb_data.status == NFCSTATUS_SUCCESS);
TRACE("Tag removed from the RF Field\n");
TRACE("phLibNfc_RemoteDev_Disconnect(%x)", handle);
REENTRANCE_LOCK();
status = phLibNfc_RemoteDev_Disconnect(handle, NFC_DISCOVERY_CONTINUE,
nfc_jni_disconnect_callback, (void *)&cb_data);
REENTRANCE_UNLOCK();
if(status == NFCSTATUS_TARGET_NOT_CONNECTED)
{
result = JNI_TRUE;
TRACE("phLibNfc_RemoteDev_Disconnect() - Target already disconnected");
goto clean_and_return;
}
if(status != NFCSTATUS_PENDING)
{
LOGE("phLibNfc_RemoteDev_Disconnect(%x) returned 0x%04x[%s]", handle, status, nfc_jni_get_status_name(status));
nfc_jni_restart_discovery_locked(nfc_jni_get_nat_ext(e));
goto clean_and_return;
}
TRACE("phLibNfc_RemoteDev_Disconnect(%x) returned 0x%04x[%s]", handle, 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:
nfc_cb_data_deinit(&cb_data);
CONCURRENCY_UNLOCK();
return result;
}
static jbyteArray com_android_nfc_NativeNfcTag_doTransceive(JNIEnv *e,
jobject o, jbyteArray data)
{
uint8_t offset = 0;
uint8_t *buf;
uint32_t buflen;
phLibNfc_sTransceiveInfo_t transceive_info;
jbyteArray result = NULL;
int res;
jstring type = nfc_jni_get_nfc_tag_type(e, o);
const char* str = e->GetStringUTFChars(type, 0);
phLibNfc_Handle handle = nfc_jni_get_nfc_tag_handle(e, o);
NFCSTATUS status;
struct nfc_jni_callback_data cb_data;
CONCURRENCY_LOCK();
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, NULL))
{
goto clean_and_return;
}
TRACE("Tag %s\n", str);
buf = (uint8_t *)e->GetByteArrayElements(data, NULL);
buflen = (uint32_t)e->GetArrayLength(data);
/* Prepare transceive info structure */
if((res = strcmp(str, "Mifare1K") == 0) || (res = strcmp(str, "Mifare4K") == 0) || (res = strcmp(str, "MifareUL") == 0))
{
offset = 2;
transceive_info.cmd.MfCmd = (phNfc_eMifareCmdList_t)buf[0];
transceive_info.addr = (uint8_t)buf[1];
}
else if((res = strcmp(str, "Felica") == 0))
{
transceive_info.cmd.FelCmd = phNfc_eFelica_Raw;
transceive_info.addr = 0;
}
else if((res = strcmp(str, "Iso14443") == 0))
{
transceive_info.cmd.Iso144434Cmd = phNfc_eIso14443_4_Raw;
transceive_info.addr = 0;
}
else if((res = strcmp(str, "Jewel") == 0))
{
transceive_info.cmd.JewelCmd = phNfc_eJewel_Raw;
transceive_info.addr = 0;
}
/* Free memory */
e->ReleaseStringUTFChars(type, str);
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()");
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() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
goto clean_and_return;
}
TRACE("phLibNfc_RemoteDev_Transceive() 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(nfc_jni_transceive_buffer->length);
if(result != NULL)
{
e->SetByteArrayRegion(result, 0,
nfc_jni_transceive_buffer->length,
(jbyte *)nfc_jni_transceive_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 jboolean com_android_nfc_NativeNfcTag_doCheckNdef(JNIEnv *e, jobject o)
{
phLibNfc_Handle handle = 0;
NFCSTATUS status;
jboolean result = JNI_FALSE;
struct nfc_jni_callback_data cb_data;
CONCURRENCY_LOCK();
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, NULL))
{
goto clean_and_return;
}
handle = nfc_jni_get_nfc_tag_handle(e, o);
TRACE("phLibNfc_Ndef_CheckNdef()");
REENTRANCE_LOCK();
status = phLibNfc_Ndef_CheckNdef(handle, nfc_jni_checkndef_callback,(void *)&cb_data);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_PENDING)
{
LOGE("phLibNfc_Ndef_CheckNdef() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
goto clean_and_return;
}
TRACE("phLibNfc_Ndef_CheckNdef() 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:
nfc_cb_data_deinit(&cb_data);
CONCURRENCY_UNLOCK();
return result;
}
static jboolean com_android_nfc_NativeNfcTag_doPresenceCheck(JNIEnv *e, jobject o)
{
phLibNfc_Handle handle = 0;
NFCSTATUS status;
jboolean result = JNI_FALSE;
struct nfc_jni_callback_data cb_data;
CONCURRENCY_LOCK();
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, NULL))
{
goto clean_and_return;
}
handle = nfc_jni_get_nfc_tag_handle(e, o);
TRACE("phLibNfc_RemoteDev_CheckPresence()");
REENTRANCE_LOCK();
status = phLibNfc_RemoteDev_CheckPresence(handle, nfc_jni_presencecheck_callback, (void *)&cb_data);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_PENDING)
{
LOGE("phLibNfc_RemoteDev_CheckPresence() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
goto clean_and_return;
}
TRACE("phLibNfc_RemoteDev_CheckPresence() 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)
{
result = JNI_TRUE;
}
clean_and_return:
nfc_cb_data_deinit(&cb_data);
CONCURRENCY_UNLOCK();
return result;
}
/*
* JNI registration.
*/
static JNINativeMethod gMethods[] =
{
{"doConnect", "()Z",
(void *)com_android_nfc_NativeNfcTag_doConnect},
{"doDisconnect", "()Z",
(void *)com_android_nfc_NativeNfcTag_doDisconnect},
{"doTransceive", "([B)[B",
(void *)com_android_nfc_NativeNfcTag_doTransceive},
{"doCheckNdef", "()Z",
(void *)com_android_nfc_NativeNfcTag_doCheckNdef},
{"doRead", "()[B",
(void *)com_android_nfc_NativeNfcTag_doRead},
{"doWrite", "([B)Z",
(void *)com_android_nfc_NativeNfcTag_doWrite},
{"doPresenceCheck", "()Z",
(void *)com_android_nfc_NativeNfcTag_doPresenceCheck},
};
int register_com_android_nfc_NativeNfcTag(JNIEnv *e)
{
return jniRegisterNativeMethods(e,
"com/android/nfc/NativeNfcTag",
gMethods, NELEM(gMethods));
}
} // namespace android