diff options
Diffstat (limited to 'nxp/jni/com_android_nfc_NativeNfcManager.cpp')
-rw-r--r-- | nxp/jni/com_android_nfc_NativeNfcManager.cpp | 2623 |
1 files changed, 2623 insertions, 0 deletions
diff --git a/nxp/jni/com_android_nfc_NativeNfcManager.cpp b/nxp/jni/com_android_nfc_NativeNfcManager.cpp new file mode 100644 index 00000000..e6da0fad --- /dev/null +++ b/nxp/jni/com_android_nfc_NativeNfcManager.cpp @@ -0,0 +1,2623 @@ +/* + * 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 "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_notifyTransactionListeners; +static jmethodID cached_NfcManager_notifyLlcpLinkActivation; +static jmethodID cached_NfcManager_notifyLlcpLinkDeactivated; +static jmethodID cached_NfcManager_notifyTargetDeselected; + +static jmethodID cached_NfcManager_notifySeFieldActivated; +static jmethodID cached_NfcManager_notifySeFieldDeactivated; + +static jmethodID cached_NfcManager_notifySeApduReceived; +static jmethodID cached_NfcManager_notifySeMifareAccess; +static jmethodID cached_NfcManager_notifySeEmvCardRemoval; + +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); + +/* + * 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) +{ + JNIEnv *e; + NFCSTATUS ret; + jclass tag_cls = NULL; + jobject target_array; + jobject tag; + jmethodID ctor; + jfieldID f; + const char * typeName; + jbyteArray tagUid; + jbyteArray generalBytes = NULL; + struct nfc_jni_native_data *nat; + struct timespec ts; + phNfc_sData_t data; + int i; + int target_index = 0; // Target that will be reported (if multiple can be >0) + + nat = (struct nfc_jni_native_data *)pContext; + + 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 occured"); + kill_client(nat); + } + } + else + { + LOG_CALLBACK("nfc_jni_Discovery_notification_callback", status); + TRACE("Discovered %d tags", uNofRemoteDev); + + target_index = find_preferred_target(psRemoteDevList, uNofRemoteDev); + + /* 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)) + { + + tag_cls = e->GetObjectClass(nat->cached_P2pDevice); + if(e->ExceptionCheck()) + { + ALOGE("Get Object Class Error"); + kill_client(nat); + return; + } + + /* New target instance */ + ctor = e->GetMethodID(tag_cls, "<init>", "()V"); + tag = e->NewObject(tag_cls, ctor); + + /* Set P2P Target mode */ + f = e->GetFieldID(tag_cls, "mMode", "I"); + + if(remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator) + { + ALOGD("Discovered P2P Initiator"); + e->SetIntField(tag, f, (jint)MODE_P2P_INITIATOR); + } + else + { + ALOGD("Discovered P2P Target"); + e->SetIntField(tag, f, (jint)MODE_P2P_TARGET); + } + + if(remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator) + { + /* Set General Bytes */ + f = e->GetFieldID(tag_cls, "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]); + } + + generalBytes = e->NewByteArray(remDevInfo->RemoteDevInfo.NfcIP_Info.ATRInfo_Length); + + e->SetByteArrayRegion(generalBytes, 0, + remDevInfo->RemoteDevInfo.NfcIP_Info.ATRInfo_Length, + (jbyte *)remDevInfo->RemoteDevInfo.NfcIP_Info.ATRInfo); + + e->SetObjectField(tag, f, generalBytes); + } + + /* Set tag handle */ + f = e->GetFieldID(tag_cls, "mHandle", "I"); + e->SetIntField(tag, f,(jint)remDevHandle); + TRACE("Target handle = 0x%08x",remDevHandle); + } + else + { + tag_cls = e->GetObjectClass(nat->cached_NfcTag); + if(e->ExceptionCheck()) + { + kill_client(nat); + return; + } + + /* New tag instance */ + ctor = e->GetMethodID(tag_cls, "<init>", "()V"); + tag = e->NewObject(tag_cls, ctor); + + bool multi_protocol = false; + + if(status == NFCSTATUS_MULTIPLE_PROTOCOLS) + { + TRACE("Multiple Protocol TAG detected\n"); + multi_protocol = true; + } + + /* Set tag UID */ + f = e->GetFieldID(tag_cls, "mUid", "[B"); + data = get_target_uid(remDevInfo); + tagUid = e->NewByteArray(data.length); + if(data.length > 0) + { + e->SetByteArrayRegion(tagUid, 0, data.length, (jbyte *)data.buffer); + } + e->SetObjectField(tag, f, tagUid); + + /* Generate technology list */ + jintArray techList; + jintArray handleList; + jintArray typeList; + 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, "mTechList", "[I"); + e->SetObjectField(tag, f, techList); + + f = e->GetFieldID(tag_cls, "mTechHandles", "[I"); + e->SetObjectField(tag, f, handleList); + + f = e->GetFieldID(tag_cls, "mTechLibNfcTypes", "[I"); + e->SetObjectField(tag, f, typeList); + + f = e->GetFieldID(tag_cls, "mConnectedTechIndex", "I"); + e->SetIntField(tag, f,(jint)-1); + + f = e->GetFieldID(tag_cls, "mConnectedHandle", "I"); + e->SetIntField(tag, f,(jint)-1); + } + + storedHandle = remDevHandle; + if (nat->tag != NULL) { + e->DeleteGlobalRef(nat->tag); + } + nat->tag = e->NewGlobalRef(tag); + + /* Notify the service */ + TRACE("Notify Nfc Service"); + if((remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator) + || (remDevInfo->RemDevType == phNfc_eNfcIP1_Target)) + { + /* Store the hanlde of the P2P device */ + hLlcpHandle = remDevHandle; + + /* Notify manager that new a P2P device was found */ + e->CallVoidMethod(nat->manager, cached_NfcManager_notifyLlcpLinkActivation, tag); + if(e->ExceptionCheck()) + { + ALOGE("Exception occured"); + kill_client(nat); + } + } + else + { + /* Notify manager that new a tag was found */ + e->CallVoidMethod(nat->manager, cached_NfcManager_notifyNdefMessageListeners, tag); + if(e->ExceptionCheck()) + { + ALOGE("Exception occured"); + kill_client(nat); + } + } + e->DeleteLocalRef(tag); + } +} + +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"); + if(evt_info->UiccEvtInfo.aid.length <= AID_MAXLEN) + { + aid = &(evt_info->UiccEvtInfo.aid); + + ALOGD("> AID DETECTED"); + + if(aid != NULL) + { + if (TRACE_ENABLED == 1) { + char aid_str[AID_MAXLEN * 2 + 1]; + aid_str[0] = '\0'; + for (i = 0; i < (int) (aid->length) && i < AID_MAXLEN; i++) { + snprintf(&aid_str[i*2], 3, "%02x", aid->buffer[i]); + } + ALOGD("> AID: %s", aid_str); + } + tmp_array = e->NewByteArray(aid->length); + if (tmp_array == NULL) + { + goto error; + } + + e->SetByteArrayRegion((jbyteArray)tmp_array, 0, aid->length, (jbyte *)aid->buffer); + if(e->ExceptionCheck()) + { + goto error; + } + } + else + { + goto error; + } + + TRACE("Notify Nfc Service"); + /* Notify manager that a new event occurred on a SE */ + e->CallVoidMethod(nat->manager, cached_NfcManager_notifyTransactionListeners, tmp_array); + if(e->ExceptionCheck()) + { + goto error; + } + } + else + { + ALOGD("> NO AID DETECTED"); + } + }break; + + case phLibNfc_eSE_EvtApduReceived: + { + phNfc_sData_t *apdu = &(evt_info->UiccEvtInfo.aid); + TRACE("> SE EVT_APDU_RECEIVED"); + + if (apdu != NULL) { + TRACE(" APDU length=%d", apdu->length); + tmp_array = e->NewByteArray(apdu->length); + if (tmp_array == NULL) { + goto error; + } + e->SetByteArrayRegion((jbyteArray)tmp_array, 0, apdu->length, (jbyte *)apdu->buffer); + if (e->ExceptionCheck()) { + goto error; + } + } else { + TRACE(" APDU EMPTY"); + } + + TRACE("Notify Nfc Service"); + e->CallVoidMethod(nat->manager, cached_NfcManager_notifySeApduReceived, tmp_array); + }break; + + case phLibNfc_eSE_EvtCardRemoval: + { + TRACE("> SE EVT_EMV_CARD_REMOVAL"); + TRACE("Notify Nfc Service"); + e->CallVoidMethod(nat->manager, cached_NfcManager_notifySeEmvCardRemoval); + }break; + + case phLibNfc_eSE_EvtMifareAccess: + { + TRACE("> SE EVT_MIFARE_ACCESS"); + mifare_command = &(evt_info->UiccEvtInfo.aid); + TRACE("> MIFARE Block: %d",mifare_command->buffer[1]); + tmp_array = e->NewByteArray(2); + if (tmp_array == NULL) + { + goto error; + } + + e->SetByteArrayRegion((jbyteArray)tmp_array, 0, 2, (jbyte *)mifare_command->buffer); + if(e->ExceptionCheck()) + { + goto error; + } + TRACE("Notify Nfc Service"); + e->CallVoidMethod(nat->manager, cached_NfcManager_notifySeMifareAccess, mifare_block); + }break; + + case phLibNfc_eSE_EvtFieldOn: + { + TRACE("> SE EVT_FIELD_ON"); + TRACE("Notify Nfc Service"); + e->CallVoidMethod(nat->manager, cached_NfcManager_notifySeFieldActivated); + }break; + + case phLibNfc_eSE_EvtFieldOff: + { + TRACE("> SE EVT_FIELD_OFF"); + TRACE("Notify Nfc Service"); + e->CallVoidMethod(nat->manager, cached_NfcManager_notifySeFieldDeactivated); + }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; + + /* 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; + + /* 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_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; + + /* 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; + + /* 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_PENDING) + { + 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(); + +} + +static void com_android_nfc_NfcManager_enableDiscovery(JNIEnv *e, jobject o) { + 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); + + nfc_jni_start_discovery_locked(nat, false); +clean_and_return: + CONCURRENCY_UNLOCK(); +} + +static void com_android_nfc_NfcManager_doResetTimeouts( JNIEnv *e, jobject o) { + 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 *e, jobject o, + jint tech, jint timeout) { + bool success = false; + CONCURRENCY_LOCK(); + if (timeout <= 0) { + ALOGE("Timeout must be positive."); + return 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 *e, jobject o, + 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", "I"); + e->SetIntField(o, f, (jint)nat); + + /* Initialize native cached references */ + cached_NfcManager_notifyNdefMessageListeners = e->GetMethodID(cls, + "notifyNdefMessageListeners","(Lcom/android/nfc/nxp/NativeNfcTag;)V"); + + cached_NfcManager_notifyTransactionListeners = e->GetMethodID(cls, + "notifyTransactionListeners", "([B)V"); + + cached_NfcManager_notifyLlcpLinkActivation = e->GetMethodID(cls, + "notifyLlcpLinkActivation","(Lcom/android/nfc/nxp/NativeP2pDevice;)V"); + + cached_NfcManager_notifyLlcpLinkDeactivated = e->GetMethodID(cls, + "notifyLlcpLinkDeactivated","(Lcom/android/nfc/nxp/NativeP2pDevice;)V"); + + cached_NfcManager_notifyTargetDeselected = e->GetMethodID(cls, + "notifyTargetDeselected","()V"); + + cached_NfcManager_notifySeFieldActivated = e->GetMethodID(cls, + "notifySeFieldActivated", "()V"); + + cached_NfcManager_notifySeFieldDeactivated = e->GetMethodID(cls, + "notifySeFieldDeactivated", "()V"); + + cached_NfcManager_notifySeApduReceived= e->GetMethodID(cls, + "notifySeApduReceived", "([B)V"); + + cached_NfcManager_notifySeMifareAccess = e->GetMethodID(cls, + "notifySeMifareAccess", "([B)V"); + + cached_NfcManager_notifySeEmvCardRemoval = e->GetMethodID(cls, + "notifySeEmvCardRemoval", "()V"); + + if(nfc_jni_cache_object(e,"com/android/nfc/nxp/NativeNfcTag",&(nat->cached_NfcTag)) == -1) + { + ALOGD("Native Structure initialization failed"); + return FALSE; + } + + if(nfc_jni_cache_object(e,"com/android/nfc/nxp/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; +} + +/* Secure Element methods */ +static jintArray com_android_nfc_NfcManager_doGetSecureElementList(JNIEnv *e, jobject o) { + NFCSTATUS ret; + jintArray list= NULL; + phLibNfc_SE_List_t se_list[PHLIBNFC_MAXNO_OF_SE]; + uint8_t i, se_count = PHLIBNFC_MAXNO_OF_SE; + + TRACE("****** Get Secure Element List ******"); + + TRACE("phLibNfc_SE_GetSecureElementList()"); + REENTRANCE_LOCK(); + ret = phLibNfc_SE_GetSecureElementList(se_list, &se_count); + REENTRANCE_UNLOCK(); + if (ret != NFCSTATUS_SUCCESS) { + ALOGE("phLibNfc_SE_GetSecureElementList() returned 0x%04x[%s]", ret, + nfc_jni_get_status_name(ret)); + return list; + } + TRACE("phLibNfc_SE_GetSecureElementList() returned 0x%04x[%s]", ret, + nfc_jni_get_status_name(ret)); + + TRACE("Nb SE: %d", se_count); + list =e->NewIntArray(se_count); + for (i = 0; i < se_count; i++) { + if (se_list[i].eSE_Type == phLibNfc_SE_Type_SmartMX) { + ALOGD("phLibNfc_SE_GetSecureElementList(): SMX detected"); + ALOGD("SE ID #%d: 0x%08x", i, se_list[i].hSecureElement); + } else if(se_list[i].eSE_Type == phLibNfc_SE_Type_UICC) { + ALOGD("phLibNfc_SE_GetSecureElementList(): UICC detected"); + ALOGD("SE ID #%d: 0x%08x", i, se_list[i].hSecureElement); + } + e->SetIntArrayRegion(list, i, 1, (jint*)&se_list[i].hSecureElement); + } + + e->DeleteLocalRef(list); + + return list; +} + +static void com_android_nfc_NfcManager_doSelectSecureElement(JNIEnv *e, jobject o) { + NFCSTATUS ret; + struct nfc_jni_native_data *nat; + struct nfc_jni_callback_data cb_data; + + CONCURRENCY_LOCK(); + + /* Retrieve native structure address */ + nat = nfc_jni_get_nat(e, o); + + /* Create the local semaphore */ + if (!nfc_cb_data_init(&cb_data, NULL)) { + goto clean_and_return; + } + + TRACE("****** Select Secure Element ******"); + + TRACE("phLibNfc_SE_SetMode()"); + /* Set SE mode - Virtual */ + REENTRANCE_LOCK(); + ret = phLibNfc_SE_SetMode(nat->seId, phLibNfc_SE_ActModeVirtualVolatile, nfc_jni_se_set_mode_callback, + (void *)&cb_data); + REENTRANCE_UNLOCK(); + if (ret != NFCSTATUS_PENDING) { + ALOGD("phLibNfc_SE_SetMode() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret)); + goto clean_and_return; + } + TRACE("phLibNfc_SE_SetMode() 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; + } + + clean_and_return: + nfc_cb_data_deinit(&cb_data); + CONCURRENCY_UNLOCK(); +} + +static void com_android_nfc_NfcManager_doDeselectSecureElement(JNIEnv *e, jobject o) { + NFCSTATUS ret; + struct nfc_jni_native_data *nat; + struct nfc_jni_callback_data cb_data; + + CONCURRENCY_LOCK(); + + /* Retrieve native structure address */ + nat = nfc_jni_get_nat(e, o); + + /* Create the local semaphore */ + if (!nfc_cb_data_init(&cb_data, NULL)) { + goto clean_and_return; + } + + TRACE("****** Deselect Secure Element ******"); + + TRACE("phLibNfc_SE_SetMode()"); + /* Set SE mode - Default */ + REENTRANCE_LOCK(); + ret = phLibNfc_SE_SetMode(nat->seId, phLibNfc_SE_ActModeDefault, + nfc_jni_se_set_mode_callback, (void *)&cb_data); + REENTRANCE_UNLOCK(); + + TRACE("phLibNfc_SE_SetMode returned 0x%02x", ret); + if (ret != NFCSTATUS_PENDING) { + ALOGE("phLibNfc_SE_SetMode() returned 0x%04x[%s]", ret, nfc_jni_get_status_name(ret)); + goto clean_and_return; + } + TRACE("phLibNfc_SE_SetMode() 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; + } + + clean_and_return: + nfc_cb_data_deinit(&cb_data); + CONCURRENCY_UNLOCK(); +} + +/* 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 *e, jobject o) +{ + 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/nxp/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/nxp/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/nxp/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 *e, jobject o) +{ + 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 *e, jobject o) +{ + 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 o) +{ + 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}, + + {"enableDiscovery", "()V", + (void *)com_android_nfc_NfcManager_enableDiscovery}, + + {"doGetSecureElementList", "()[I", + (void *)com_android_nfc_NfcManager_doGetSecureElementList}, + + {"doSelectSecureElement", "()V", + (void *)com_android_nfc_NfcManager_doSelectSecureElement}, + + {"doDeselectSecureElement", "()V", + (void *)com_android_nfc_NfcManager_doDeselectSecureElement}, + + {"doCheckLlcp", "()Z", + (void *)com_android_nfc_NfcManager_doCheckLlcp}, + + {"doActivateLlcp", "()Z", + (void *)com_android_nfc_NfcManager_doActivateLlcp}, + + {"doCreateLlcpConnectionlessSocket", "(ILjava/lang/String;)Lcom/android/nfc/nxp/NativeLlcpConnectionlessSocket;", + (void *)com_android_nfc_NfcManager_doCreateLlcpConnectionlessSocket}, + + {"doCreateLlcpServiceSocket", "(ILjava/lang/String;III)Lcom/android/nfc/nxp/NativeLlcpServiceSocket;", + (void *)com_android_nfc_NfcManager_doCreateLlcpServiceSocket}, + + {"doCreateLlcpSocket", "(IIII)Lcom/android/nfc/nxp/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/nxp/NativeNfcManager", + gMethods, NELEM(gMethods)); +} + +} /* namespace android */ |