blob: 0ba7b21a8c973dcd7997294a8d0f7c358e384167 [file] [log] [blame]
/*
* 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 <semaphore.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include "OverrideLog.h"
#include "NfcJniUtil.h"
#include "NfcTag.h"
#include "config.h"
#include "Mutex.h"
#include "IntervalTimer.h"
#include "JavaClassConstants.h"
#include "Pn544Interop.h"
extern "C"
{
#include "nfa_api.h"
#include "nfa_rw_api.h"
#include "ndef_utils.h"
#include "rw_api.h"
}
namespace android
{
extern nfc_jni_native_data* getNative(JNIEnv *e, jobject o);
extern bool nfcManager_isNfcActive();
extern int gGeneralTransceiveTimeout;
}
/*****************************************************************************
**
** public variables and functions
**
*****************************************************************************/
namespace android
{
bool gIsTagDeactivating = false; // flag for nfa callback indicating we are deactivating for RF interface switch
bool gIsSelectingRfInterface = false; // flag for nfa callback indicating we are selecting for RF interface switch
}
/*****************************************************************************
**
** private variables and functions
**
*****************************************************************************/
namespace android
{
// Pre-defined tag type values. These must match the values in
// framework Ndef.java for Google public NFC API.
#define NDEF_UNKNOWN_TYPE -1
#define NDEF_TYPE1_TAG 1
#define NDEF_TYPE2_TAG 2
#define NDEF_TYPE3_TAG 3
#define NDEF_TYPE4_TAG 4
#define NDEF_MIFARE_CLASSIC_TAG 101
#define STATUS_CODE_TARGET_LOST 146 // this error code comes from the service
static uint32_t sCheckNdefCurrentSize = 0;
static tNFA_STATUS sCheckNdefStatus = 0; //whether tag already contains a NDEF message
static bool sCheckNdefCapable = false; //whether tag has NDEF capability
static tNFA_HANDLE sNdefTypeHandlerHandle = NFA_HANDLE_INVALID;
static tNFA_INTF_TYPE sCurrentRfInterface = NFA_INTERFACE_ISO_DEP;
static uint8_t* sTransceiveData = NULL;
static uint32_t sTransceiveDataLen = 0;
static bool sWaitingForTransceive = false;
static bool sNeedToSwitchRf = false;
static Mutex sRfInterfaceMutex;
static uint32_t sReadDataLen = 0;
static uint8_t* sReadData = NULL;
static bool sIsReadingNdefMessage = false;
static SyncEvent sReadEvent;
static sem_t sWriteSem;
static sem_t sFormatSem;
static SyncEvent sTransceiveEvent;
static SyncEvent sReconnectEvent;
static sem_t sCheckNdefSem;
static sem_t sPresenceCheckSem;
static sem_t sMakeReadonlySem;
static IntervalTimer sSwitchBackTimer; // timer used to tell us to switch back to ISO_DEP frame interface
static jboolean sWriteOk = JNI_FALSE;
static jboolean sWriteWaitingForComplete = JNI_FALSE;
static bool sFormatOk = false;
static jboolean sConnectOk = JNI_FALSE;
static jboolean sConnectWaitingForComplete = JNI_FALSE;
static bool sGotDeactivate = false;
static uint32_t sCheckNdefMaxSize = 0;
static bool sCheckNdefCardReadOnly = false;
static jboolean sCheckNdefWaitingForComplete = JNI_FALSE;
static int sCountTagAway = 0; //count the consecutive number of presence-check failures
static tNFA_STATUS sMakeReadonlyStatus = NFA_STATUS_FAILED;
static jboolean sMakeReadonlyWaitingForComplete = JNI_FALSE;
static int reSelect (tNFA_INTF_TYPE rfInterface);
static bool switchRfInterface(tNFA_INTF_TYPE rfInterface);
/*******************************************************************************
**
** Function: nativeNfcTag_abortWaits
**
** Description: Unblock all thread synchronization objects.
**
** Returns: None
**
*******************************************************************************/
void nativeNfcTag_abortWaits ()
{
ALOGD ("%s", __FUNCTION__);
{
SyncEventGuard g (sReadEvent);
sReadEvent.notifyOne ();
}
sem_post (&sWriteSem);
sem_post (&sFormatSem);
{
SyncEventGuard g (sTransceiveEvent);
sTransceiveEvent.notifyOne ();
}
{
SyncEventGuard g (sReconnectEvent);
sReconnectEvent.notifyOne ();
}
sem_post (&sCheckNdefSem);
sem_post (&sPresenceCheckSem);
sem_post (&sMakeReadonlySem);
}
/*******************************************************************************
**
** Function: switchBackTimerProc
**
** Description: Callback function for interval timer.
**
** Returns: None
**
*******************************************************************************/
static void switchBackTimerProc (union sigval)
{
ALOGD ("%s", __FUNCTION__);
switchRfInterface(NFA_INTERFACE_ISO_DEP);
}
/*******************************************************************************
**
** Function: nativeNfcTag_doReadCompleted
**
** Description: Receive the completion status of read operation. Called by
** NFA_READ_CPLT_EVT.
** status: Status of operation.
**
** Returns: None
**
*******************************************************************************/
void nativeNfcTag_doReadCompleted (tNFA_STATUS status)
{
ALOGD ("%s: status=0x%X; is reading=%u", __FUNCTION__, status, sIsReadingNdefMessage);
if (sIsReadingNdefMessage == false)
return; //not reading NDEF message right now, so just return
if (status != NFA_STATUS_OK)
{
sReadDataLen = 0;
if (sReadData)
free (sReadData);
sReadData = NULL;
}
SyncEventGuard g (sReadEvent);
sReadEvent.notifyOne ();
}
/*******************************************************************************
**
** Function: ndefHandlerCallback
**
** Description: Receive NDEF-message related events from stack.
** event: Event code.
** p_data: Event data.
**
** Returns: None
**
*******************************************************************************/
static void ndefHandlerCallback (tNFA_NDEF_EVT event, tNFA_NDEF_EVT_DATA *eventData)
{
ALOGD ("%s: event=%u, eventData=%p", __FUNCTION__, event, eventData);
switch (event)
{
case NFA_NDEF_REGISTER_EVT:
{
tNFA_NDEF_REGISTER& ndef_reg = eventData->ndef_reg;
ALOGD ("%s: NFA_NDEF_REGISTER_EVT; status=0x%X; h=0x%X", __FUNCTION__, ndef_reg.status, ndef_reg.ndef_type_handle);
sNdefTypeHandlerHandle = ndef_reg.ndef_type_handle;
}
break;
case NFA_NDEF_DATA_EVT:
{
ALOGD ("%s: NFA_NDEF_DATA_EVT; data_len = %lu", __FUNCTION__, eventData->ndef_data.len);
sReadDataLen = eventData->ndef_data.len;
sReadData = (uint8_t*) malloc (sReadDataLen);
memcpy (sReadData, eventData->ndef_data.p_data, eventData->ndef_data.len);
}
break;
default:
ALOGE ("%s: Unknown event %u ????", __FUNCTION__, event);
break;
}
}
/*******************************************************************************
**
** Function: nativeNfcTag_doRead
**
** Description: Read the NDEF message on the tag.
** e: JVM environment.
** o: Java object.
**
** Returns: NDEF message.
**
*******************************************************************************/
static jbyteArray nativeNfcTag_doRead (JNIEnv *e, jobject o)
{
ALOGD ("%s: enter", __FUNCTION__);
tNFA_STATUS status = NFA_STATUS_FAILED;
jbyteArray buf = NULL;
sReadDataLen = 0;
if (sReadData != NULL)
{
free (sReadData);
sReadData = NULL;
}
if (sCheckNdefCurrentSize > 0)
{
{
SyncEventGuard g (sReadEvent);
sIsReadingNdefMessage = true;
status = NFA_RwReadNDef ();
sReadEvent.wait (); //wait for NFA_READ_CPLT_EVT
}
sIsReadingNdefMessage = false;
if (sReadDataLen > 0) //if stack actually read data from the tag
{
ALOGD ("%s: read %u bytes", __FUNCTION__, sReadDataLen);
buf = e->NewByteArray (sReadDataLen);
e->SetByteArrayRegion (buf, 0, sReadDataLen, (jbyte*) sReadData);
}
}
else
{
ALOGD ("%s: create emtpy buffer", __FUNCTION__);
static uint8_t* empty = (uint8_t*) "";
sReadDataLen = 0;
sReadData = (uint8_t*) malloc (1);
buf = e->NewByteArray (sReadDataLen);
e->SetByteArrayRegion (buf, 0, sReadDataLen, (jbyte*) sReadData);
}
if (sReadData)
{
free (sReadData);
sReadData = NULL;
}
sReadDataLen = 0;
ALOGD ("%s: exit", __FUNCTION__);
return buf;
}
/*******************************************************************************
**
** Function: nativeNfcTag_doWriteStatus
**
** Description: Receive the completion status of write operation. Called
** by NFA_WRITE_CPLT_EVT.
** isWriteOk: Status of operation.
**
** Returns: None
**
*******************************************************************************/
void nativeNfcTag_doWriteStatus (jboolean isWriteOk)
{
if (sWriteWaitingForComplete != JNI_FALSE)
{
sWriteWaitingForComplete = JNI_FALSE;
sWriteOk = isWriteOk;
sem_post (&sWriteSem);
}
}
/*******************************************************************************
**
** Function: nativeNfcTag_formatStatus
**
** Description: Receive the completion status of format operation. Called
** by NFA_FORMAT_CPLT_EVT.
** isOk: Status of operation.
**
** Returns: None
**
*******************************************************************************/
void nativeNfcTag_formatStatus (bool isOk)
{
sFormatOk = isOk;
sem_post (&sFormatSem);
}
/*******************************************************************************
**
** Function: nativeNfcTag_doWrite
**
** Description: Write a NDEF message to the tag.
** e: JVM environment.
** o: Java object.
** buf: Contains a NDEF message.
**
** Returns: True if ok.
**
*******************************************************************************/
static jboolean nativeNfcTag_doWrite (JNIEnv *e, jobject o, jbyteArray buf)
{
jboolean result = JNI_FALSE;
tNFA_STATUS status = 0;
UINT32 len = 0;
UINT8* p_data = NULL;
const int maxBufferSize = 1024;
UINT8 buffer[maxBufferSize] = { 0 };
UINT32 curDataSize = 0;
len = (UINT32) e->GetArrayLength (buf);
p_data = (UINT8*) e->GetByteArrayElements (buf, NULL);
ALOGD ("%s: enter; len = %lu", __FUNCTION__, len);
/* Create the write semaphore */
if (sem_init (&sWriteSem, 0, 0) == -1)
{
ALOGE ("%s: semaphore creation failed (errno=0x%08x)", __FUNCTION__, errno);
return JNI_FALSE;
}
sWriteWaitingForComplete = JNI_TRUE;
if (sCheckNdefStatus == NFA_STATUS_FAILED)
{
//if tag does not contain a NDEF message
//and tag is capable of storing NDEF message
if (sCheckNdefCapable)
{
ALOGD ("%s: try format", __FUNCTION__);
sem_init (&sFormatSem, 0, 0);
sFormatOk = false;
status = NFA_RwFormatTag ();
sem_wait (&sFormatSem);
sem_destroy (&sFormatSem);
if (sFormatOk == false) //if format operation failed
goto TheEnd;
}
ALOGD ("%s: try write", __FUNCTION__);
status = NFA_RwWriteNDef (p_data, len);
}
else if (len == 0)
{
//if (NXP TagWriter wants to erase tag) then create and write an empty ndef message
NDEF_MsgInit (buffer, maxBufferSize, &curDataSize);
status = NDEF_MsgAddRec (buffer, maxBufferSize, &curDataSize, NDEF_TNF_EMPTY, NULL, 0, NULL, 0, NULL, 0);
ALOGD ("%s: create empty ndef msg; status=%u; size=%lu", __FUNCTION__, status, curDataSize);
status = NFA_RwWriteNDef (buffer, curDataSize);
}
else
{
ALOGD ("%s: NFA_RwWriteNDef", __FUNCTION__);
status = NFA_RwWriteNDef (p_data, len);
}
if (status != NFA_STATUS_OK)
{
ALOGE ("%s: write/format error=%d", __FUNCTION__, status);
goto TheEnd;
}
/* Wait for write completion status */
sWriteOk = false;
if (sem_wait (&sWriteSem))
{
ALOGE ("%s: wait semaphore (errno=0x%08x)", __FUNCTION__, errno);
goto TheEnd;
}
result = sWriteOk;
TheEnd:
/* Destroy semaphore */
if (sem_destroy (&sWriteSem))
{
ALOGE ("%s: failed destroy semaphore (errno=0x%08x)", __FUNCTION__, errno);
}
sWriteWaitingForComplete = JNI_FALSE;
ALOGD ("%s: exit; result=%d", __FUNCTION__, result);
return result;
}
/*******************************************************************************
**
** Function: nativeNfcTag_doConnectStatus
**
** Description: Receive the completion status of connect operation.
** isConnectOk: Status of the operation.
**
** Returns: None
**
*******************************************************************************/
void nativeNfcTag_doConnectStatus (jboolean isConnectOk)
{
if (sConnectWaitingForComplete != JNI_FALSE)
{
sConnectWaitingForComplete = JNI_FALSE;
sConnectOk = isConnectOk;
SyncEventGuard g (sReconnectEvent);
sReconnectEvent.notifyOne ();
}
}
/*******************************************************************************
**
** Function: nativeNfcTag_doDeactivateStatus
**
** Description: Receive the completion status of deactivate operation.
**
** Returns: None
**
*******************************************************************************/
void nativeNfcTag_doDeactivateStatus (int status)
{
sGotDeactivate = (status == 0);
SyncEventGuard g (sReconnectEvent);
sReconnectEvent.notifyOne ();
}
/*******************************************************************************
**
** Function: nativeNfcTag_doConnect
**
** Description: Connect to the tag in RF field.
** e: JVM environment.
** o: Java object.
** targetHandle: Handle of the tag.
**
** Returns: Must return NXP status code, which NFC service expects.
**
*******************************************************************************/
static jint nativeNfcTag_doConnect (JNIEnv *e, jobject o, jint targetHandle)
{
ALOGD ("%s: targetHandle = %d", __FUNCTION__, targetHandle);
int i = targetHandle;
struct nfc_jni_native_data *nat = getNative (0, 0);
NfcTag& natTag = NfcTag::getInstance ();
int retCode = NFCSTATUS_SUCCESS;
sNeedToSwitchRf = false;
if (i >= NfcTag::MAX_NUM_TECHNOLOGY)
{
ALOGE ("%s: Handle not found", __FUNCTION__);
retCode = NFCSTATUS_FAILED;
goto TheEnd;
}
if (natTag.getActivationState() != NfcTag::Active)
{
ALOGE ("%s: tag already deactivated", __FUNCTION__);
retCode = NFCSTATUS_FAILED;
goto TheEnd;
}
if (natTag.mTechLibNfcTypes[i] != NFC_PROTOCOL_ISO_DEP)
{
ALOGD ("%s() Nfc type = %d, do nothing for non ISO_DEP", __FUNCTION__, natTag.mTechLibNfcTypes[i]);
retCode = NFCSTATUS_SUCCESS;
goto TheEnd;
}
if (natTag.mTechList[i] == TARGET_TYPE_ISO14443_3A || natTag.mTechList[i] == TARGET_TYPE_ISO14443_3B)
{
ALOGD ("%s: switching to tech: %d need to switch rf intf to frame", __FUNCTION__, natTag.mTechList[i]);
// connecting to NfcA or NfcB don't actually switch until/unless we get a transceive
sNeedToSwitchRf = true;
}
else
{
// connecting back to IsoDep or NDEF
return (switchRfInterface (NFA_INTERFACE_ISO_DEP) ? NFCSTATUS_SUCCESS : NFCSTATUS_FAILED);
}
TheEnd:
ALOGD ("%s: exit 0x%X", __FUNCTION__, retCode);
return retCode;
}
/*******************************************************************************
**
** Function: reSelect
**
** Description: Deactivates the tag and re-selects it with the specified
** rf interface.
**
** Returns: status code, 0 on success, 1 on failure,
** 146 (defined in service) on tag lost
**
*******************************************************************************/
static int reSelect (tNFA_INTF_TYPE rfInterface)
{
ALOGD ("%s: enter; rf intf = %d", __FUNCTION__, rfInterface);
NfcTag& natTag = NfcTag::getInstance ();
tNFA_STATUS status;
int rVal = 1;
do
{
//if tag has shutdown, abort this method
if (NfcTag::getInstance ().isNdefDetectionTimedOut())
{
ALOGD ("%s: ndef detection timeout; break", __FUNCTION__);
rVal = STATUS_CODE_TARGET_LOST;
break;
}
{
SyncEventGuard g (sReconnectEvent);
gIsTagDeactivating = true;
sGotDeactivate = false;
ALOGD ("%s: deactivate to sleep", __FUNCTION__);
if (NFA_STATUS_OK != (status = NFA_Deactivate (TRUE))) //deactivate to sleep state
{
ALOGE ("%s: deactivate failed, status = %d", __FUNCTION__, status);
break;
}
if (sReconnectEvent.wait (1000) == false) //if timeout occurred
{
ALOGE ("%s: timeout waiting for deactivate", __FUNCTION__);
}
}
if (NfcTag::getInstance ().getActivationState () != NfcTag::Sleep)
{
ALOGD ("%s: tag is not in sleep", __FUNCTION__);
rVal = STATUS_CODE_TARGET_LOST;
break;
}
gIsTagDeactivating = false;
{
SyncEventGuard g2 (sReconnectEvent);
sConnectWaitingForComplete = JNI_TRUE;
ALOGD ("%s: select interface %u", __FUNCTION__, rfInterface);
gIsSelectingRfInterface = true;
if (NFA_STATUS_OK != (status = NFA_Select (natTag.mTechHandles[0], natTag.mTechLibNfcTypes[0], rfInterface)))
{
ALOGE ("%s: NFA_Select failed, status = %d", __FUNCTION__, status);
break;
}
sConnectOk = false;
if (sReconnectEvent.wait (1000) == false) //if timeout occured
{
ALOGE ("%s: timeout waiting for select", __FUNCTION__);
break;
}
}
ALOGD("%s: select completed; sConnectOk=%d", __FUNCTION__, sConnectOk);
if (NfcTag::getInstance ().getActivationState () != NfcTag::Active)
{
ALOGD("%s: tag is not active", __FUNCTION__);
rVal = STATUS_CODE_TARGET_LOST;
break;
}
rVal = (sConnectOk) ? 0 : 1;
} while (0);
sConnectWaitingForComplete = JNI_FALSE;
gIsTagDeactivating = false;
gIsSelectingRfInterface = false;
ALOGD ("%s: exit; status=%d", __FUNCTION__, rVal);
return rVal;
}
/*******************************************************************************
**
** Function: switchRfInterface
**
** Description: Switch controller's RF interface to frame, ISO-DEP, or NFC-DEP.
** rfInterface: Type of RF interface.
**
** Returns: True if ok.
**
*******************************************************************************/
static bool switchRfInterface (tNFA_INTF_TYPE rfInterface)
{
ALOGD ("%s: rf intf = %d", __FUNCTION__, rfInterface);
NfcTag& natTag = NfcTag::getInstance ();
if (natTag.mTechLibNfcTypes[0] != NFC_PROTOCOL_ISO_DEP)
{
ALOGD ("%s: protocol: %d not ISO_DEP, do nothing", __FUNCTION__, natTag.mTechLibNfcTypes[0]);
return true;
}
sRfInterfaceMutex.lock ();
ALOGD ("%s: new rf intf = %d, cur rf intf = %d", __FUNCTION__, rfInterface, sCurrentRfInterface);
bool rVal = true;
if (rfInterface != sCurrentRfInterface)
{
if (rVal = (0 == reSelect(rfInterface)))
{
sCurrentRfInterface = rfInterface;
}
}
sRfInterfaceMutex.unlock ();
return rVal;
}
/*******************************************************************************
**
** Function: nativeNfcTag_doReconnect
**
** Description: Re-connect to the tag in RF field.
** e: JVM environment.
** o: Java object.
**
** Returns: Status code.
**
*******************************************************************************/
static jint nativeNfcTag_doReconnect (JNIEnv *e, jobject o)
{
ALOGD ("%s: enter", __FUNCTION__);
int retCode = NFCSTATUS_SUCCESS;
NfcTag& natTag = NfcTag::getInstance ();
if (natTag.getActivationState() != NfcTag::Active)
{
ALOGE ("%s: tag already deactivated", __FUNCTION__);
retCode = NFCSTATUS_FAILED;
goto TheEnd;
}
// this is only supported for type 2 or 4 (ISO_DEP) tags
if (natTag.mTechLibNfcTypes[0] == NFA_PROTOCOL_ISO_DEP)
retCode = reSelect(NFA_INTERFACE_ISO_DEP);
else if (natTag.mTechLibNfcTypes[0] == NFA_PROTOCOL_T2T)
retCode = reSelect(NFA_INTERFACE_FRAME);
TheEnd:
ALOGD ("%s: exit 0x%X", __FUNCTION__, retCode);
return retCode;
}
/*******************************************************************************
**
** Function: nativeNfcTag_doHandleReconnect
**
** Description: Re-connect to the tag in RF field.
** e: JVM environment.
** o: Java object.
** targetHandle: Handle of the tag.
**
** Returns: Status code.
**
*******************************************************************************/
static jint nativeNfcTag_doHandleReconnect (JNIEnv *e, jobject o, jint targetHandle)
{
ALOGD ("%s: targetHandle = %d", __FUNCTION__, targetHandle);
return nativeNfcTag_doConnect (e, o, targetHandle);
}
/*******************************************************************************
**
** Function: nativeNfcTag_doDisconnect
**
** Description: Deactivate the RF field.
** e: JVM environment.
** o: Java object.
**
** Returns: True if ok.
**
*******************************************************************************/
static jboolean nativeNfcTag_doDisconnect (JNIEnv *e, jobject o)
{
ALOGD ("%s: enter", __FUNCTION__);
struct nfc_jni_native_data *nat = getNative (0, 0);
tNFA_STATUS nfaStat = NFA_STATUS_OK;
gGeneralTransceiveTimeout = DEFAULT_GENERAL_TRANS_TIMEOUT;
if (NfcTag::getInstance ().getActivationState () != NfcTag::Active)
{
ALOGE ("%s: tag already deactivated", __FUNCTION__);
goto TheEnd;
}
nfaStat = NFA_Deactivate (FALSE);
if (nfaStat != NFA_STATUS_OK)
ALOGE ("%s: deactivate failed; error=0x%X", __FUNCTION__, nfaStat);
TheEnd:
ALOGD ("%s: exit", __FUNCTION__);
return (nfaStat == NFA_STATUS_OK) ? JNI_TRUE : JNI_FALSE;
}
/*******************************************************************************
**
** Function: nativeNfcTag_doTransceiveStatus
**
** Description: Receive the completion status of transceive operation.
** buf: Contains tag's response.
** bufLen: Length of buffer.
**
** Returns: None
**
*******************************************************************************/
void nativeNfcTag_doTransceiveStatus (uint8_t* buf, uint32_t bufLen)
{
ALOGD ("%s: data len=%d, waiting for transceive: %d", __FUNCTION__, bufLen, sWaitingForTransceive);
if (!sWaitingForTransceive)
return;
sTransceiveDataLen = 0;
if (bufLen)
{
if (NULL == (sTransceiveData = (uint8_t *) malloc (bufLen)))
{
ALOGD ("%s: memory allocation error", __FUNCTION__);
}
else
{
memcpy (sTransceiveData, buf, sTransceiveDataLen = bufLen);
}
}
{
SyncEventGuard g (sTransceiveEvent);
sTransceiveEvent.notifyOne ();
}
}
/*******************************************************************************
**
** Function: nativeNfcTag_doTransceive
**
** Description: Send raw data to the tag; receive tag's response.
** e: JVM environment.
** o: Java object.
** raw: Not used.
** statusTargetLost: Whether tag responds or times out.
**
** Returns: Response from tag.
**
*******************************************************************************/
static jbyteArray nativeNfcTag_doTransceive (JNIEnv *e, jobject o, jbyteArray data, jboolean raw, jintArray statusTargetLost)
{
ALOGD ("%s: enter; raw=%u; timeout = %d", __FUNCTION__, raw, gGeneralTransceiveTimeout);
bool fNeedToSwitchBack = false;
nfc_jni_native_data *nat = getNative (0, 0);
bool waitOk = false;
bool isNack = false;
uint8_t *buf = NULL;
uint32_t bufLen = 0;
jint *targetLost = NULL;
if (NfcTag::getInstance ().getActivationState () != NfcTag::Active)
{
if (statusTargetLost)
{
targetLost = e->GetIntArrayElements (statusTargetLost, 0);
if (targetLost)
*targetLost = 1; //causes NFC service to throw TagLostException
e->ReleaseIntArrayElements (statusTargetLost, targetLost, 0);
}
ALOGD ("%s: tag not active", __FUNCTION__);
return NULL;
}
NfcTag& natTag = NfcTag::getInstance ();
// get input buffer and length from java call
buf = (uint8_t *) e->GetByteArrayElements (data, NULL);
bufLen = (uint32_t) e->GetArrayLength (data);
if (statusTargetLost)
{
targetLost = e->GetIntArrayElements (statusTargetLost, 0);
if (targetLost)
*targetLost = 0; //success, tag is still present
}
sSwitchBackTimer.kill ();
jbyteArray result = NULL;
do
{
if (sNeedToSwitchRf)
{
// for ISO_DEP tags connected to NfcA or NfcB we need to be in FRAME interface
if (!switchRfInterface (NFA_INTERFACE_FRAME)) //NFA_INTERFACE_ISO_DEP
{
break;
}
fNeedToSwitchBack = true;
}
sWaitingForTransceive = true;
sTransceiveDataLen = 0;
{
SyncEventGuard g (sTransceiveEvent);
tNFA_STATUS status = NFA_SendRawFrame (buf, bufLen);
if (status != NFA_STATUS_OK)
{
ALOGE ("%s: fail send; error=%d", __FUNCTION__, status);
break;
}
waitOk = sTransceiveEvent.wait (gGeneralTransceiveTimeout);
}
if (waitOk == false) //if timeout occurred
{
ALOGE ("%s: wait response timeout", __FUNCTION__);
if (targetLost)
*targetLost = 1; //causes NFC service to throw TagLostException
break;
}
if (NfcTag::getInstance ().getActivationState () != NfcTag::Active)
{
ALOGE ("%s: already deactivated", __FUNCTION__);
if (targetLost)
*targetLost = 1; //causes NFC service to throw TagLostException
break;
}
ALOGD ("%s: response %d bytes", __FUNCTION__, sTransceiveDataLen);
if ((natTag.getProtocol () == NFA_PROTOCOL_T2T) &&
natTag.isT2tNackResponse (sTransceiveData, sTransceiveDataLen))
{
isNack = true;
}
if (sTransceiveDataLen)
{
if (!isNack) {
// marshall data to java for return
result = e->NewByteArray (sTransceiveDataLen);
if (result != NULL)
{
e->SetByteArrayRegion (result, 0, sTransceiveDataLen, (jbyte *) sTransceiveData);
}
else
ALOGE ("%s: Failed to allocate java byte array", __FUNCTION__);
} // else a nack is treated as a transceive failure to the upper layers
free (sTransceiveData);
sTransceiveData = NULL;
sTransceiveDataLen = 0;
}
} while (0);
sWaitingForTransceive = false;
e->ReleaseByteArrayElements (data, (jbyte *) buf, JNI_ABORT);
if (targetLost)
e->ReleaseIntArrayElements (statusTargetLost, targetLost, 0);
if (fNeedToSwitchBack)
{
// this timer proc will switch us back to ISO_DEP frame interface
sSwitchBackTimer.set (1500, switchBackTimerProc);
}
ALOGD ("%s: exit", __FUNCTION__);
return result;
}
/*******************************************************************************
**
** Function: nativeNfcTag_doGetNdefType
**
** Description: Retrieve the type of tag.
** e: JVM environment.
** o: Java object.
** libnfcType: Type of tag represented by JNI.
** javaType: Not used.
**
** Returns: Type of tag represented by NFC Service.
**
*******************************************************************************/
static jint nativeNfcTag_doGetNdefType (JNIEnv *e, jobject o, jint libnfcType, jint javaType)
{
ALOGD ("%s: enter; libnfc type=%d; java type=%d", __FUNCTION__, libnfcType, javaType);
jint ndefType = NDEF_UNKNOWN_TYPE;
// For NFA, libnfcType is mapped to the protocol value received
// in the NFA_ACTIVATED_EVT and NFA_DISC_RESULT_EVT event.
switch (libnfcType) {
case NFA_PROTOCOL_T1T:
ndefType = NDEF_TYPE1_TAG;
break;
case NFA_PROTOCOL_T2T:
ndefType = NDEF_TYPE2_TAG;;
break;
case NFA_PROTOCOL_T3T:
ndefType = NDEF_TYPE3_TAG;
break;
case NFA_PROTOCOL_ISO_DEP:
ndefType = NDEF_TYPE4_TAG;
break;
case NFA_PROTOCOL_ISO15693:
ndefType = NDEF_UNKNOWN_TYPE;
break;
case NFA_PROTOCOL_INVALID:
ndefType = NDEF_UNKNOWN_TYPE;
break;
default:
ndefType = NDEF_UNKNOWN_TYPE;
break;
}
ALOGD ("%s: exit; ndef type=%d", __FUNCTION__, ndefType);
return ndefType;
}
/*******************************************************************************
**
** Function: nativeNfcTag_doCheckNdefResult
**
** Description: Receive the result of checking whether the tag contains a NDEF
** message. Called by the NFA_NDEF_DETECT_EVT.
** status: Status of the operation.
** maxSize: Maximum size of NDEF message.
** currentSize: Current size of NDEF message.
** flags: Indicate various states.
**
** Returns: None
**
*******************************************************************************/
void nativeNfcTag_doCheckNdefResult (tNFA_STATUS status, uint32_t maxSize, uint32_t currentSize, uint8_t flags)
{
//this function's flags parameter is defined using the following macros
//in nfc/include/rw_api.h;
//#define RW_NDEF_FL_READ_ONLY 0x01 /* Tag is read only */
//#define RW_NDEF_FL_FORMATED 0x02 /* Tag formated for NDEF */
//#define RW_NDEF_FL_SUPPORTED 0x04 /* NDEF supported by the tag */
//#define RW_NDEF_FL_UNKNOWN 0x08 /* Unable to find if tag is ndef capable/formated/read only */
//#define RW_NDEF_FL_FORMATABLE 0x10 /* Tag supports format operation */
if (status == NFC_STATUS_BUSY)
{
ALOGE ("%s: stack is busy", __FUNCTION__);
return;
}
if (!sCheckNdefWaitingForComplete)
{
ALOGE ("%s: not waiting", __FUNCTION__);
return;
}
if (flags & RW_NDEF_FL_READ_ONLY)
ALOGD ("%s: flag read-only", __FUNCTION__);
if (flags & RW_NDEF_FL_FORMATED)
ALOGD ("%s: flag formatted for ndef", __FUNCTION__);
if (flags & RW_NDEF_FL_SUPPORTED)
ALOGD ("%s: flag ndef supported", __FUNCTION__);
if (flags & RW_NDEF_FL_UNKNOWN)
ALOGD ("%s: flag all unknown", __FUNCTION__);
if (flags & RW_NDEF_FL_FORMATABLE)
ALOGD ("%s: flag formattable", __FUNCTION__);
sCheckNdefWaitingForComplete = JNI_FALSE;
sCheckNdefStatus = status;
sCheckNdefCapable = false; //assume tag is NOT ndef capable
if (sCheckNdefStatus == NFA_STATUS_OK)
{
//NDEF content is on the tag
sCheckNdefMaxSize = maxSize;
sCheckNdefCurrentSize = currentSize;
sCheckNdefCardReadOnly = flags & RW_NDEF_FL_READ_ONLY;
sCheckNdefCapable = true;
}
else if (sCheckNdefStatus == NFA_STATUS_FAILED)
{
//no NDEF content on the tag
sCheckNdefMaxSize = 0;
sCheckNdefCurrentSize = 0;
sCheckNdefCardReadOnly = flags & RW_NDEF_FL_READ_ONLY;
if ((flags & RW_NDEF_FL_UNKNOWN) == 0) //if stack understands the tag
{
if (flags & RW_NDEF_FL_SUPPORTED) //if tag is ndef capable
sCheckNdefCapable = true;
}
}
else
{
ALOGE ("%s: unknown status=0x%X", __FUNCTION__, status);
sCheckNdefMaxSize = 0;
sCheckNdefCurrentSize = 0;
sCheckNdefCardReadOnly = false;
}
sem_post (&sCheckNdefSem);
}
/*******************************************************************************
**
** Function: nativeNfcTag_doCheckNdef
**
** Description: Does the tag contain a NDEF message?
** e: JVM environment.
** o: Java object.
** ndefInfo: NDEF info.
**
** Returns: Status code; 0 is success.
**
*******************************************************************************/
static jint nativeNfcTag_doCheckNdef (JNIEnv *e, jobject o, jintArray ndefInfo)
{
tNFA_STATUS status = NFA_STATUS_FAILED;
jint* ndef = NULL;
ALOGD ("%s: enter", __FUNCTION__);
/* Create the write semaphore */
if (sem_init (&sCheckNdefSem, 0, 0) == -1)
{
ALOGE ("%s: Check NDEF semaphore creation failed (errno=0x%08x)", __FUNCTION__, errno);
return JNI_FALSE;
}
if (NfcTag::getInstance ().getActivationState () != NfcTag::Active)
{
ALOGE ("%s: tag already deactivated", __FUNCTION__);
goto TheEnd;
}
ALOGD ("%s: try NFA_RwDetectNDef", __FUNCTION__);
sCheckNdefWaitingForComplete = JNI_TRUE;
status = NFA_RwDetectNDef ();
if (status != NFA_STATUS_OK)
{
ALOGE ("%s: NFA_RwDetectNDef failed, status = 0x%X", __FUNCTION__, status);
goto TheEnd;
}
/* Wait for check NDEF completion status */
if (sem_wait (&sCheckNdefSem))
{
ALOGE ("%s: Failed to wait for check NDEF semaphore (errno=0x%08x)", __FUNCTION__, errno);
goto TheEnd;
}
if (sCheckNdefStatus == NFA_STATUS_OK)
{
//stack found a NDEF message on the tag
ndef = e->GetIntArrayElements (ndefInfo, 0);
if (NfcTag::getInstance ().getProtocol () == NFA_PROTOCOL_T1T)
ndef[0] = NfcTag::getInstance ().getT1tMaxMessageSize ();
else
ndef[0] = sCheckNdefMaxSize;
if (sCheckNdefCardReadOnly)
ndef[1] = NDEF_MODE_READ_ONLY;
else
ndef[1] = NDEF_MODE_READ_WRITE;
e->ReleaseIntArrayElements (ndefInfo, ndef, 0);
status = NFA_STATUS_OK;
}
else if (sCheckNdefStatus == NFA_STATUS_FAILED)
{
//stack did not find a NDEF message on the tag;
ndef = e->GetIntArrayElements (ndefInfo, 0);
if (NfcTag::getInstance ().getProtocol () == NFA_PROTOCOL_T1T)
ndef[0] = NfcTag::getInstance ().getT1tMaxMessageSize ();
else
ndef[0] = sCheckNdefMaxSize;
if (sCheckNdefCardReadOnly)
ndef[1] = NDEF_MODE_READ_ONLY;
else
ndef[1] = NDEF_MODE_READ_WRITE;
e->ReleaseIntArrayElements (ndefInfo, ndef, 0);
status = NFA_STATUS_FAILED;
}
else if (sCheckNdefStatus == NFA_STATUS_TIMEOUT)
{
pn544InteropStopPolling ();
status = sCheckNdefStatus;
}
else
{
ALOGD ("%s: unknown status 0x%X", __FUNCTION__, sCheckNdefStatus);
status = sCheckNdefStatus;
}
TheEnd:
/* Destroy semaphore */
if (sem_destroy (&sCheckNdefSem))
{
ALOGE ("%s: Failed to destroy check NDEF semaphore (errno=0x%08x)", __FUNCTION__, errno);
}
sCheckNdefWaitingForComplete = JNI_FALSE;
ALOGD ("%s: exit; status=0x%X", __FUNCTION__, status);
return status;
}
/*******************************************************************************
**
** Function: nativeNfcTag_resetPresenceCheck
**
** Description: Reset variables related to presence-check.
**
** Returns: None
**
*******************************************************************************/
void nativeNfcTag_resetPresenceCheck ()
{
sCountTagAway = 0;
}
/*******************************************************************************
**
** Function: nativeNfcTag_doPresenceCheckResult
**
** Description: Receive the result of presence-check.
** status: Result of presence-check.
**
** Returns: None
**
*******************************************************************************/
void nativeNfcTag_doPresenceCheckResult (tNFA_STATUS status)
{
if (status == NFA_STATUS_OK)
sCountTagAway = 0;
else
sCountTagAway++;
if (sCountTagAway > 0)
ALOGD ("%s: sCountTagAway=%d", __FUNCTION__, sCountTagAway);
sem_post (&sPresenceCheckSem);
}
/*******************************************************************************
**
** Function: nativeNfcTag_doPresenceCheck
**
** Description: Check if the tag is in the RF field.
** e: JVM environment.
** o: Java object.
**
** Returns: True if tag is in RF field.
**
*******************************************************************************/
static jboolean nativeNfcTag_doPresenceCheck (JNIEnv *e, jobject o)
{
ALOGD ("%s", __FUNCTION__);
tNFA_STATUS status = NFA_STATUS_OK;
jboolean isPresent = JNI_FALSE;
if (nfcManager_isNfcActive() == false)
{
ALOGD ("%s: NFC is no longer active.", __FUNCTION__);
return JNI_FALSE;
}
if (NfcTag::getInstance ().getActivationState () != NfcTag::Active)
{
ALOGD ("%s: tag already deactivated", __FUNCTION__);
return JNI_FALSE;
}
if (sem_init (&sPresenceCheckSem, 0, 0) == -1)
{
ALOGE ("%s: semaphore creation failed (errno=0x%08x)", __FUNCTION__, errno);
return JNI_FALSE;
}
status = NFA_RwPresenceCheck ();
if (status == NFA_STATUS_OK)
{
if (sem_wait (&sPresenceCheckSem))
{
ALOGE ("%s: failed to wait (errno=0x%08x)", __FUNCTION__, errno);
}
else
{
isPresent = (sCountTagAway > 3) ? JNI_FALSE : JNI_TRUE;
}
}
if (sem_destroy (&sPresenceCheckSem))
{
ALOGE ("Failed to destroy check NDEF semaphore (errno=0x%08x)", errno);
}
if (isPresent == JNI_FALSE)
ALOGD ("%s: tag absent ????", __FUNCTION__);
return isPresent;
}
/*******************************************************************************
**
** Function: nativeNfcTag_doIsNdefFormatable
**
** Description: Can tag be formatted to store NDEF message?
** e: JVM environment.
** o: Java object.
** libNfcType: Type of tag.
** uidBytes: Tag's unique ID.
** pollBytes: Data from activation.
** actBytes: Data from activation.
**
** Returns: True if formattable.
**
*******************************************************************************/
static jboolean nativeNfcTag_doIsNdefFormatable (JNIEnv *e,
jobject o, jint libNfcType, jbyteArray uidBytes, jbyteArray pollBytes,
jbyteArray actBytes)
{
jboolean isFormattable = JNI_FALSE;
switch (NfcTag::getInstance().getProtocol())
{
case NFA_PROTOCOL_T1T:
case NFA_PROTOCOL_ISO15693:
isFormattable = JNI_TRUE;
break;
case NFA_PROTOCOL_T2T:
isFormattable = NfcTag::getInstance().isMifareUltralight() ? JNI_TRUE : JNI_FALSE;
}
ALOGD("%s: is formattable=%u", __FUNCTION__, isFormattable);
return isFormattable;
}
/*******************************************************************************
**
** Function: nativeNfcTag_doIsIsoDepNdefFormatable
**
** Description: Is ISO-DEP tag formattable?
** e: JVM environment.
** o: Java object.
** pollBytes: Data from activation.
** actBytes: Data from activation.
**
** Returns: True if formattable.
**
*******************************************************************************/
static jboolean nativeNfcTag_doIsIsoDepNdefFormatable (JNIEnv *e, jobject o, jbyteArray pollBytes, jbyteArray actBytes)
{
uint8_t uidFake[] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
ALOGD ("%s", __FUNCTION__);
jbyteArray uidArray = e->NewByteArray (8);
e->SetByteArrayRegion (uidArray, 0, 8, (jbyte*) uidFake);
return nativeNfcTag_doIsNdefFormatable (e, o, 0, uidArray, pollBytes, actBytes);
}
/*******************************************************************************
**
** Function: nativeNfcTag_doNdefFormat
**
** Description: Format a tag so it can store NDEF message.
** e: JVM environment.
** o: Java object.
** key: Not used.
**
** Returns: True if ok.
**
*******************************************************************************/
static jboolean nativeNfcTag_doNdefFormat (JNIEnv *e, jobject o, jbyteArray key)
{
ALOGD ("%s: enter", __FUNCTION__);
tNFA_STATUS status = NFA_STATUS_OK;
sem_init (&sFormatSem, 0, 0);
sFormatOk = false;
status = NFA_RwFormatTag ();
if (status == NFA_STATUS_OK)
{
ALOGD ("%s: wait for completion", __FUNCTION__);
sem_wait (&sFormatSem);
status = sFormatOk ? NFA_STATUS_OK : NFA_STATUS_FAILED;
}
else
ALOGE ("%s: error status=%u", __FUNCTION__, status);
sem_destroy (&sFormatSem);
ALOGD ("%s: exit", __FUNCTION__);
return (status == NFA_STATUS_OK) ? JNI_TRUE : JNI_FALSE;
}
/*******************************************************************************
**
** Function: nativeNfcTag_doMakeReadonlyResult
**
** Description: Receive the result of making a tag read-only. Called by the
** NFA_SET_TAG_RO_EVT.
** status: Status of the operation.
**
** Returns: None
**
*******************************************************************************/
void nativeNfcTag_doMakeReadonlyResult (tNFA_STATUS status)
{
if (sMakeReadonlyWaitingForComplete != JNI_FALSE)
{
sMakeReadonlyWaitingForComplete = JNI_FALSE;
sMakeReadonlyStatus = status;
sem_post (&sMakeReadonlySem);
}
}
/*******************************************************************************
**
** Function: nativeNfcTag_doMakeReadonly
**
** Description: Make the tag read-only.
** e: JVM environment.
** o: Java object.
** key: Key to access the tag.
**
** Returns: True if ok.
**
*******************************************************************************/
static jboolean nativeNfcTag_doMakeReadonly (JNIEnv *e, jobject o, jbyteArray key)
{
jboolean result = JNI_FALSE;
tNFA_STATUS status;
ALOGD ("%s", __FUNCTION__);
/* Create the make_readonly semaphore */
if (sem_init (&sMakeReadonlySem, 0, 0) == -1)
{
ALOGE ("%s: Make readonly semaphore creation failed (errno=0x%08x)", __FUNCTION__, errno);
return JNI_FALSE;
}
sMakeReadonlyWaitingForComplete = JNI_TRUE;
// Hard-lock the tag (cannot be reverted)
status = NFA_RwSetTagReadOnly(TRUE);
if (status != NFA_STATUS_OK)
{
ALOGE ("%s: NFA_RwSetTagReadOnly failed, status = %d", __FUNCTION__, status);
goto TheEnd;
}
/* Wait for check NDEF completion status */
if (sem_wait (&sMakeReadonlySem))
{
ALOGE ("%s: Failed to wait for make_readonly semaphore (errno=0x%08x)", __FUNCTION__, errno);
goto TheEnd;
}
if (sMakeReadonlyStatus == NFA_STATUS_OK)
{
result = JNI_TRUE;
}
TheEnd:
/* Destroy semaphore */
if (sem_destroy (&sMakeReadonlySem))
{
ALOGE ("%s: Failed to destroy read_only semaphore (errno=0x%08x)", __FUNCTION__, errno);
}
sMakeReadonlyWaitingForComplete = JNI_FALSE;
return result;
}
/*******************************************************************************
**
** Function: nativeNfcTag_registerNdefTypeHandler
**
** Description: Register a callback to receive NDEF message from the tag
** from the NFA_NDEF_DATA_EVT.
**
** Returns: None
**
*******************************************************************************/
//register a callback to receive NDEF message from the tag
//from the NFA_NDEF_DATA_EVT;
void nativeNfcTag_registerNdefTypeHandler ()
{
ALOGD ("%s", __FUNCTION__);
sNdefTypeHandlerHandle = NFA_HANDLE_INVALID;
NFA_RegisterNDefTypeHandler (TRUE, NFA_TNF_DEFAULT, (UINT8 *) "", 0, ndefHandlerCallback);
}
/*******************************************************************************
**
** Function: nativeNfcTag_deregisterNdefTypeHandler
**
** Description: No longer need to receive NDEF message from the tag.
**
** Returns: None
**
*******************************************************************************/
void nativeNfcTag_deregisterNdefTypeHandler ()
{
ALOGD ("%s", __FUNCTION__);
NFA_DeregisterNDefTypeHandler (sNdefTypeHandlerHandle);
sNdefTypeHandlerHandle = NFA_HANDLE_INVALID;
}
/*****************************************************************************
**
** JNI functions for Android 4.0.3
**
*****************************************************************************/
static JNINativeMethod gMethods[] =
{
{"doConnect", "(I)I", (void *)nativeNfcTag_doConnect},
{"doDisconnect", "()Z", (void *)nativeNfcTag_doDisconnect},
{"doReconnect", "()I", (void *)nativeNfcTag_doReconnect},
{"doHandleReconnect", "(I)I", (void *)nativeNfcTag_doHandleReconnect},
{"doTransceive", "([BZ[I)[B", (void *)nativeNfcTag_doTransceive},
{"doGetNdefType", "(II)I", (void *)nativeNfcTag_doGetNdefType},
{"doCheckNdef", "([I)I", (void *)nativeNfcTag_doCheckNdef},
{"doRead", "()[B", (void *)nativeNfcTag_doRead},
{"doWrite", "([B)Z", (void *)nativeNfcTag_doWrite},
{"doPresenceCheck", "()Z", (void *)nativeNfcTag_doPresenceCheck},
{"doIsIsoDepNdefFormatable", "([B[B)Z", (void *)nativeNfcTag_doIsIsoDepNdefFormatable},
{"doNdefFormat", "([B)Z", (void *)nativeNfcTag_doNdefFormat},
{"doMakeReadonly", "([B)Z", (void *)nativeNfcTag_doMakeReadonly},
};
/*******************************************************************************
**
** Function: register_com_android_nfc_NativeNfcTag
**
** Description: Regisgter JNI functions with Java Virtual Machine.
** e: Environment of JVM.
**
** Returns: Status of registration.
**
*******************************************************************************/
int register_com_android_nfc_NativeNfcTag (JNIEnv *e)
{
ALOGD ("%s", __FUNCTION__);
return jniRegisterNativeMethods (e, gNativeNfcTagClassName, gMethods, NELEM (gMethods));
}
} /* namespace android */