/* * 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 <stdlib.h> #include "errno.h" #include "com_android_nfc.h" #include "com_android_nfc_list.h" #include "phLibNfcStatus.h" #include <ScopedLocalRef.h> /* * JNI Initialization */ jint JNI_OnLoad(JavaVM *jvm, void* /*reserved*/) { JNIEnv *e; ALOGI("NFC Service: loading nxp JNI"); // Check JNI version if(jvm->GetEnv((void **)&e, JNI_VERSION_1_6)) return JNI_ERR; android::vm = jvm; if (android::register_com_android_nfc_NativeNfcManager(e) == -1) return JNI_ERR; if (android::register_com_android_nfc_NativeNfcTag(e) == -1) return JNI_ERR; if (android::register_com_android_nfc_NativeP2pDevice(e) == -1) return JNI_ERR; if (android::register_com_android_nfc_NativeLlcpSocket(e) == -1) return JNI_ERR; if (android::register_com_android_nfc_NativeLlcpConnectionlessSocket(e) == -1) return JNI_ERR; if (android::register_com_android_nfc_NativeLlcpServiceSocket(e) == -1) return JNI_ERR; return JNI_VERSION_1_6; } namespace android { extern struct nfc_jni_native_data *exported_nat; JavaVM *vm; /* * JNI Utils */ JNIEnv *nfc_get_env() { JNIEnv *e; if (vm->GetEnv((void **)&e, JNI_VERSION_1_6) != JNI_OK) { ALOGE("Current thread is not attached to VM"); phLibNfc_Mgt_Recovery(); abort(); } return e; } bool nfc_cb_data_init(nfc_jni_callback_data* pCallbackData, void* pContext) { /* Create semaphore */ if(sem_init(&pCallbackData->sem, 0, 0) == -1) { ALOGE("Semaphore creation failed (errno=0x%08x)", errno); return false; } /* Set default status value */ pCallbackData->status = NFCSTATUS_FAILED; /* Copy the context */ pCallbackData->pContext = pContext; /* Add to active semaphore list */ if (!listAdd(&nfc_jni_get_monitor()->sem_list, pCallbackData)) { ALOGE("Failed to add the semaphore to the list"); } return true; } void nfc_cb_data_deinit(nfc_jni_callback_data* pCallbackData) { /* Destroy semaphore */ if (sem_destroy(&pCallbackData->sem)) { ALOGE("Failed to destroy semaphore (errno=0x%08x)", errno); } /* Remove from active semaphore list */ if (!listRemove(&nfc_jni_get_monitor()->sem_list, pCallbackData)) { ALOGE("Failed to remove semaphore from the list"); } } void nfc_cb_data_releaseAll() { nfc_jni_callback_data* pCallbackData; while (listGetAndRemoveNext(&nfc_jni_get_monitor()->sem_list, (void**)&pCallbackData)) { pCallbackData->status = NFCSTATUS_FAILED; sem_post(&pCallbackData->sem); } } int nfc_jni_cache_object(JNIEnv *e, const char *clsname, jobject *cached_obj) { ScopedLocalRef<jclass> cls(e, e->FindClass(clsname)); if (cls.get() == NULL) { ALOGD("Find class error\n"); return -1; } jmethodID ctor = e->GetMethodID(cls.get(), "<init>", "()V"); ScopedLocalRef<jobject> obj(e, e->NewObject(cls.get(), ctor)); if (obj.get() == NULL) { ALOGD("Create object error\n"); return -1; } *cached_obj = e->NewGlobalRef(obj.get()); if (*cached_obj == NULL) { ALOGD("Global ref error\n"); return -1; } return 0; } struct nfc_jni_native_data* nfc_jni_get_nat(JNIEnv *e, jobject o) { /* Retrieve native structure address */ ScopedLocalRef<jclass> c(e, e->GetObjectClass(o)); jfieldID f = e->GetFieldID(c.get(), "mNative", "J"); return (struct nfc_jni_native_data*) e->GetLongField(o, f); } struct nfc_jni_native_data* nfc_jni_get_nat_ext(JNIEnv*) { return exported_nat; } static nfc_jni_native_monitor_t *nfc_jni_native_monitor = NULL; nfc_jni_native_monitor_t* nfc_jni_init_monitor(void) { pthread_mutexattr_t recursive_attr; pthread_mutexattr_init(&recursive_attr); pthread_mutexattr_settype(&recursive_attr, PTHREAD_MUTEX_RECURSIVE_NP); if(nfc_jni_native_monitor == NULL) { nfc_jni_native_monitor = (nfc_jni_native_monitor_t*)malloc(sizeof(nfc_jni_native_monitor_t)); } if(nfc_jni_native_monitor != NULL) { memset(nfc_jni_native_monitor, 0, sizeof(nfc_jni_native_monitor_t)); if(pthread_mutex_init(&nfc_jni_native_monitor->reentrance_mutex, &recursive_attr) == -1) { ALOGE("NFC Manager Reentrance Mutex creation returned 0x%08x", errno); return NULL; } if(pthread_mutex_init(&nfc_jni_native_monitor->concurrency_mutex, NULL) == -1) { ALOGE("NFC Manager Concurrency Mutex creation returned 0x%08x", errno); return NULL; } if(!listInit(&nfc_jni_native_monitor->sem_list)) { ALOGE("NFC Manager Semaphore List creation failed"); return NULL; } LIST_INIT(&nfc_jni_native_monitor->incoming_socket_head); if(pthread_mutex_init(&nfc_jni_native_monitor->incoming_socket_mutex, NULL) == -1) { ALOGE("NFC Manager incoming socket mutex creation returned 0x%08x", errno); return NULL; } if(pthread_cond_init(&nfc_jni_native_monitor->incoming_socket_cond, NULL) == -1) { ALOGE("NFC Manager incoming socket condition creation returned 0x%08x", errno); return NULL; } } return nfc_jni_native_monitor; } nfc_jni_native_monitor_t* nfc_jni_get_monitor(void) { return nfc_jni_native_monitor; } phLibNfc_Handle nfc_jni_get_p2p_device_handle(JNIEnv *e, jobject o) { ScopedLocalRef<jclass> c(e, e->GetObjectClass(o)); jfieldID f = e->GetFieldID(c.get(), "mHandle", "I"); return e->GetIntField(o, f); } jshort nfc_jni_get_p2p_device_mode(JNIEnv *e, jobject o) { ScopedLocalRef<jclass> c(e, e->GetObjectClass(o)); jfieldID f = e->GetFieldID(c.get(), "mMode", "S"); return e->GetShortField(o, f); } int nfc_jni_get_connected_tech_index(JNIEnv *e, jobject o) { ScopedLocalRef<jclass> c(e, e->GetObjectClass(o)); jfieldID f = e->GetFieldID(c.get(), "mConnectedTechIndex", "I"); return e->GetIntField(o, f); } jint nfc_jni_get_connected_technology(JNIEnv *e, jobject o) { int connectedTech = -1; int connectedTechIndex = nfc_jni_get_connected_tech_index(e,o); jintArray techTypes = nfc_jni_get_nfc_tag_type(e, o); if ((connectedTechIndex != -1) && (techTypes != NULL) && (connectedTechIndex < e->GetArrayLength(techTypes))) { jint* technologies = e->GetIntArrayElements(techTypes, 0); if (technologies != NULL) { connectedTech = technologies[connectedTechIndex]; e->ReleaseIntArrayElements(techTypes, technologies, JNI_ABORT); } } return connectedTech; } jint nfc_jni_get_connected_technology_libnfc_type(JNIEnv *e, jobject o) { jint connectedLibNfcType = -1; int connectedTechIndex = nfc_jni_get_connected_tech_index(e,o); ScopedLocalRef<jclass> c(e, e->GetObjectClass(o)); jfieldID f = e->GetFieldID(c.get(), "mTechLibNfcTypes", "[I"); ScopedLocalRef<jintArray> libNfcTypes(e, (jintArray) e->GetObjectField(o, f)); if ((connectedTechIndex != -1) && (libNfcTypes.get() != NULL) && (connectedTechIndex < e->GetArrayLength(libNfcTypes.get()))) { jint* types = e->GetIntArrayElements(libNfcTypes.get(), 0); if (types != NULL) { connectedLibNfcType = types[connectedTechIndex]; e->ReleaseIntArrayElements(libNfcTypes.get(), types, JNI_ABORT); } } return connectedLibNfcType; } phLibNfc_Handle nfc_jni_get_connected_handle(JNIEnv *e, jobject o) { ScopedLocalRef<jclass> c(e, e->GetObjectClass(o)); jfieldID f = e->GetFieldID(c.get(), "mConnectedHandle", "I"); return e->GetIntField(o, f); } phLibNfc_Handle nfc_jni_get_nfc_socket_handle(JNIEnv *e, jobject o) { ScopedLocalRef<jclass> c(e, e->GetObjectClass(o)); jfieldID f = e->GetFieldID(c.get(), "mHandle", "I"); return e->GetIntField(o, f); } jintArray nfc_jni_get_nfc_tag_type(JNIEnv *e, jobject o) { ScopedLocalRef<jclass> c(e, e->GetObjectClass(o)); jfieldID f = e->GetFieldID(c.get(), "mTechList","[I"); return (jintArray) e->GetObjectField(o, f); } //Display status code const char* nfc_jni_get_status_name(NFCSTATUS status) { #define STATUS_ENTRY(status) { status, #status } struct status_entry { NFCSTATUS code; const char *name; }; const struct status_entry sNameTable[] = { STATUS_ENTRY(NFCSTATUS_SUCCESS), STATUS_ENTRY(NFCSTATUS_FAILED), STATUS_ENTRY(NFCSTATUS_INVALID_PARAMETER), STATUS_ENTRY(NFCSTATUS_INSUFFICIENT_RESOURCES), STATUS_ENTRY(NFCSTATUS_TARGET_LOST), STATUS_ENTRY(NFCSTATUS_INVALID_HANDLE), STATUS_ENTRY(NFCSTATUS_MULTIPLE_TAGS), STATUS_ENTRY(NFCSTATUS_ALREADY_REGISTERED), STATUS_ENTRY(NFCSTATUS_FEATURE_NOT_SUPPORTED), STATUS_ENTRY(NFCSTATUS_SHUTDOWN), STATUS_ENTRY(NFCSTATUS_ABORTED), STATUS_ENTRY(NFCSTATUS_REJECTED ), STATUS_ENTRY(NFCSTATUS_NOT_INITIALISED), STATUS_ENTRY(NFCSTATUS_PENDING), STATUS_ENTRY(NFCSTATUS_BUFFER_TOO_SMALL), STATUS_ENTRY(NFCSTATUS_ALREADY_INITIALISED), STATUS_ENTRY(NFCSTATUS_BUSY), STATUS_ENTRY(NFCSTATUS_TARGET_NOT_CONNECTED), STATUS_ENTRY(NFCSTATUS_MULTIPLE_PROTOCOLS), STATUS_ENTRY(NFCSTATUS_DESELECTED), STATUS_ENTRY(NFCSTATUS_INVALID_DEVICE), STATUS_ENTRY(NFCSTATUS_MORE_INFORMATION), STATUS_ENTRY(NFCSTATUS_RF_TIMEOUT), STATUS_ENTRY(NFCSTATUS_RF_ERROR), STATUS_ENTRY(NFCSTATUS_BOARD_COMMUNICATION_ERROR), STATUS_ENTRY(NFCSTATUS_INVALID_STATE), STATUS_ENTRY(NFCSTATUS_NOT_REGISTERED), STATUS_ENTRY(NFCSTATUS_RELEASED), STATUS_ENTRY(NFCSTATUS_NOT_ALLOWED), STATUS_ENTRY(NFCSTATUS_INVALID_REMOTE_DEVICE), STATUS_ENTRY(NFCSTATUS_SMART_TAG_FUNC_NOT_SUPPORTED), STATUS_ENTRY(NFCSTATUS_READ_FAILED), STATUS_ENTRY(NFCSTATUS_WRITE_FAILED), STATUS_ENTRY(NFCSTATUS_NO_NDEF_SUPPORT), STATUS_ENTRY(NFCSTATUS_EOF_NDEF_CONTAINER_REACHED), STATUS_ENTRY(NFCSTATUS_INVALID_RECEIVE_LENGTH), STATUS_ENTRY(NFCSTATUS_INVALID_FORMAT), STATUS_ENTRY(NFCSTATUS_INSUFFICIENT_STORAGE), STATUS_ENTRY(NFCSTATUS_FORMAT_ERROR), }; int i = sizeof(sNameTable)/sizeof(status_entry); while(i>0) { i--; if (sNameTable[i].code == PHNFCSTATUS(status)) { return sNameTable[i].name; } } return "UNKNOWN"; } int addTechIfNeeded(int *techList, int* handleList, int* typeList, int listSize, int maxListSize, int techToAdd, int handleToAdd, int typeToAdd) { bool found = false; for (int i = 0; i < listSize; i++) { if (techList[i] == techToAdd) { found = true; break; } } if (!found && listSize < maxListSize) { techList[listSize] = techToAdd; handleList[listSize] = handleToAdd; typeList[listSize] = typeToAdd; return listSize + 1; } else { return listSize; } } #define MAX_NUM_TECHNOLOGIES 32 /* * Utility to get a technology tree and a corresponding handle list from a detected tag. */ void nfc_jni_get_technology_tree(JNIEnv* e, phLibNfc_RemoteDevList_t* devList, uint8_t count, ScopedLocalRef<jintArray>* techList, ScopedLocalRef<jintArray>* handleList, ScopedLocalRef<jintArray>* libnfcTypeList) { int technologies[MAX_NUM_TECHNOLOGIES]; int handles[MAX_NUM_TECHNOLOGIES]; int libnfctypes[MAX_NUM_TECHNOLOGIES]; int index = 0; // TODO: This counts from up to down because on multi-protocols, the // ISO handle is usually the second, and we prefer the ISO. Should implement // a method to find the "preferred handle order" and use that instead, // since we shouldn't have dependencies on the tech list ordering. for (int target = count - 1; target >= 0; target--) { int type = devList[target].psRemoteDevInfo->RemDevType; int handle = devList[target].hTargetDev; switch (type) { case phNfc_eISO14443_A_PICC: case phNfc_eISO14443_4A_PICC: { index = addTechIfNeeded(technologies, handles, libnfctypes, index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_4, handle, type); break; } case phNfc_eISO14443_4B_PICC: { index = addTechIfNeeded(technologies, handles, libnfctypes, index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_4, handle, type); index = addTechIfNeeded(technologies, handles, libnfctypes, index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_3B, handle, type); }break; case phNfc_eISO14443_3A_PICC: { index = addTechIfNeeded(technologies, handles, libnfctypes, index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_3A, handle, type); }break; case phNfc_eISO14443_B_PICC: { // TODO a bug in libnfc will cause 14443-3B only cards // to be returned as this type as well, but these cards // are very rare. Hence assume it's -4B index = addTechIfNeeded(technologies, handles, libnfctypes, index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_4, handle, type); index = addTechIfNeeded(technologies, handles, libnfctypes, index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_3B, handle, type); }break; case phNfc_eISO15693_PICC: { index = addTechIfNeeded(technologies, handles, libnfctypes, index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO15693, handle, type); }break; case phNfc_eMifare_PICC: { // We don't want to be too clever here; libnfc has already determined // it's a Mifare, so we only check for UL, for all other tags // we assume it's a mifare classic. This should make us more // future-proof. int sak = devList[target].psRemoteDevInfo->RemoteDevInfo.Iso14443A_Info.Sak; switch(sak) { case 0x00: // could be UL or UL-C index = addTechIfNeeded(technologies, handles, libnfctypes, index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_MIFARE_UL, handle, type); break; default: index = addTechIfNeeded(technologies, handles, libnfctypes, index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_MIFARE_CLASSIC, handle, type); break; } }break; case phNfc_eFelica_PICC: { index = addTechIfNeeded(technologies, handles, libnfctypes, index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_FELICA, handle, type); }break; case phNfc_eJewel_PICC: { // Jewel represented as NfcA index = addTechIfNeeded(technologies, handles, libnfctypes, index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_ISO14443_3A, handle, type); }break; default: { index = addTechIfNeeded(technologies, handles, libnfctypes, index, MAX_NUM_TECHNOLOGIES, TARGET_TYPE_UNKNOWN, handle, type); } } } // Build the Java arrays if (techList != NULL) { techList->reset(e->NewIntArray(index)); e->SetIntArrayRegion(techList->get(), 0, index, technologies); } if (handleList != NULL) { handleList->reset(e->NewIntArray(index)); e->SetIntArrayRegion(handleList->get(), 0, index, handles); } if (libnfcTypeList != NULL) { libnfcTypeList->reset(e->NewIntArray(index)); e->SetIntArrayRegion(libnfcTypeList->get(), 0, index, libnfctypes); } } } // namespace android