/*
* 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 <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <sys/queue.h>
#include <hardware/hardware.h>
#include <hardware/nfc.h>
#include <cutils/properties.h>
#include <ScopedLocalRef.h>
#include "com_android_nfc.h"
#define ERROR_BUFFER_TOO_SMALL -12
#define ERROR_INSUFFICIENT_RESOURCES -9
extern uint32_t libnfc_llc_error_count;
static phLibNfc_sConfig_t gDrvCfg;
void *gHWRef;
static phNfc_sData_t gInputParam;
static phNfc_sData_t gOutputParam;
uint8_t device_connected_flag;
static bool driverConfigured = FALSE;
static phLibNfc_Handle hLlcpHandle;
static NFCSTATUS lastErrorStatus = NFCSTATUS_FAILED;
static phLibNfc_Llcp_eLinkStatus_t g_eLinkStatus = phFriNfc_LlcpMac_eLinkDefault;
static jmethodID cached_NfcManager_notifyNdefMessageListeners;
static jmethodID cached_NfcManager_notifyLlcpLinkActivation;
static jmethodID cached_NfcManager_notifyLlcpLinkDeactivated;
static jmethodID cached_NfcManager_notifyTargetDeselected;
static jmethodID cached_NfcManager_notifyRfFieldActivated;
static jmethodID cached_NfcManager_notifyRfFieldDeactivated;
namespace android {
phLibNfc_Handle storedHandle = 0;
struct nfc_jni_native_data *exported_nat = NULL;
/* Internal functions declaration */
static void *nfc_jni_client_thread(void *arg);
static void nfc_jni_init_callback(void *pContext, NFCSTATUS status);
static void nfc_jni_deinit_callback(void *pContext, NFCSTATUS status);
static void nfc_jni_discover_callback(void *pContext, NFCSTATUS status);
static void nfc_jni_se_set_mode_callback(void *context,
phLibNfc_Handle handle, NFCSTATUS status);
static void nfc_jni_llcpcfg_callback(void *pContext, NFCSTATUS status);
static void nfc_jni_start_discovery_locked(struct nfc_jni_native_data *nat, bool resume);
static void nfc_jni_Discovery_notification_callback(void *pContext,
phLibNfc_RemoteDevList_t *psRemoteDevList,
uint8_t uNofRemoteDev, NFCSTATUS status);
static void nfc_jni_transaction_callback(void *context,
phLibNfc_eSE_EvtType_t evt_type, phLibNfc_Handle handle,
phLibNfc_uSeEvtInfo_t *evt_info, NFCSTATUS status);
static bool performDownload(struct nfc_jni_native_data *nat, bool takeLock);
extern void set_target_activationBytes(JNIEnv *e, jobject tag,
phLibNfc_sRemoteDevInformation_t *psRemoteDevInfo);
extern void set_target_pollBytes(JNIEnv *e, jobject tag,
phLibNfc_sRemoteDevInformation_t *psRemoteDevInfo);
/*
* Deferred callback called when client thread must be exited
*/
static void client_kill_deferred_call(void* arg)
{
struct nfc_jni_native_data *nat = (struct nfc_jni_native_data *)arg;
nat->running = FALSE;
}
static void kill_client(nfc_jni_native_data *nat)
{
phDal4Nfc_Message_Wrapper_t wrapper;
phLibNfc_DeferredCall_t *pMsg;
usleep(50000);
ALOGD("Terminating client thread...");
pMsg = (phLibNfc_DeferredCall_t*)malloc(sizeof(phLibNfc_DeferredCall_t));
pMsg->pCallback = client_kill_deferred_call;
pMsg->pParameter = (void*)nat;
wrapper.msg.eMsgType = PH_LIBNFC_DEFERREDCALL_MSG;
wrapper.msg.pMsgData = pMsg;
wrapper.msg.Size = sizeof(phLibNfc_DeferredCall_t);
phDal4Nfc_msgsnd(gDrvCfg.nClientId, (struct msgbuf *)&wrapper, sizeof(phLibNfc_Message_t), 0);
}
static void nfc_jni_ioctl_callback(void *pContext, phNfc_sData_t* /*pOutput*/, NFCSTATUS status) {
struct nfc_jni_callback_data * pCallbackData = (struct nfc_jni_callback_data *) pContext;
LOG_CALLBACK("nfc_jni_ioctl_callback", status);
/* Report the callback status and wake up the caller */
pCallbackData->status = status;
sem_post(&pCallbackData->sem);
}
static void nfc_jni_deinit_download_callback(void *pContext, NFCSTATUS status)
{
struct nfc_jni_callback_data * pCallbackData = (struct nfc_jni_callback_data *) pContext;
LOG_CALLBACK("nfc_jni_deinit_download_callback", status);
/* Report the callback status and wake up the caller */
pCallbackData->status = status;
sem_post(&pCallbackData->sem);
}
static int nfc_jni_download_locked(struct nfc_jni_native_data *nat, uint8_t update)
{
uint8_t OutputBuffer[1];
uint8_t InputBuffer[1];
struct timespec ts;
NFCSTATUS status = NFCSTATUS_FAILED;
phLibNfc_StackCapabilities_t caps;
struct nfc_jni_callback_data cb_data;
bool result;
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, NULL))
{
goto clean_and_return;
}
if(update)
{
//deinit
TRACE("phLibNfc_Mgt_DeInitialize() (download)");
REENTRANCE_LOCK();
status = phLibNfc_Mgt_DeInitialize(gHWRef, nfc_jni_deinit_download_callback, (void *)&cb_data);
REENTRANCE_UNLOCK();
if (status != NFCSTATUS_PENDING)
{
ALOGE("phLibNfc_Mgt_DeInitialize() (download) returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
}
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 5;
/* Wait for callback response */
if(sem_timedwait(&cb_data.sem, &ts))
{
ALOGW("Deinitialization timed out (download)");
}
if(cb_data.status != NFCSTATUS_SUCCESS)
{
ALOGW("Deinitialization FAILED (download)");
}
TRACE("Deinitialization SUCCESS (download)");
}
result = performDownload(nat, false);
if (!result) {
status = NFCSTATUS_FAILED;
goto clean_and_return;
}
TRACE("phLibNfc_Mgt_Initialize()");
REENTRANCE_LOCK();
status = phLibNfc_Mgt_Initialize(gHWRef, nfc_jni_init_callback, (void *)&cb_data);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_PENDING)
{
ALOGE("phLibNfc_Mgt_Initialize() (download) returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
goto clean_and_return;
}
TRACE("phLibNfc_Mgt_Initialize() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
if(sem_wait(&cb_data.sem))
{
ALOGE("Failed to wait for semaphore (errno=0x%08x)", errno);
status = NFCSTATUS_FAILED;
goto clean_and_return;
}
/* Initialization Status */
if(cb_data.status != NFCSTATUS_SUCCESS)
{
status = cb_data.status;
goto clean_and_return;
}
/* ====== CAPABILITIES ======= */
REENTRANCE_LOCK();
status = phLibNfc_Mgt_GetstackCapabilities(&caps, (void*)nat);
REENTRANCE_UNLOCK();
if (status != NFCSTATUS_SUCCESS)
{
ALOGW("phLibNfc_Mgt_GetstackCapabilities returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
}
else
{
ALOGD("NFC capabilities: HAL = %x, FW = %x, HW = %x, Model = %x, HCI = %x, Full_FW = %d, Rev = %d, FW Update Info = %d",
caps.psDevCapabilities.hal_version,
caps.psDevCapabilities.fw_version,
caps.psDevCapabilities.hw_version,
caps.psDevCapabilities.model_id,
caps.psDevCapabilities.hci_version,
caps.psDevCapabilities.full_version[NXP_FULL_VERSION_LEN-1],
caps.psDevCapabilities.full_version[NXP_FULL_VERSION_LEN-2],
caps.psDevCapabilities.firmware_update_info);
}
/*Download is successful*/
status = NFCSTATUS_SUCCESS;
clean_and_return:
nfc_cb_data_deinit(&cb_data);
return status;
}
static int nfc_jni_configure_driver(struct nfc_jni_native_data *nat)
{
char value[PROPERTY_VALUE_MAX];
int result = FALSE;
NFCSTATUS status;
/* ====== CONFIGURE DRIVER ======= */
/* Configure hardware link */
gDrvCfg.nClientId = phDal4Nfc_msgget(0, 0600);
TRACE("phLibNfc_Mgt_ConfigureDriver(0x%08x)", gDrvCfg.nClientId);
REENTRANCE_LOCK();
status = phLibNfc_Mgt_ConfigureDriver(&gDrvCfg, &gHWRef);
REENTRANCE_UNLOCK();
if(status == NFCSTATUS_ALREADY_INITIALISED) {
ALOGW("phLibNfc_Mgt_ConfigureDriver() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
}
else if(status != NFCSTATUS_SUCCESS)
{
ALOGE("phLibNfc_Mgt_ConfigureDriver() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
goto clean_and_return;
}
TRACE("phLibNfc_Mgt_ConfigureDriver() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
if(pthread_create(&(nat->thread), NULL, nfc_jni_client_thread, nat) != 0)
{
ALOGE("pthread_create failed");
goto clean_and_return;
}
driverConfigured = TRUE;
clean_and_return:
return result;
}
static int nfc_jni_unconfigure_driver(struct nfc_jni_native_data* /*nat*/)
{
int result = FALSE;
NFCSTATUS status;
/* Unconfigure driver */
TRACE("phLibNfc_Mgt_UnConfigureDriver()");
REENTRANCE_LOCK();
status = phLibNfc_Mgt_UnConfigureDriver(gHWRef);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_SUCCESS)
{
ALOGE("phLibNfc_Mgt_UnConfigureDriver() returned error 0x%04x[%s] -- this should never happen", status, nfc_jni_get_status_name( status));
}
else
{
ALOGD("phLibNfc_Mgt_UnConfigureDriver() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
result = TRUE;
}
driverConfigured = FALSE;
return result;
}
/* Initialization function */
static int nfc_jni_initialize(struct nfc_jni_native_data *nat) {
struct timespec ts;
uint8_t resp[16];
NFCSTATUS status;
phLibNfc_StackCapabilities_t caps;
phLibNfc_SE_List_t SE_List[PHLIBNFC_MAXNO_OF_SE];
uint8_t i, No_SE = PHLIBNFC_MAXNO_OF_SE, SmartMX_index = 0, SmartMX_detected = 0;
phLibNfc_Llcp_sLinkParameters_t LlcpConfigInfo;
struct nfc_jni_callback_data cb_data;
uint8_t firmware_status;
uint8_t update = TRUE;
int result = JNI_FALSE;
const hw_module_t* hw_module;
nfc_pn544_device_t* pn544_dev = NULL;
int ret = 0;
ALOGD("Start Initialization\n");
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, NULL))
{
goto clean_and_return;
}
/* Get EEPROM values and device port from product-specific settings */
ret = hw_get_module(NFC_HARDWARE_MODULE_ID, &hw_module);
if (ret) {
ALOGE("hw_get_module() failed.");
goto clean_and_return;
}
ret = nfc_pn544_open(hw_module, &pn544_dev);
if (ret) {
ALOGE("Could not open pn544 hw_module.");
goto clean_and_return;
}
if (pn544_dev->num_eeprom_settings == 0 || pn544_dev->eeprom_settings == NULL) {
ALOGE("Could not load EEPROM settings");
goto clean_and_return;
}
/* Reset device connected handle */
device_connected_flag = 0;
/* Reset stored handle */
storedHandle = 0;
/* Initialize Driver */
if(!driverConfigured)
{
nfc_jni_configure_driver(nat);
}
/* ====== INITIALIZE ======= */
TRACE("phLibNfc_Mgt_Initialize()");
REENTRANCE_LOCK();
status = phLibNfc_Mgt_Initialize(gHWRef, nfc_jni_init_callback, (void *)&cb_data);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_PENDING)
{
ALOGE("phLibNfc_Mgt_Initialize() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
update = FALSE;
goto force_download;
}
TRACE("phLibNfc_Mgt_Initialize returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
/* Wait for callback response */
if(sem_wait(&cb_data.sem))
{
ALOGE("Failed to wait for semaphore (errno=0x%08x)", errno);
goto clean_and_return;
}
/* Initialization Status */
if(cb_data.status != NFCSTATUS_SUCCESS)
{
update = FALSE;
goto force_download;
}
/* ====== CAPABILITIES ======= */
REENTRANCE_LOCK();
status = phLibNfc_Mgt_GetstackCapabilities(&caps, (void*)nat);
REENTRANCE_UNLOCK();
if (status != NFCSTATUS_SUCCESS)
{
ALOGW("phLibNfc_Mgt_GetstackCapabilities returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
}
else
{
ALOGD("NFC capabilities: HAL = %x, FW = %x, HW = %x, Model = %x, HCI = %x, Full_FW = %d, Rev = %d, FW Update Info = %d",
caps.psDevCapabilities.hal_version,
caps.psDevCapabilities.fw_version,
caps.psDevCapabilities.hw_version,
caps.psDevCapabilities.model_id,
caps.psDevCapabilities.hci_version,
caps.psDevCapabilities.full_version[NXP_FULL_VERSION_LEN-1],
caps.psDevCapabilities.full_version[NXP_FULL_VERSION_LEN-2],
caps.psDevCapabilities.firmware_update_info);
}
/* ====== FIRMWARE VERSION ======= */
if(caps.psDevCapabilities.firmware_update_info)
{
force_download:
for (i=0; i<3; i++)
{
TRACE("Firmware version not UpToDate");
status = nfc_jni_download_locked(nat, update);
if(status == NFCSTATUS_SUCCESS)
{
ALOGI("Firmware update SUCCESS");
break;
}
ALOGW("Firmware update FAILED");
update = FALSE;
}
if(i>=3)
{
ALOGE("Unable to update firmware, giving up");
goto clean_and_return;
}
}
else
{
TRACE("Firmware version UpToDate");
}
/* ====== EEPROM SETTINGS ======= */
// Update EEPROM settings
TRACE("****** START EEPROM SETTINGS UPDATE ******");
for (i = 0; i < pn544_dev->num_eeprom_settings; i++)
{
char eeprom_property[PROPERTY_KEY_MAX];
char eeprom_value[PROPERTY_VALUE_MAX];
uint8_t* eeprom_base = &(pn544_dev->eeprom_settings[i*4]);
TRACE("> EEPROM SETTING: %d", i);
// Check for override of this EEPROM value in properties
snprintf(eeprom_property, sizeof(eeprom_property), "debug.nfc.eeprom.%02X%02X",
eeprom_base[1], eeprom_base[2]);
TRACE(">> Checking property: %s", eeprom_property);
if (property_get(eeprom_property, eeprom_value, "") == 2) {
int eeprom_value_num = (int)strtol(eeprom_value, (char**)NULL, 16);
ALOGD(">> Override EEPROM addr 0x%02X%02X with value %02X",
eeprom_base[1], eeprom_base[2], eeprom_value_num);
eeprom_base[3] = eeprom_value_num;
}
TRACE(">> Addr: 0x%02X%02X set to: 0x%02X", eeprom_base[1], eeprom_base[2],
eeprom_base[3]);
gInputParam.buffer = eeprom_base;
gInputParam.length = 0x04;
gOutputParam.buffer = resp;
REENTRANCE_LOCK();
status = phLibNfc_Mgt_IoCtl(gHWRef, NFC_MEM_WRITE, &gInputParam, &gOutputParam, nfc_jni_ioctl_callback, (void *)&cb_data);
REENTRANCE_UNLOCK();
if (status != NFCSTATUS_PENDING) {
ALOGE("phLibNfc_Mgt_IoCtl() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
goto clean_and_return;
}
/* Wait for callback response */
if(sem_wait(&cb_data.sem))
{
ALOGE("Failed to wait for semaphore (errno=0x%08x)", errno);
goto clean_and_return;
}
/* Initialization Status */
if (cb_data.status != NFCSTATUS_SUCCESS)
{
goto clean_and_return;
}
}
TRACE("****** ALL EEPROM SETTINGS UPDATED ******");
/* ====== SECURE ELEMENTS ======= */
REENTRANCE_LOCK();
ALOGD("phLibNfc_SE_GetSecureElementList()");
status = phLibNfc_SE_GetSecureElementList(SE_List, &No_SE);
REENTRANCE_UNLOCK();
if (status != NFCSTATUS_SUCCESS)
{
ALOGD("phLibNfc_SE_GetSecureElementList(): Error");
goto clean_and_return;
}
ALOGD("\n> Number of Secure Element(s) : %d\n", No_SE);
/* Display Secure Element information */
for (i = 0; i < No_SE; i++)
{
if (SE_List[i].eSE_Type == phLibNfc_SE_Type_SmartMX) {
ALOGD("phLibNfc_SE_GetSecureElementList(): SMX detected, handle=%p", (void*)SE_List[i].hSecureElement);
} else if (SE_List[i].eSE_Type == phLibNfc_SE_Type_UICC) {
ALOGD("phLibNfc_SE_GetSecureElementList(): UICC detected, handle=%p", (void*)SE_List[i].hSecureElement);
}
/* Set SE mode - Off */
REENTRANCE_LOCK();
status = phLibNfc_SE_SetMode(SE_List[i].hSecureElement,
phLibNfc_SE_ActModeOff, nfc_jni_se_set_mode_callback,
(void *)&cb_data);
REENTRANCE_UNLOCK();
if (status != NFCSTATUS_PENDING)
{
ALOGE("phLibNfc_SE_SetMode() returned 0x%04x[%s]", status,
nfc_jni_get_status_name(status));
goto clean_and_return;
}
ALOGD("phLibNfc_SE_SetMode() returned 0x%04x[%s]", status,
nfc_jni_get_status_name(status));
/* Wait for callback response */
if(sem_wait(&cb_data.sem))
{
ALOGE("Failed to wait for semaphore (errno=0x%08x)", errno);
goto clean_and_return;
}
}
/* ====== LLCP ======= */
/* LLCP Params */
TRACE("****** NFC Config Mode NFCIP1 - LLCP ******");
LlcpConfigInfo.miu = nat->miu;
LlcpConfigInfo.lto = nat->lto;
LlcpConfigInfo.wks = nat->wks;
LlcpConfigInfo.option = nat->opt;
REENTRANCE_LOCK();
status = phLibNfc_Mgt_SetLlcp_ConfigParams(&LlcpConfigInfo,
nfc_jni_llcpcfg_callback,
(void *)&cb_data);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_PENDING)
{
ALOGE("phLibNfc_Mgt_SetLlcp_ConfigParams returned 0x%04x[%s]", status,
nfc_jni_get_status_name(status));
goto clean_and_return;
}
TRACE("phLibNfc_Mgt_SetLlcp_ConfigParams returned 0x%04x[%s]", status,
nfc_jni_get_status_name(status));
/* Wait for callback response */
if(sem_wait(&cb_data.sem))
{
ALOGE("Failed to wait for semaphore (errno=0x%08x)", errno);
goto clean_and_return;
}
/* ===== DISCOVERY ==== */
nat->discovery_cfg.NfcIP_Mode = nat->p2p_initiator_modes; //initiator
nat->discovery_cfg.NfcIP_Target_Mode = nat->p2p_target_modes; //target
nat->discovery_cfg.Duration = 300000; /* in ms */
nat->discovery_cfg.NfcIP_Tgt_Disable = FALSE;
/* Register for the card emulation mode */
REENTRANCE_LOCK();
ret = phLibNfc_SE_NtfRegister(nfc_jni_transaction_callback,(void *)nat);
REENTRANCE_UNLOCK();
if(ret != NFCSTATUS_SUCCESS)
{
ALOGD("phLibNfc_SE_NtfRegister returned 0x%02x",ret);
goto clean_and_return;
}
TRACE("phLibNfc_SE_NtfRegister returned 0x%x\n", ret);
/* ====== END ======= */
ALOGI("NFC Initialized");
result = TRUE;
clean_and_return:
if (result != TRUE)
{
if(nat)
{
kill_client(nat);
}
}
if (pn544_dev != NULL) {
nfc_pn544_close(pn544_dev);
}
nfc_cb_data_deinit(&cb_data);
return result;
}
static int is_user_build() {
char value[PROPERTY_VALUE_MAX];
property_get("ro.build.type", value, "");
return !strncmp("user", value, PROPERTY_VALUE_MAX);
}
/*
* Last-chance fallback when there is no clean way to recover
* Performs a software reset
*/
void emergency_recovery(struct nfc_jni_native_data* /*nat*/) {
if (is_user_build()) {
ALOGE("emergency_recovery: force restart of NFC service");
} else {
// dont recover immediately, so we can debug
unsigned int t;
for (t=1; t < 1000000; t <<= 1) {
ALOGE("emergency_recovery: NFC stack dead-locked");
sleep(t);
}
}
phLibNfc_Mgt_Recovery();
abort(); // force a noisy crash
}
void nfc_jni_reset_timeout_values()
{
REENTRANCE_LOCK();
phLibNfc_SetIsoXchgTimeout(NXP_ISO_XCHG_TIMEOUT);
phLibNfc_SetHciTimeout(NXP_NFC_HCI_TIMEOUT);
phLibNfc_SetFelicaTimeout(NXP_FELICA_XCHG_TIMEOUT);
phLibNfc_SetMifareRawTimeout(NXP_MIFARE_XCHG_TIMEOUT);
REENTRANCE_UNLOCK();
}
/*
* Restart the polling loop when unable to perform disconnect
*/
void nfc_jni_restart_discovery_locked(struct nfc_jni_native_data *nat)
{
nfc_jni_start_discovery_locked(nat, true);
}
/*
* Utility to recover UID from target infos
*/
static phNfc_sData_t get_target_uid(phLibNfc_sRemoteDevInformation_t *psRemoteDevInfo)
{
phNfc_sData_t uid;
switch(psRemoteDevInfo->RemDevType)
{
case phNfc_eISO14443_A_PICC:
case phNfc_eISO14443_4A_PICC:
case phNfc_eISO14443_3A_PICC:
case phNfc_eMifare_PICC:
uid.buffer = psRemoteDevInfo->RemoteDevInfo.Iso14443A_Info.Uid;
uid.length = psRemoteDevInfo->RemoteDevInfo.Iso14443A_Info.UidLength;
break;
case phNfc_eISO14443_B_PICC:
case phNfc_eISO14443_4B_PICC:
uid.buffer = psRemoteDevInfo->RemoteDevInfo.Iso14443B_Info.AtqB.AtqResInfo.Pupi;
uid.length = sizeof(psRemoteDevInfo->RemoteDevInfo.Iso14443B_Info.AtqB.AtqResInfo.Pupi);
break;
case phNfc_eFelica_PICC:
uid.buffer = psRemoteDevInfo->RemoteDevInfo.Felica_Info.IDm;
uid.length = psRemoteDevInfo->RemoteDevInfo.Felica_Info.IDmLength;
break;
case phNfc_eJewel_PICC:
uid.buffer = psRemoteDevInfo->RemoteDevInfo.Jewel_Info.Uid;
uid.length = psRemoteDevInfo->RemoteDevInfo.Jewel_Info.UidLength;
break;
case phNfc_eISO15693_PICC:
uid.buffer = psRemoteDevInfo->RemoteDevInfo.Iso15693_Info.Uid;
uid.length = psRemoteDevInfo->RemoteDevInfo.Iso15693_Info.UidLength;
break;
case phNfc_eNfcIP1_Target:
case phNfc_eNfcIP1_Initiator:
uid.buffer = psRemoteDevInfo->RemoteDevInfo.NfcIP_Info.NFCID;
uid.length = psRemoteDevInfo->RemoteDevInfo.NfcIP_Info.NFCID_Length;
break;
default:
uid.buffer = NULL;
uid.length = 0;
break;
}
return uid;
}
/*
* NFC stack message processing
*/
static void *nfc_jni_client_thread(void *arg)
{
struct nfc_jni_native_data *nat;
JNIEnv *e;
JavaVMAttachArgs thread_args;
phDal4Nfc_Message_Wrapper_t wrapper;
nat = (struct nfc_jni_native_data *)arg;
thread_args.name = "NFC Message Loop";
thread_args.version = nat->env_version;
thread_args.group = NULL;
nat->vm->AttachCurrentThread(&e, &thread_args);
pthread_setname_np(pthread_self(), "message");
TRACE("NFC client started");
nat->running = TRUE;
while(nat->running == TRUE)
{
/* Fetch next message from the NFC stack message queue */
if(phDal4Nfc_msgrcv(gDrvCfg.nClientId, (void *)&wrapper,
sizeof(phLibNfc_Message_t), 0, 0) == -1)
{
ALOGE("NFC client received bad message");
continue;
}
switch(wrapper.msg.eMsgType)
{
case PH_LIBNFC_DEFERREDCALL_MSG:
{
phLibNfc_DeferredCall_t *msg =
(phLibNfc_DeferredCall_t *)(wrapper.msg.pMsgData);
REENTRANCE_LOCK();
msg->pCallback(msg->pParameter);
REENTRANCE_UNLOCK();
break;
}
}
}
TRACE("NFC client stopped");
nat->vm->DetachCurrentThread();
return NULL;
}
extern uint8_t nfc_jni_is_ndef;
extern uint8_t *nfc_jni_ndef_buf;
extern uint32_t nfc_jni_ndef_buf_len;
static phLibNfc_sNfcIPCfg_t nfc_jni_nfcip1_cfg =
{
3,
{ 0x46, 0x66, 0x6D }
};
/*
* Callbacks
*/
/* P2P - LLCP callbacks */
static void nfc_jni_llcp_linkStatus_callback(void *pContext,
phFriNfc_LlcpMac_eLinkStatus_t eLinkStatus)
{
phFriNfc_Llcp_sLinkParameters_t sLinkParams;
JNIEnv *e;
NFCSTATUS status;
struct nfc_jni_callback_data * pContextData = (struct nfc_jni_callback_data*)pContext;
struct nfc_jni_native_data *nat = (nfc_jni_native_data *)pContextData->pContext;
nfc_jni_listen_data_t * pListenData = NULL;
nfc_jni_native_monitor * pMonitor = nfc_jni_get_monitor();
TRACE("Callback: nfc_jni_llcp_linkStatus_callback()");
nat->vm->GetEnv( (void **)&e, nat->env_version);
/* Update link status */
g_eLinkStatus = eLinkStatus;
if(eLinkStatus == phFriNfc_LlcpMac_eLinkActivated)
{
REENTRANCE_LOCK();
status = phLibNfc_Llcp_GetRemoteInfo(hLlcpHandle, &sLinkParams);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_SUCCESS)
{
ALOGW("GetRemote Info failded - Status = %02x",status);
}
else
{
ALOGI("LLCP Link activated (LTO=%d, MIU=%d, OPTION=0x%02x, WKS=0x%02x)",sLinkParams.lto,
sLinkParams.miu,
sLinkParams.option,
sLinkParams.wks);
device_connected_flag = 1;
}
}
else if(eLinkStatus == phFriNfc_LlcpMac_eLinkDeactivated)
{
ALOGI("LLCP Link deactivated");
free(pContextData);
/* Reset device connected flag */
device_connected_flag = 0;
/* Reset incoming socket list */
while (!LIST_EMPTY(&pMonitor->incoming_socket_head))
{
pListenData = LIST_FIRST(&pMonitor->incoming_socket_head);
LIST_REMOVE(pListenData, entries);
free(pListenData);
}
/* Notify manager that the LLCP is lost or deactivated */
e->CallVoidMethod(nat->manager, cached_NfcManager_notifyLlcpLinkDeactivated, nat->tag);
if(e->ExceptionCheck())
{
ALOGE("Exception occured");
kill_client(nat);
}
}
}
static void nfc_jni_checkLlcp_callback(void *context,
NFCSTATUS status)
{
struct nfc_jni_callback_data * pContextData = (struct nfc_jni_callback_data*)context;
LOG_CALLBACK("nfc_jni_checkLlcp_callback", status);
pContextData->status = status;
sem_post(&pContextData->sem);
}
static void nfc_jni_llcpcfg_callback(void *pContext, NFCSTATUS status)
{
struct nfc_jni_callback_data * pCallbackData = (struct nfc_jni_callback_data *) pContext;
LOG_CALLBACK("nfc_jni_llcpcfg_callback", status);
/* Report the callback status and wake up the caller */
pCallbackData->status = status;
sem_post(&pCallbackData->sem);
}
static void nfc_jni_llcp_transport_listen_socket_callback(void *pContext,
phLibNfc_Handle hIncomingSocket)
{
phLibNfc_Handle hServiceSocket = (phLibNfc_Handle)pContext;
nfc_jni_listen_data_t * pListenData = NULL;
nfc_jni_native_monitor * pMonitor = nfc_jni_get_monitor();
TRACE("nfc_jni_llcp_transport_listen_socket_callback socket handle = %p", (void*)hIncomingSocket);
pthread_mutex_lock(&pMonitor->incoming_socket_mutex);
/* Store the connection request */
pListenData = (nfc_jni_listen_data_t*)malloc(sizeof(nfc_jni_listen_data_t));
if (pListenData == NULL)
{
ALOGE("Failed to create structure to handle incoming LLCP connection request");
goto clean_and_return;
}
pListenData->pServerSocket = hServiceSocket;
pListenData->pIncomingSocket = hIncomingSocket;
LIST_INSERT_HEAD(&pMonitor->incoming_socket_head, pListenData, entries);
/* Signal pending accept operations that the list is updated */
pthread_cond_broadcast(&pMonitor->incoming_socket_cond);
clean_and_return:
pthread_mutex_unlock(&pMonitor->incoming_socket_mutex);
}
void nfc_jni_llcp_transport_socket_err_callback(void* pContext,
uint8_t nErrCode)
{
PHNFC_UNUSED_VARIABLE(pContext);
TRACE("Callback: nfc_jni_llcp_transport_socket_err_callback()");
if(nErrCode == PHFRINFC_LLCP_ERR_FRAME_REJECTED)
{
ALOGW("Frame Rejected - Disconnected");
}
else if(nErrCode == PHFRINFC_LLCP_ERR_DISCONNECTED)
{
ALOGD("Socket Disconnected");
}
}
static void nfc_jni_discover_callback(void *pContext, NFCSTATUS status)
{
struct nfc_jni_callback_data * pContextData = (struct nfc_jni_callback_data*)pContext;
LOG_CALLBACK("nfc_jni_discover_callback", status);
pContextData->status = status;
sem_post(&pContextData->sem);
}
static uint8_t find_preferred_target(phLibNfc_RemoteDevList_t *psRemoteDevList,
uint8_t uNoOfRemoteDev)
{
// Always prefer p2p targets over other targets. Otherwise, select the first target
// reported.
uint8_t preferred_index = 0;
for (uint8_t i = 0; i < uNoOfRemoteDev; i++) {
if((psRemoteDevList[i].psRemoteDevInfo->RemDevType == phNfc_eNfcIP1_Initiator)
|| (psRemoteDevList[i].psRemoteDevInfo->RemDevType == phNfc_eNfcIP1_Target)) {
preferred_index = i;
}
}
return preferred_index;
}
static void nfc_jni_Discovery_notification_callback(void *pContext,
phLibNfc_RemoteDevList_t *psRemoteDevList,
uint8_t uNofRemoteDev, NFCSTATUS status)
{
NFCSTATUS ret;
const char * typeName;
struct timespec ts;
phNfc_sData_t data;
int i;
int target_index = 0; // Target that will be reported (if multiple can be >0)
struct nfc_jni_native_data* nat = (struct nfc_jni_native_data *)pContext;
JNIEnv *e;
nat->vm->GetEnv( (void **)&e, nat->env_version);
if(status == NFCSTATUS_DESELECTED)
{
LOG_CALLBACK("nfc_jni_Discovery_notification_callback: Target deselected", status);
/* Notify manager that a target was deselected */
e->CallVoidMethod(nat->manager, cached_NfcManager_notifyTargetDeselected);
if(e->ExceptionCheck())
{
ALOGE("Exception occurred");
kill_client(nat);
}
}
else
{
LOG_CALLBACK("nfc_jni_Discovery_notification_callback", status);
TRACE("Discovered %d tags", uNofRemoteDev);
target_index = find_preferred_target(psRemoteDevList, uNofRemoteDev);
ScopedLocalRef<jobject> tag(e, NULL);
/* Reset device connected flag */
device_connected_flag = 1;
phLibNfc_sRemoteDevInformation_t *remDevInfo = psRemoteDevList[target_index].psRemoteDevInfo;
phLibNfc_Handle remDevHandle = psRemoteDevList[target_index].hTargetDev;
if((remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator)
|| (remDevInfo->RemDevType == phNfc_eNfcIP1_Target))
{
ScopedLocalRef<jclass> tag_cls(e, e->GetObjectClass(nat->cached_P2pDevice));
if(e->ExceptionCheck())
{
ALOGE("Get Object Class Error");
kill_client(nat);
return;
}
/* New target instance */
jmethodID ctor = e->GetMethodID(tag_cls.get(), "<init>", "()V");
tag.reset(e->NewObject(tag_cls.get(), ctor));
/* Set P2P Target mode */
jfieldID f = e->GetFieldID(tag_cls.get(), "mMode", "I");
if(remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator)
{
ALOGD("Discovered P2P Initiator");
e->SetIntField(tag.get(), f, (jint)MODE_P2P_INITIATOR);
}
else
{
ALOGD("Discovered P2P Target");
e->SetIntField(tag.get(), f, (jint)MODE_P2P_TARGET);
}
if(remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator)
{
/* Set General Bytes */
f = e->GetFieldID(tag_cls.get(), "mGeneralBytes", "[B");
TRACE("General Bytes length =");
for(i=0;i<remDevInfo->RemoteDevInfo.NfcIP_Info.ATRInfo_Length;i++)
{
ALOGD("%02x ", remDevInfo->RemoteDevInfo.NfcIP_Info.ATRInfo[i]);
}
ScopedLocalRef<jbyteArray> generalBytes(e, e->NewByteArray(remDevInfo->RemoteDevInfo.NfcIP_Info.ATRInfo_Length));
e->SetByteArrayRegion(generalBytes.get(), 0,
remDevInfo->RemoteDevInfo.NfcIP_Info.ATRInfo_Length,
(jbyte *)remDevInfo->RemoteDevInfo.NfcIP_Info.ATRInfo);
e->SetObjectField(tag.get(), f, generalBytes.get());
}
/* Set tag handle */
f = e->GetFieldID(tag_cls.get(), "mHandle", "I");
e->SetIntField(tag.get(), f,(jint)remDevHandle);
TRACE("Target handle = 0x%08x",remDevHandle);
}
else
{
ScopedLocalRef<jclass> tag_cls(e, e->GetObjectClass(nat->cached_NfcTag));
if(e->ExceptionCheck())
{
kill_client(nat);
return;
}
/* New tag instance */
jmethodID ctor = e->GetMethodID(tag_cls.get(), "<init>", "()V");
tag.reset(e->NewObject(tag_cls.get(), ctor));
bool multi_protocol = false;
if(status == NFCSTATUS_MULTIPLE_PROTOCOLS)
{
TRACE("Multiple Protocol TAG detected\n");
multi_protocol = true;
}
/* Set tag UID */
jfieldID f = e->GetFieldID(tag_cls.get(), "mUid", "[B");
data = get_target_uid(remDevInfo);
ScopedLocalRef<jbyteArray> tagUid(e, e->NewByteArray(data.length));
if(data.length > 0)
{
e->SetByteArrayRegion(tagUid.get(), 0, data.length, (jbyte *)data.buffer);
}
e->SetObjectField(tag.get(), f, tagUid.get());
/* Generate technology list */
ScopedLocalRef<jintArray> techList(e, NULL);
ScopedLocalRef<jintArray> handleList(e, NULL);
ScopedLocalRef<jintArray> typeList(e, NULL);
nfc_jni_get_technology_tree(e, psRemoteDevList,
multi_protocol ? uNofRemoteDev : 1,
&techList, &handleList, &typeList);
/* Push the technology list into the java object */
f = e->GetFieldID(tag_cls.get(), "mTechList", "[I");
e->SetObjectField(tag.get(), f, techList.get());
f = e->GetFieldID(tag_cls.get(), "mTechHandles", "[I");
e->SetObjectField(tag.get(), f, handleList.get());
f = e->GetFieldID(tag_cls.get(), "mTechLibNfcTypes", "[I");
e->SetObjectField(tag.get(), f, typeList.get());
f = e->GetFieldID(tag_cls.get(), "mConnectedTechIndex", "I");
e->SetIntField(tag.get(), f,(jint)-1);
f = e->GetFieldID(tag_cls.get(), "mConnectedHandle", "I");
e->SetIntField(tag.get(), f,(jint)-1);
set_target_pollBytes(e, tag.get(), psRemoteDevList->psRemoteDevInfo);
set_target_activationBytes(e, tag.get(), psRemoteDevList->psRemoteDevInfo);
}
storedHandle = remDevHandle;
if (nat->tag != NULL) {
e->DeleteGlobalRef(nat->tag);
}
nat->tag = e->NewGlobalRef(tag.get());
/* Notify the service */
TRACE("Notify Nfc Service");
if((remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator)
|| (remDevInfo->RemDevType == phNfc_eNfcIP1_Target))
{
/* Store the handle of the P2P device */
hLlcpHandle = remDevHandle;
/* Notify manager that new a P2P device was found */
e->CallVoidMethod(nat->manager, cached_NfcManager_notifyLlcpLinkActivation, tag.get());
if(e->ExceptionCheck())
{
ALOGE("Exception occurred");
kill_client(nat);
}
}
else
{
/* Notify manager that new a tag was found */
e->CallVoidMethod(nat->manager, cached_NfcManager_notifyNdefMessageListeners, tag.get());
if(e->ExceptionCheck())
{
ALOGE("Exception occurred");
kill_client(nat);
}
}
}
}
static void nfc_jni_init_callback(void *pContext, NFCSTATUS status)
{
struct nfc_jni_callback_data * pContextData = (struct nfc_jni_callback_data*)pContext;
LOG_CALLBACK("nfc_jni_init_callback", status);
pContextData->status = status;
sem_post(&pContextData->sem);
}
static void nfc_jni_deinit_callback(void *pContext, NFCSTATUS status)
{
struct nfc_jni_callback_data * pContextData = (struct nfc_jni_callback_data*)pContext;
LOG_CALLBACK("nfc_jni_deinit_callback", status);
pContextData->status = status;
sem_post(&pContextData->sem);
}
/* Set Secure Element mode callback*/
static void nfc_jni_smartMX_setModeCb (void* pContext,
phLibNfc_Handle /*hSecureElement*/,
NFCSTATUS status)
{
struct nfc_jni_callback_data * pContextData = (struct nfc_jni_callback_data*)pContext;
LOG_CALLBACK("nfc_jni_smartMX_setModeCb", status);
pContextData->status = status;
sem_post(&pContextData->sem);
}
/* Card Emulation callback */
static void nfc_jni_transaction_callback(void *context,
phLibNfc_eSE_EvtType_t evt_type, phLibNfc_Handle /*handle*/,
phLibNfc_uSeEvtInfo_t *evt_info, NFCSTATUS status)
{
JNIEnv *e;
jobject tmp_array = NULL;
jobject mifare_block = NULL;
struct nfc_jni_native_data *nat;
phNfc_sData_t *aid;
phNfc_sData_t *mifare_command;
struct nfc_jni_callback_data *pCallbackData;
int i=0;
LOG_CALLBACK("nfc_jni_transaction_callback", status);
nat = (struct nfc_jni_native_data *)context;
nat->vm->GetEnv( (void **)&e, nat->env_version);
if(status == NFCSTATUS_SUCCESS)
{
switch(evt_type)
{
case phLibNfc_eSE_EvtStartTransaction:
{
TRACE("> SE EVT_START_TRANSACTION");
}break;
case phLibNfc_eSE_EvtApduReceived:
{
TRACE("> SE EVT_APDU_RECEIVED");
}break;
case phLibNfc_eSE_EvtCardRemoval:
{
TRACE("> SE EVT_EMV_CARD_REMOVAL");
}break;
case phLibNfc_eSE_EvtMifareAccess:
{
TRACE("> SE EVT_MIFARE_ACCESS");
}break;
case phLibNfc_eSE_EvtFieldOn:
{
TRACE("> SE EVT_FIELD_ON");
e->CallVoidMethod(nat->manager, cached_NfcManager_notifyRfFieldActivated);
}break;
case phLibNfc_eSE_EvtFieldOff:
{
TRACE("> SE EVT_FIELD_OFF");
e->CallVoidMethod(nat->manager, cached_NfcManager_notifyRfFieldDeactivated);
}break;
default:
{
TRACE("Unknown SE event");
}break;
}
}
else
{
ALOGE("SE transaction notification error");
goto error;
}
/* Function finished, now clean and return */
goto clean_and_return;
error:
/* In case of error, just discard the notification */
ALOGE("Failed to send SE transaction notification");
e->ExceptionClear();
clean_and_return:
if(tmp_array != NULL)
{
e->DeleteLocalRef(tmp_array);
}
}
static void nfc_jni_se_set_mode_callback(void *pContext,
phLibNfc_Handle /*handle*/, NFCSTATUS status)
{
struct nfc_jni_callback_data * pContextData = (struct nfc_jni_callback_data*)pContext;
LOG_CALLBACK("nfc_jni_se_set_mode_callback", status);
pContextData->status = status;
sem_post(&pContextData->sem);
}
/*
* NFCManager methods
*/
static void nfc_jni_start_discovery_locked(struct nfc_jni_native_data *nat, bool resume)
{
NFCSTATUS ret;
struct nfc_jni_callback_data cb_data;
int numRetries = 3;
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, NULL))
{
goto clean_and_return;
}
/* Reset the PN544 ISO XCHG / sw watchdog timeouts */
nfc_jni_reset_timeout_values();
/* Reload the p2p modes */
nat->discovery_cfg.NfcIP_Mode = nat->p2p_initiator_modes; //initiator
nat->discovery_cfg.NfcIP_Target_Mode = nat->p2p_target_modes; //target
nat->discovery_cfg.NfcIP_Tgt_Disable = FALSE;
/* Reset device connected flag */
device_connected_flag = 0;
configure:
/* Start Polling loop */
TRACE("****** Start NFC Discovery ******");
REENTRANCE_LOCK();
ret = phLibNfc_Mgt_ConfigureDiscovery(resume ? NFC_DISCOVERY_RESUME : NFC_DISCOVERY_CONFIG,
nat->discovery_cfg, nfc_jni_discover_callback, (void *)&cb_data);
REENTRANCE_UNLOCK();
TRACE("phLibNfc_Mgt_ConfigureDiscovery(%s-%s-%s-%s-%s-%s, %s-%x-%x) returned 0x%08x\n",
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableIso14443A==TRUE?"3A":"",
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableIso14443B==TRUE?"3B":"",
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableFelica212==TRUE?"F2":"",
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableFelica424==TRUE?"F4":"",
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableNfcActive==TRUE?"NFC":"",
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableIso15693==TRUE?"RFID":"",
nat->discovery_cfg.PollDevInfo.PollCfgInfo.DisableCardEmulation==FALSE?"CE":"",
nat->discovery_cfg.NfcIP_Mode, nat->discovery_cfg.Duration, ret);
if (ret == NFCSTATUS_BUSY && numRetries-- > 0)
{
TRACE("ConfigDiscovery BUSY, retrying");
usleep(1000000);
goto configure;
}
if(ret != NFCSTATUS_PENDING)
{
emergency_recovery(nat);
goto clean_and_return;
}
/* Wait for callback response */
if(sem_wait(&cb_data.sem))
{
ALOGE("Failed to wait for semaphore (errno=0x%08x)", errno);
goto clean_and_return;
}
clean_and_return:
nfc_cb_data_deinit(&cb_data);
}
static void nfc_jni_stop_discovery_locked(struct nfc_jni_native_data *nat)
{
phLibNfc_sADD_Cfg_t discovery_cfg;
NFCSTATUS ret;
struct nfc_jni_callback_data cb_data;
int numRetries = 3;
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, NULL))
{
goto clean_and_return;
}
discovery_cfg.PollDevInfo.PollEnabled = 0;
discovery_cfg.NfcIP_Mode = phNfc_eDefaultP2PMode;
discovery_cfg.NfcIP_Target_Mode = 0;
discovery_cfg.NfcIP_Tgt_Disable = TRUE;
configure:
/* Start Polling loop */
TRACE("****** Stop NFC Discovery ******");
REENTRANCE_LOCK();
ret = phLibNfc_Mgt_ConfigureDiscovery(NFC_DISCOVERY_CONFIG,discovery_cfg, nfc_jni_discover_callback, (void *)&cb_data);
REENTRANCE_UNLOCK();
TRACE("phLibNfc_Mgt_ConfigureDiscovery(%s-%s-%s-%s-%s-%s, %s-%x-%x) returned 0x%08x\n",
discovery_cfg.PollDevInfo.PollCfgInfo.EnableIso14443A==TRUE?"3A":"",
discovery_cfg.PollDevInfo.PollCfgInfo.EnableIso14443B==TRUE?"3B":"",
discovery_cfg.PollDevInfo.PollCfgInfo.EnableFelica212==TRUE?"F2":"",
discovery_cfg.PollDevInfo.PollCfgInfo.EnableFelica424==TRUE?"F4":"",
discovery_cfg.PollDevInfo.PollCfgInfo.EnableNfcActive==TRUE?"NFC":"",
discovery_cfg.PollDevInfo.PollCfgInfo.EnableIso15693==TRUE?"RFID":"",
discovery_cfg.PollDevInfo.PollCfgInfo.DisableCardEmulation==FALSE?"CE":"",
discovery_cfg.NfcIP_Mode, discovery_cfg.Duration, ret);
if (ret == NFCSTATUS_BUSY && numRetries-- > 0)
{
TRACE("ConfigDiscovery BUSY, retrying");
usleep(1000000);
goto configure;
}
if(ret != NFCSTATUS_PENDING)
{
ALOGE("[STOP] ConfigDiscovery returned %x", ret);
emergency_recovery(nat);
}
/* Wait for callback response */
if(sem_wait(&cb_data.sem))
{
ALOGE("Failed to wait for semaphore (errno=0x%08x)", errno);
goto clean_and_return;
}
clean_and_return:
nfc_cb_data_deinit(&cb_data);
}
static void com_android_nfc_NfcManager_disableDiscovery(JNIEnv *e, jobject o)
{
struct nfc_jni_native_data *nat;
CONCURRENCY_LOCK();
/* Retrieve native structure address */
nat = nfc_jni_get_nat(e, o);
nfc_jni_stop_discovery_locked(nat);
CONCURRENCY_UNLOCK();
}
// TODO: use enable_lptd
static void com_android_nfc_NfcManager_enableDiscovery(JNIEnv *e, jobject o, jint modes,
jboolean, jboolean reader_mode, jboolean enable_p2p, jboolean restart)
{
NFCSTATUS ret;
struct nfc_jni_native_data *nat;
CONCURRENCY_LOCK();
nat = nfc_jni_get_nat(e, o);
/* Register callback for remote device notifications.
* Must re-register every time we turn on discovery, since other operations
* (such as opening the Secure Element) can change the remote device
* notification callback*/
REENTRANCE_LOCK();
ret = phLibNfc_RemoteDev_NtfRegister(&nat->registry_info, nfc_jni_Discovery_notification_callback, (void *)nat);
REENTRANCE_UNLOCK();
if(ret != NFCSTATUS_SUCCESS)
{
ALOGD("pphLibNfc_RemoteDev_NtfRegister returned 0x%02x",ret);
goto clean_and_return;
}
TRACE("phLibNfc_RemoteDev_NtfRegister(%s-%s-%s-%s-%s-%s-%s-%s) returned 0x%x\n",
nat->registry_info.Jewel==TRUE?"J":"",
nat->registry_info.MifareUL==TRUE?"UL":"",
nat->registry_info.MifareStd==TRUE?"Mi":"",
nat->registry_info.Felica==TRUE?"F":"",
nat->registry_info.ISO14443_4A==TRUE?"4A":"",
nat->registry_info.ISO14443_4B==TRUE?"4B":"",
nat->registry_info.NFC==TRUE?"P2P":"",
nat->registry_info.ISO15693==TRUE?"R":"", ret);
if (modes != 0)
{
if (enable_p2p)
{
nat->p2p_initiator_modes = phNfc_eP2P_ALL;
nat->p2p_target_modes = 0x0E; // All passive except 106, active
nat->discovery_cfg.Duration = 300000; /* in ms */
}
else
{
nat->p2p_initiator_modes = 0;
nat->p2p_target_modes = 0;
nat->discovery_cfg.Duration = 200000; /* in ms */
}
if (reader_mode)
{
nat->discovery_cfg.PollDevInfo.PollCfgInfo.DisableCardEmulation = TRUE;
nat->discovery_cfg.Duration = 200000; /* in ms */
}
else
{
nat->discovery_cfg.PollDevInfo.PollCfgInfo.DisableCardEmulation = FALSE;
nat->discovery_cfg.Duration = 300000; /* in ms */
}
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableIso14443A = (modes & 0x01) != 0;
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableIso14443B = (modes & 0x02) != 0;
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableFelica212 = (modes & 0x04) != 0;
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableFelica424 = (modes & 0x04) != 0;
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableIso15693 = (modes & 0x08) != 0;
}
nfc_jni_start_discovery_locked(nat, false);
clean_and_return:
CONCURRENCY_UNLOCK();
}
static void com_android_nfc_NfcManager_doResetTimeouts(JNIEnv*, jobject) {
CONCURRENCY_LOCK();
nfc_jni_reset_timeout_values();
CONCURRENCY_UNLOCK();
}
static void setFelicaTimeout(jint timeout) {
// The Felica timeout is configurable in the PN544 upto a maximum of 255 ms.
// It can be set to 0 to disable the timeout altogether, in which case we
// use the sw watchdog as a fallback.
if (timeout <= 255) {
phLibNfc_SetFelicaTimeout(timeout);
} else {
// Disable hw timeout, use sw watchdog for timeout
phLibNfc_SetFelicaTimeout(0);
phLibNfc_SetHciTimeout(timeout);
}
}
// Calculates ceiling log2 of value
static unsigned int log2(int value) {
unsigned int ret = 0;
bool isPowerOf2 = ((value & (value - 1)) == 0);
while ( (value >> ret) > 1 ) ret++;
if (!isPowerOf2) ret++;
return ret;
}
// The Iso/Mifare Xchg timeout in PN544 is a non-linear function over X
// spanning 0 - 4.9s: timeout in seconds = (256 * 16 / 13560000) * 2 ^ X
//
// We keep the constant part of the formula in a static; note the factor
// 1000 off, which is due to the fact that the formula calculates seconds,
// but this method gets milliseconds as an argument.
static double nxp_nfc_timeout_factor = (256 * 16) / 13560.0;
static int calcTimeout(int timeout_in_ms) {
// timeout = (256 * 16 / 13560000) * 2 ^ X
// First find the first X for which timeout > requested timeout
return (log2(ceil(((double) timeout_in_ms) / nxp_nfc_timeout_factor)));
}
static void setIsoDepTimeout(jint timeout) {
if (timeout <= 4900) {
int value = calcTimeout(timeout);
// Then re-compute the actual timeout based on X
double actual_timeout = nxp_nfc_timeout_factor * (1 << value);
// Set the sw watchdog a bit longer (The PN544 timeout is very accurate,
// but it will take some time to get back through the sw layers.
// 500 ms should be enough).
phLibNfc_SetHciTimeout(ceil(actual_timeout + 500));
value |= 0x10; // bit 4 to enable timeout
phLibNfc_SetIsoXchgTimeout(value);
}
else {
// Also note that if we desire a timeout > 4.9s, the Iso Xchg timeout
// must be disabled completely, to prevent the PN544 from aborting
// the transaction. We reuse the HCI sw watchdog to catch the timeout
// in that case.
phLibNfc_SetIsoXchgTimeout(0x00);
phLibNfc_SetHciTimeout(timeout);
}
}
static void setNfcATimeout(jint timeout) {
if (timeout <= 4900) {
int value = calcTimeout(timeout);
phLibNfc_SetMifareRawTimeout(value);
}
else {
// Disable mifare raw timeout, use HCI sw watchdog instead
phLibNfc_SetMifareRawTimeout(0x00);
phLibNfc_SetHciTimeout(timeout);
}
}
static bool com_android_nfc_NfcManager_doSetTimeout(JNIEnv*, jobject,
jint tech, jint timeout) {
bool success = false;
CONCURRENCY_LOCK();
if (timeout <= 0) {
ALOGE("Timeout must be positive.");
success = false;
} else {
switch (tech) {
case TARGET_TYPE_MIFARE_CLASSIC:
case TARGET_TYPE_MIFARE_UL:
// Intentional fall-through, Mifare UL, Classic
// transceive just uses raw 3A frames
case TARGET_TYPE_ISO14443_3A:
setNfcATimeout(timeout);
success = true;
break;
case TARGET_TYPE_ISO14443_4:
setIsoDepTimeout(timeout);
success = true;
break;
case TARGET_TYPE_FELICA:
setFelicaTimeout(timeout);
success = true;
break;
default:
ALOGW("doSetTimeout: Timeout not supported for tech %d", tech);
success = false;
}
}
CONCURRENCY_UNLOCK();
return success;
}
static jint com_android_nfc_NfcManager_doGetTimeout(JNIEnv*, jobject,
jint tech) {
int timeout = -1;
CONCURRENCY_LOCK();
switch (tech) {
case TARGET_TYPE_MIFARE_CLASSIC:
case TARGET_TYPE_MIFARE_UL:
// Intentional fall-through, Mifare UL, Classic
// transceive just uses raw 3A frames
case TARGET_TYPE_ISO14443_3A:
timeout = phLibNfc_GetMifareRawTimeout();
if (timeout == 0) {
timeout = phLibNfc_GetHciTimeout();
} else {
// Timeout returned from libnfc needs conversion to ms
timeout = (nxp_nfc_timeout_factor * (1 << timeout));
}
break;
case TARGET_TYPE_ISO14443_4:
timeout = phLibNfc_GetIsoXchgTimeout() & 0x0F; // lower 4 bits only
if (timeout == 0) {
timeout = phLibNfc_GetHciTimeout();
} else {
// Timeout returned from libnfc needs conversion to ms
timeout = (nxp_nfc_timeout_factor * (1 << timeout));
}
break;
case TARGET_TYPE_FELICA:
timeout = phLibNfc_GetFelicaTimeout();
if (timeout == 0) {
timeout = phLibNfc_GetHciTimeout();
} else {
// Felica timeout already in ms
}
break;
default:
ALOGW("doGetTimeout: Timeout not supported for tech %d", tech);
break;
}
CONCURRENCY_UNLOCK();
return timeout;
}
static jboolean com_android_nfc_NfcManager_init_native_struc(JNIEnv *e, jobject o)
{
NFCSTATUS status;
struct nfc_jni_native_data *nat = NULL;
jclass cls;
jobject obj;
jfieldID f;
TRACE("****** Init Native Structure ******");
/* Initialize native structure */
nat = (nfc_jni_native_data*)malloc(sizeof(struct nfc_jni_native_data));
if(nat == NULL)
{
ALOGD("malloc of nfc_jni_native_data failed");
return FALSE;
}
memset(nat, 0, sizeof(*nat));
e->GetJavaVM(&(nat->vm));
nat->env_version = e->GetVersion();
nat->manager = e->NewGlobalRef(o);
cls = e->GetObjectClass(o);
f = e->GetFieldID(cls, "mNative", "J");
e->SetLongField(o, f, (jlong)nat);
/* Initialize native cached references */
cached_NfcManager_notifyNdefMessageListeners = e->GetMethodID(cls,
"notifyNdefMessageListeners","(Lcom/android/nfc/dhimpl/NativeNfcTag;)V");
cached_NfcManager_notifyLlcpLinkActivation = e->GetMethodID(cls,
"notifyLlcpLinkActivation","(Lcom/android/nfc/dhimpl/NativeP2pDevice;)V");
cached_NfcManager_notifyLlcpLinkDeactivated = e->GetMethodID(cls,
"notifyLlcpLinkDeactivated","(Lcom/android/nfc/dhimpl/NativeP2pDevice;)V");
cached_NfcManager_notifyRfFieldActivated = e->GetMethodID(cls,
"notifyRfFieldActivated", "()V");
cached_NfcManager_notifyRfFieldDeactivated = e->GetMethodID(cls,
"notifyRfFieldDeactivated", "()V");
if(nfc_jni_cache_object(e,"com/android/nfc/dhimpl/NativeNfcTag",&(nat->cached_NfcTag)) == -1)
{
ALOGD("Native Structure initialization failed");
return FALSE;
}
if(nfc_jni_cache_object(e,"com/android/nfc/dhimpl/NativeP2pDevice",&(nat->cached_P2pDevice)) == -1)
{
ALOGD("Native Structure initialization failed");
return FALSE;
}
TRACE("****** Init Native Structure OK ******");
return TRUE;
}
/* Init/Deinit method */
static jboolean com_android_nfc_NfcManager_initialize(JNIEnv *e, jobject o)
{
struct nfc_jni_native_data *nat = NULL;
int init_result = JNI_FALSE;
#ifdef TNFC_EMULATOR_ONLY
char value[PROPERTY_VALUE_MAX];
#endif
jboolean result;
CONCURRENCY_LOCK();
#ifdef TNFC_EMULATOR_ONLY
if (!property_get("ro.kernel.qemu", value, 0))
{
ALOGE("NFC Initialization failed: not running in an emulator\n");
goto clean_and_return;
}
#endif
/* Retrieve native structure address */
nat = nfc_jni_get_nat(e, o);
nat->seId = SMX_SECURE_ELEMENT_ID;
nat->lto = 150; // LLCP_LTO
nat->miu = 128; // LLCP_MIU
// WKS indicates well-known services; 1 << sap for each supported SAP.
// We support Link mgmt (SAP 0), SDP (SAP 1) and SNEP (SAP 4)
nat->wks = 0x13; // LLCP_WKS
nat->opt = 0; // LLCP_OPT
nat->p2p_initiator_modes = phNfc_eP2P_ALL;
nat->p2p_target_modes = 0x0E; // All passive except 106, active
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableIso14443A = TRUE;
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableIso14443B = TRUE;
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableFelica212 = TRUE;
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableFelica424 = TRUE;
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableIso15693 = TRUE;
nat->discovery_cfg.PollDevInfo.PollCfgInfo.EnableNfcActive = TRUE;
nat->discovery_cfg.PollDevInfo.PollCfgInfo.DisableCardEmulation = FALSE;
nat->registry_info.MifareUL = TRUE;
nat->registry_info.MifareStd = TRUE;
nat->registry_info.ISO14443_4A = TRUE;
nat->registry_info.ISO14443_4B = TRUE;
nat->registry_info.Jewel = TRUE;
nat->registry_info.Felica = TRUE;
nat->registry_info.NFC = TRUE;
nat->registry_info.ISO15693 = TRUE;
exported_nat = nat;
/* Perform the initialization */
init_result = nfc_jni_initialize(nat);
clean_and_return:
CONCURRENCY_UNLOCK();
/* Convert the result and return */
return (init_result==TRUE)?JNI_TRUE:JNI_FALSE;
}
static jboolean com_android_nfc_NfcManager_deinitialize(JNIEnv *e, jobject o)
{
struct timespec ts;
NFCSTATUS status;
int result = JNI_FALSE;
struct nfc_jni_native_data *nat;
int bStackReset = FALSE;
struct nfc_jni_callback_data cb_data;
CONCURRENCY_LOCK();
/* Retrieve native structure address */
nat = nfc_jni_get_nat(e, o);
/* Clear previous configuration */
memset(&nat->discovery_cfg, 0, sizeof(phLibNfc_sADD_Cfg_t));
memset(&nat->registry_info, 0, sizeof(phLibNfc_Registry_Info_t));
/* Create the local semaphore */
if (nfc_cb_data_init(&cb_data, NULL))
{
TRACE("phLibNfc_Mgt_DeInitialize()");
REENTRANCE_LOCK();
status = phLibNfc_Mgt_DeInitialize(gHWRef, nfc_jni_deinit_callback, (void *)&cb_data);
REENTRANCE_UNLOCK();
if (status == NFCSTATUS_PENDING)
{
TRACE("phLibNfc_Mgt_DeInitialize() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 5;
/* Wait for callback response */
if(sem_timedwait(&cb_data.sem, &ts) == -1)
{
ALOGW("Operation timed out");
bStackReset = TRUE;
}
if(cb_data.status != NFCSTATUS_SUCCESS)
{
ALOGE("Failed to deinit the stack");
bStackReset = TRUE;
}
}
else
{
TRACE("phLibNfc_Mgt_DeInitialize() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
bStackReset = TRUE;
}
nfc_cb_data_deinit(&cb_data);
}
else
{
ALOGE("Failed to create semaphore (errno=0x%08x)", errno);
bStackReset = TRUE;
}
kill_client(nat);
if(bStackReset == TRUE)
{
/* Complete deinit. failed, try hard restart of NFC */
ALOGW("Reseting stack...");
emergency_recovery(nat);
}
result = nfc_jni_unconfigure_driver(nat);
TRACE("NFC Deinitialized");
CONCURRENCY_UNLOCK();
return TRUE;
}
/* Llcp methods */
static jboolean com_android_nfc_NfcManager_doCheckLlcp(JNIEnv *e, jobject o)
{
NFCSTATUS ret;
bool freeData = false;
jboolean result = JNI_FALSE;
struct nfc_jni_native_data *nat;
struct nfc_jni_callback_data *cb_data;
CONCURRENCY_LOCK();
/* Memory allocation for cb_data
* This is on the heap because it is used by libnfc
* even after this call has succesfully finished. It is only freed
* upon link closure in nfc_jni_llcp_linkStatus_callback.
*/
cb_data = (struct nfc_jni_callback_data*) malloc (sizeof(nfc_jni_callback_data));
/* Retrieve native structure address */
nat = nfc_jni_get_nat(e, o);
/* Create the local semaphore */
if (!nfc_cb_data_init(cb_data, (void*)nat))
{
goto clean_and_return;
}
/* Check LLCP compliancy */
TRACE("phLibNfc_Llcp_CheckLlcp(hLlcpHandle=0x%08x)", hLlcpHandle);
REENTRANCE_LOCK();
ret = phLibNfc_Llcp_CheckLlcp(hLlcpHandle,
nfc_jni_checkLlcp_callback,
nfc_jni_llcp_linkStatus_callback,
(void*)cb_data);
REENTRANCE_UNLOCK();
/* In case of a NFCIP return NFCSTATUS_SUCCESS and in case of an another protocol
* NFCSTATUS_PENDING. In this case NFCSTATUS_SUCCESS will also cause the callback. */
if(ret != NFCSTATUS_PENDING && ret != NFCSTATUS_SUCCESS)
{
ALOGE("phLibNfc_Llcp_CheckLlcp() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
freeData = true;
goto clean_and_return;
}
TRACE("phLibNfc_Llcp_CheckLlcp() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
/* Wait for callback response */
if(sem_wait(&cb_data->sem))
{
ALOGE("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);
if (freeData) {
free(cb_data);
}
CONCURRENCY_UNLOCK();
return result;
}
static jboolean com_android_nfc_NfcManager_doActivateLlcp(JNIEnv*, jobject)
{
NFCSTATUS ret;
TRACE("phLibNfc_Llcp_Activate(hRemoteDevice=0x%08x)", hLlcpHandle);
REENTRANCE_LOCK();
ret = phLibNfc_Llcp_Activate(hLlcpHandle);
REENTRANCE_UNLOCK();
if(ret == NFCSTATUS_SUCCESS)
{
TRACE("phLibNfc_Llcp_Activate() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
return JNI_TRUE;
}
else
{
ALOGE("phLibNfc_Llcp_Activate() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
return JNI_FALSE;
}
}
static jobject com_android_nfc_NfcManager_doCreateLlcpConnectionlessSocket(JNIEnv *e, jobject o,
jint nSap, jstring sn)
{
NFCSTATUS ret;
jobject connectionlessSocket = NULL;
phLibNfc_Handle hLlcpSocket;
struct nfc_jni_native_data *nat;
phNfc_sData_t sWorkingBuffer = {NULL, 0};
phNfc_sData_t serviceName = {NULL, 0};
phLibNfc_Llcp_sLinkParameters_t sParams;
jclass clsNativeConnectionlessSocket;
jfieldID f;
/* Retrieve native structure address */
nat = nfc_jni_get_nat(e, o);
/* Allocate Working buffer length */
phLibNfc_Llcp_GetLocalInfo(hLlcpHandle, &sParams);
sWorkingBuffer.length = sParams.miu + 1; // extra byte for SAP
sWorkingBuffer.buffer = (uint8_t*)malloc(sWorkingBuffer.length);
/* Create socket */
TRACE("phLibNfc_Llcp_Socket(eType=phFriNfc_LlcpTransport_eConnectionLess, ...)");
REENTRANCE_LOCK();
ret = phLibNfc_Llcp_Socket(phFriNfc_LlcpTransport_eConnectionLess,
NULL,
&sWorkingBuffer,
&hLlcpSocket,
nfc_jni_llcp_transport_socket_err_callback,
(void*)nat);
REENTRANCE_UNLOCK();
if(ret != NFCSTATUS_SUCCESS)
{
lastErrorStatus = ret;
ALOGE("phLibNfc_Llcp_Socket() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
goto error;
}
TRACE("phLibNfc_Llcp_Socket() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
/* Service socket */
if (sn == NULL) {
serviceName.buffer = NULL;
serviceName.length = 0;
} else {
serviceName.buffer = (uint8_t*)e->GetStringUTFChars(sn, NULL);
serviceName.length = (uint32_t)e->GetStringUTFLength(sn);
}
/* Bind socket */
TRACE("phLibNfc_Llcp_Bind(hSocket=0x%08x, nSap=0x%02x)", hLlcpSocket, nSap);
REENTRANCE_LOCK();
ret = phLibNfc_Llcp_Bind(hLlcpSocket,nSap, &serviceName);
REENTRANCE_UNLOCK();
if(ret != NFCSTATUS_SUCCESS)
{
lastErrorStatus = ret;
ALOGE("phLibNfc_Llcp_Bind() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
/* Close socket created */
REENTRANCE_LOCK();
ret = phLibNfc_Llcp_Close(hLlcpSocket);
REENTRANCE_UNLOCK();
goto error;
}
TRACE("phLibNfc_Llcp_Bind() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
/* Create new NativeLlcpConnectionlessSocket object */
if(nfc_jni_cache_object(e,"com/android/nfc/dhimpl/NativeLlcpConnectionlessSocket",&(connectionlessSocket)) == -1)
{
goto error;
}
/* Get NativeConnectionless class object */
clsNativeConnectionlessSocket = e->GetObjectClass(connectionlessSocket);
if(e->ExceptionCheck())
{
goto error;
}
/* Set socket handle */
f = e->GetFieldID(clsNativeConnectionlessSocket, "mHandle", "I");
e->SetIntField(connectionlessSocket, f,(jint)hLlcpSocket);
TRACE("Connectionless socket Handle = %02x\n",hLlcpSocket);
/* Set the miu link of the connectionless socket */
f = e->GetFieldID(clsNativeConnectionlessSocket, "mLinkMiu", "I");
e->SetIntField(connectionlessSocket, f,(jint)PHFRINFC_LLCP_MIU_DEFAULT);
TRACE("Connectionless socket Link MIU = %d\n",PHFRINFC_LLCP_MIU_DEFAULT);
/* Set socket SAP */
f = e->GetFieldID(clsNativeConnectionlessSocket, "mSap", "I");
e->SetIntField(connectionlessSocket, f,(jint)nSap);
TRACE("Connectionless socket SAP = %d\n",nSap);
return connectionlessSocket;
error:
if (serviceName.buffer != NULL) {
e->ReleaseStringUTFChars(sn, (const char *)serviceName.buffer);
}
if (sWorkingBuffer.buffer != NULL) {
free(sWorkingBuffer.buffer);
}
return NULL;
}
static jobject com_android_nfc_NfcManager_doCreateLlcpServiceSocket(JNIEnv *e, jobject o, jint nSap, jstring sn, jint miu, jint rw, jint linearBufferLength)
{
NFCSTATUS ret;
phLibNfc_Handle hLlcpSocket;
phLibNfc_Llcp_sSocketOptions_t sOptions;
phNfc_sData_t sWorkingBuffer;
phNfc_sData_t serviceName;
struct nfc_jni_native_data *nat;
jobject serviceSocket = NULL;
jclass clsNativeLlcpServiceSocket;
jfieldID f;
/* Retrieve native structure address */
nat = nfc_jni_get_nat(e, o);
/* Set Connection Oriented socket options */
sOptions.miu = miu;
sOptions.rw = rw;
/* Allocate Working buffer length */
sWorkingBuffer.length = (miu*rw)+ miu + linearBufferLength;
sWorkingBuffer.buffer = (uint8_t*)malloc(sWorkingBuffer.length);
/* Create socket */
TRACE("phLibNfc_Llcp_Socket(hRemoteDevice=0x%08x, eType=phFriNfc_LlcpTransport_eConnectionOriented, ...)", hLlcpHandle);
REENTRANCE_LOCK();
ret = phLibNfc_Llcp_Socket(phFriNfc_LlcpTransport_eConnectionOriented,
&sOptions,
&sWorkingBuffer,
&hLlcpSocket,
nfc_jni_llcp_transport_socket_err_callback,
(void*)nat);
REENTRANCE_UNLOCK();
if(ret != NFCSTATUS_SUCCESS)
{
ALOGE("phLibNfc_Llcp_Socket() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
lastErrorStatus = ret;
goto error;
}
TRACE("phLibNfc_Llcp_Socket() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
/* Service socket */
if (sn == NULL) {
serviceName.buffer = NULL;
serviceName.length = 0;
} else {
serviceName.buffer = (uint8_t*)e->GetStringUTFChars(sn, NULL);
serviceName.length = (uint32_t)e->GetStringUTFLength(sn);
}
/* Bind socket */
TRACE("phLibNfc_Llcp_Bind(hSocket=0x%08x, nSap=0x%02x)", hLlcpSocket, nSap);
REENTRANCE_LOCK();
ret = phLibNfc_Llcp_Bind(hLlcpSocket,nSap, &serviceName);
REENTRANCE_UNLOCK();
if(ret != NFCSTATUS_SUCCESS)
{
lastErrorStatus = ret;
ALOGE("phLibNfc_Llcp_Bind() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
/* Close socket created */
ret = phLibNfc_Llcp_Close(hLlcpSocket);
goto error;
}
TRACE("phLibNfc_Llcp_Bind() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
TRACE("phLibNfc_Llcp_Listen(hSocket=0x%08x, ...)", hLlcpSocket);
REENTRANCE_LOCK();
ret = phLibNfc_Llcp_Listen( hLlcpSocket,
nfc_jni_llcp_transport_listen_socket_callback,
(void*)hLlcpSocket);
REENTRANCE_UNLOCK();
if(ret != NFCSTATUS_SUCCESS)
{
ALOGE("phLibNfc_Llcp_Listen() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
lastErrorStatus = ret;
/* Close created socket */
REENTRANCE_LOCK();
ret = phLibNfc_Llcp_Close(hLlcpSocket);
REENTRANCE_UNLOCK();
goto error;
}
TRACE("phLibNfc_Llcp_Listen() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
/* Create new NativeLlcpServiceSocket object */
if(nfc_jni_cache_object(e,"com/android/nfc/dhimpl/NativeLlcpServiceSocket",&(serviceSocket)) == -1)
{
ALOGE("Llcp Socket object creation error");
goto error;
}
/* Get NativeLlcpServiceSocket class object */
clsNativeLlcpServiceSocket = e->GetObjectClass(serviceSocket);
if(e->ExceptionCheck())
{
ALOGE("Llcp Socket get object class error");
goto error;
}
/* Set socket handle */
f = e->GetFieldID(clsNativeLlcpServiceSocket, "mHandle", "I");
e->SetIntField(serviceSocket, f,(jint)hLlcpSocket);
TRACE("Service socket Handle = %02x\n",hLlcpSocket);
/* Set socket linear buffer length */
f = e->GetFieldID(clsNativeLlcpServiceSocket, "mLocalLinearBufferLength", "I");
e->SetIntField(serviceSocket, f,(jint)linearBufferLength);
TRACE("Service socket Linear buffer length = %02x\n",linearBufferLength);
/* Set socket MIU */
f = e->GetFieldID(clsNativeLlcpServiceSocket, "mLocalMiu", "I");
e->SetIntField(serviceSocket, f,(jint)miu);
TRACE("Service socket MIU = %d\n",miu);
/* Set socket RW */
f = e->GetFieldID(clsNativeLlcpServiceSocket, "mLocalRw", "I");
e->SetIntField(serviceSocket, f,(jint)rw);
TRACE("Service socket RW = %d\n",rw);
return serviceSocket;
error:
if (serviceName.buffer != NULL) {
e->ReleaseStringUTFChars(sn, (const char *)serviceName.buffer);
}
return NULL;
}
static jobject com_android_nfc_NfcManager_doCreateLlcpSocket(JNIEnv *e, jobject o, jint nSap, jint miu, jint rw, jint linearBufferLength)
{
jobject clientSocket = NULL;
NFCSTATUS ret;
phLibNfc_Handle hLlcpSocket;
phLibNfc_Llcp_sSocketOptions_t sOptions;
phNfc_sData_t sWorkingBuffer;
struct nfc_jni_native_data *nat;
jclass clsNativeLlcpSocket;
jfieldID f;
/* Retrieve native structure address */
nat = nfc_jni_get_nat(e, o);
/* Set Connection Oriented socket options */
sOptions.miu = miu;
sOptions.rw = rw;
/* Allocate Working buffer length */
sWorkingBuffer.length = (miu*rw)+ miu + linearBufferLength;
sWorkingBuffer.buffer = (uint8_t*)malloc(sWorkingBuffer.length);
/* Create socket */
TRACE("phLibNfc_Llcp_Socket(eType=phFriNfc_LlcpTransport_eConnectionOriented, ...)");
REENTRANCE_LOCK();
ret = phLibNfc_Llcp_Socket(phFriNfc_LlcpTransport_eConnectionOriented,
&sOptions,
&sWorkingBuffer,
&hLlcpSocket,
nfc_jni_llcp_transport_socket_err_callback,
(void*)nat);
REENTRANCE_UNLOCK();
if(ret != NFCSTATUS_SUCCESS)
{
ALOGE("phLibNfc_Llcp_Socket() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
lastErrorStatus = ret;
return NULL;
}
TRACE("phLibNfc_Llcp_Socket() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
/* Create new NativeLlcpSocket object */
if(nfc_jni_cache_object(e,"com/android/nfc/dhimpl/NativeLlcpSocket",&(clientSocket)) == -1)
{
ALOGE("Llcp socket object creation error");
return NULL;
}
/* Get NativeConnectionless class object */
clsNativeLlcpSocket = e->GetObjectClass(clientSocket);
if(e->ExceptionCheck())
{
ALOGE("Get class object error");
return NULL;
}
/* Test if an SAP number is present */
if(nSap != 0)
{
/* Bind socket */
TRACE("phLibNfc_Llcp_Bind(hSocket=0x%08x, nSap=0x%02x)", hLlcpSocket, nSap);
REENTRANCE_LOCK();
ret = phLibNfc_Llcp_Bind(hLlcpSocket,nSap, NULL);
REENTRANCE_UNLOCK();
if(ret != NFCSTATUS_SUCCESS)
{
lastErrorStatus = ret;
ALOGE("phLibNfc_Llcp_Bind() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
/* Close socket created */
REENTRANCE_LOCK();
ret = phLibNfc_Llcp_Close(hLlcpSocket);
REENTRANCE_UNLOCK();
return NULL;
}
TRACE("phLibNfc_Llcp_Bind() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret));
/* Set socket SAP */
f = e->GetFieldID(clsNativeLlcpSocket, "mSap", "I");
e->SetIntField(clientSocket, f,(jint)nSap);
TRACE("socket SAP = %d\n",nSap);
}
/* Set socket handle */
f = e->GetFieldID(clsNativeLlcpSocket, "mHandle", "I");
e->SetIntField(clientSocket, f,(jint)hLlcpSocket);
TRACE("socket Handle = %02x\n",hLlcpSocket);
/* Set socket MIU */
f = e->GetFieldID(clsNativeLlcpSocket, "mLocalMiu", "I");
e->SetIntField(clientSocket, f,(jint)miu);
TRACE("socket MIU = %d\n",miu);
/* Set socket RW */
f = e->GetFieldID(clsNativeLlcpSocket, "mLocalRw", "I");
e->SetIntField(clientSocket, f,(jint)rw);
TRACE("socket RW = %d\n",rw);
return clientSocket;
}
static jint com_android_nfc_NfcManager_doGetLastError(JNIEnv*, jobject)
{
TRACE("Last Error Status = 0x%02x",lastErrorStatus);
if(lastErrorStatus == NFCSTATUS_BUFFER_TOO_SMALL)
{
return ERROR_BUFFER_TOO_SMALL;
}
else if(lastErrorStatus == NFCSTATUS_INSUFFICIENT_RESOURCES)
{
return ERROR_INSUFFICIENT_RESOURCES;
}
else
{
return lastErrorStatus;
}
}
static void com_android_nfc_NfcManager_doAbort(JNIEnv*, jobject)
{
emergency_recovery(NULL);
}
static void com_android_nfc_NfcManager_doSetP2pInitiatorModes(JNIEnv *e, jobject o,
jint modes)
{
ALOGE("Setting init modes to %x", modes);
struct nfc_jni_native_data *nat = NULL;
nat = nfc_jni_get_nat(e, o);
nat->p2p_initiator_modes = modes;
}
static void com_android_nfc_NfcManager_doSetP2pTargetModes(JNIEnv *e, jobject o,
jint modes)
{
ALOGE("Setting target modes to %x", modes);
struct nfc_jni_native_data *nat = NULL;
nat = nfc_jni_get_nat(e, o);
nat->p2p_target_modes = modes;
}
static bool performDownload(struct nfc_jni_native_data* nat, bool takeLock) {
bool result = FALSE;
int load_result;
bool wasDisabled = FALSE;
uint8_t OutputBuffer[1];
uint8_t InputBuffer[1];
NFCSTATUS status = NFCSTATUS_FAILED;
struct nfc_jni_callback_data cb_data;
/* Create the local semaphore */
if (!nfc_cb_data_init(&cb_data, NULL))
{
result = FALSE;
goto clean_and_return;
}
if (takeLock)
{
CONCURRENCY_LOCK();
}
/* Initialize Driver */
if(!driverConfigured)
{
result = nfc_jni_configure_driver(nat);
wasDisabled = TRUE;
}
TRACE("com_android_nfc_NfcManager_doDownload()");
TRACE("Go in Download Mode");
phLibNfc_Download_Mode();
TRACE("Load new Firmware Image");
load_result = phLibNfc_Load_Firmware_Image();
if(load_result != 0)
{
TRACE("Load new Firmware Image - status = %d",load_result);
result = FALSE;
goto clean_and_return;
}
// Download
gInputParam.buffer = InputBuffer;
gInputParam.length = 0x01;
gOutputParam.buffer = OutputBuffer;
gOutputParam.length = 0x01;
ALOGD("Download new Firmware");
REENTRANCE_LOCK();
status = phLibNfc_Mgt_IoCtl(gHWRef,NFC_FW_DOWNLOAD, &gInputParam, &gOutputParam, nfc_jni_ioctl_callback, (void *)&cb_data);
REENTRANCE_UNLOCK();
if(status != NFCSTATUS_PENDING)
{
ALOGE("phLibNfc_Mgt_IoCtl() (download) returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
result = FALSE;
goto clean_and_return;
}
TRACE("phLibNfc_Mgt_IoCtl() (download) returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
/* Wait for callback response */
if(sem_wait(&cb_data.sem))
{
ALOGE("Failed to wait for semaphore (errno=0x%08x)", errno);
result = FALSE;
goto clean_and_return;
}
/* NOTE: we will get NFCSTATUS_FEATURE_NOT_SUPPORTED when we
try to download an old-style firmware on top of a new-style
firmware. Hence, this is expected behavior, and not an
error condition. */
if(cb_data.status != NFCSTATUS_SUCCESS && cb_data.status != NFCSTATUS_FEATURE_NOT_SUPPORTED)
{
TRACE("phLibNfc_Mgt_IoCtl() (download) returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
result = FALSE;
goto clean_and_return;
}
if(cb_data.status == NFCSTATUS_FEATURE_NOT_SUPPORTED)
{
ALOGW("Old-style firmware not installed on top of new-style firmware. Using existing firmware in the chip.");
}
/*Download is successful*/
result = TRUE;
clean_and_return:
TRACE("phLibNfc_HW_Reset()");
phLibNfc_HW_Reset();
/* Deinitialize Driver */
if(wasDisabled)
{
result = nfc_jni_unconfigure_driver(nat);
}
if (takeLock)
{
CONCURRENCY_UNLOCK();
}
nfc_cb_data_deinit(&cb_data);
return result;
}
static jboolean com_android_nfc_NfcManager_doDownload(JNIEnv *e, jobject o)
{
struct nfc_jni_native_data *nat = NULL;
nat = nfc_jni_get_nat(e, o);
return performDownload(nat, true);
}
static jstring com_android_nfc_NfcManager_doDump(JNIEnv *e, jobject)
{
char buffer[100];
snprintf(buffer, sizeof(buffer), "libnfc llc error_count=%u", libnfc_llc_error_count);
return e->NewStringUTF(buffer);
}
/*
* JNI registration.
*/
static JNINativeMethod gMethods[] =
{
{"doDownload", "()Z",
(void *)com_android_nfc_NfcManager_doDownload},
{"initializeNativeStructure", "()Z",
(void *)com_android_nfc_NfcManager_init_native_struc},
{"doInitialize", "()Z",
(void *)com_android_nfc_NfcManager_initialize},
{"doDeinitialize", "()Z",
(void *)com_android_nfc_NfcManager_deinitialize},
{"doEnableDiscovery", "(IZZZZ)V",
(void *)com_android_nfc_NfcManager_enableDiscovery},
{"doCheckLlcp", "()Z",
(void *)com_android_nfc_NfcManager_doCheckLlcp},
{"doActivateLlcp", "()Z",
(void *)com_android_nfc_NfcManager_doActivateLlcp},
{"doCreateLlcpConnectionlessSocket", "(ILjava/lang/String;)Lcom/android/nfc/dhimpl/NativeLlcpConnectionlessSocket;",
(void *)com_android_nfc_NfcManager_doCreateLlcpConnectionlessSocket},
{"doCreateLlcpServiceSocket", "(ILjava/lang/String;III)Lcom/android/nfc/dhimpl/NativeLlcpServiceSocket;",
(void *)com_android_nfc_NfcManager_doCreateLlcpServiceSocket},
{"doCreateLlcpSocket", "(IIII)Lcom/android/nfc/dhimpl/NativeLlcpSocket;",
(void *)com_android_nfc_NfcManager_doCreateLlcpSocket},
{"doGetLastError", "()I",
(void *)com_android_nfc_NfcManager_doGetLastError},
{"disableDiscovery", "()V",
(void *)com_android_nfc_NfcManager_disableDiscovery},
{"doSetTimeout", "(II)Z",
(void *)com_android_nfc_NfcManager_doSetTimeout},
{"doGetTimeout", "(I)I",
(void *)com_android_nfc_NfcManager_doGetTimeout},
{"doResetTimeouts", "()V",
(void *)com_android_nfc_NfcManager_doResetTimeouts},
{"doAbort", "()V",
(void *)com_android_nfc_NfcManager_doAbort},
{"doSetP2pInitiatorModes","(I)V",
(void *)com_android_nfc_NfcManager_doSetP2pInitiatorModes},
{"doSetP2pTargetModes","(I)V",
(void *)com_android_nfc_NfcManager_doSetP2pTargetModes},
{"doDump", "()Ljava/lang/String;",
(void *)com_android_nfc_NfcManager_doDump},
};
int register_com_android_nfc_NativeNfcManager(JNIEnv *e)
{
nfc_jni_native_monitor_t *nfc_jni_native_monitor;
nfc_jni_native_monitor = nfc_jni_init_monitor();
if(nfc_jni_native_monitor == NULL)
{
ALOGE("NFC Manager cannot recover native monitor %x\n", errno);
return -1;
}
return jniRegisterNativeMethods(e,
"com/android/nfc/dhimpl/NativeNfcManager",
gMethods, NELEM(gMethods));
}
} /* namespace android */