diff options
Diffstat (limited to 'nci/jni')
34 files changed, 15231 insertions, 0 deletions
diff --git a/nci/jni/Android.mk b/nci/jni/Android.mk new file mode 100644 index 0000000..39832fd --- /dev/null +++ b/nci/jni/Android.mk @@ -0,0 +1,54 @@ +VOB_COMPONENTS := external/libnfc-nci/src +NFA := $(VOB_COMPONENTS)/nfa +NFC := $(VOB_COMPONENTS)/nfc + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_PRELINK_MODULE := false + +ifneq ($(NCI_VERSION),) +LOCAL_CFLAGS += -DNCI_VERSION=$(NCI_VERSION) -O0 -g +endif + +define all-cpp-files-under +$(patsubst ./%,%, \ + $(shell cd $(LOCAL_PATH) ; \ + find $(1) -name "*.cpp" -and -not -name ".*") \ + ) +endef + +LOCAL_SRC_FILES:= $(call all-cpp-files-under, .) + +LOCAL_C_INCLUDES += \ + bionic \ + bionic/libstdc++ \ + external/stlport/stlport \ + external/libxml2/include \ + external/icu4c/common \ + frameworks/native/include \ + $(NFA)/include \ + $(NFA)/brcm \ + $(NFC)/include \ + $(NFC)/brcm \ + $(NFC)/int \ + $(VOB_COMPONENTS)/hal/include \ + $(VOB_COMPONENTS)/hal/int \ + $(VOB_COMPONENTS)/include \ + $(VOB_COMPONENTS)/gki/ulinux \ + $(VOB_COMPONENTS)/gki/common + +LOCAL_SHARED_LIBRARIES := \ + libicuuc \ + libnativehelper \ + libcutils \ + libutils \ + libnfc-nci \ + libstlport + +LOCAL_STATIC_LIBRARIES := libxml2 + +LOCAL_MODULE := libnfc_nci_jni +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + diff --git a/nci/jni/CondVar.cpp b/nci/jni/CondVar.cpp new file mode 100644 index 0000000..e69cc46 --- /dev/null +++ b/nci/jni/CondVar.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Encapsulate a condition variable for thread synchronization. + */ + +#include "CondVar.h" +#include "NfcJniUtil.h" +#include <errno.h> + + +/******************************************************************************* +** +** Function: CondVar +** +** Description: Initialize member variables. +** +** Returns: None. +** +*******************************************************************************/ +CondVar::CondVar () +{ + memset (&mCondition, 0, sizeof(mCondition)); + int const res = pthread_cond_init (&mCondition, NULL); + if (res) + { + ALOGE ("CondVar::CondVar: fail init; error=0x%X", res); + } +} + + +/******************************************************************************* +** +** Function: ~CondVar +** +** Description: Cleanup all resources. +** +** Returns: None. +** +*******************************************************************************/ +CondVar::~CondVar () +{ + int const res = pthread_cond_destroy (&mCondition); + if (res) + { + ALOGE ("CondVar::~CondVar: fail destroy; error=0x%X", res); + } +} + + +/******************************************************************************* +** +** Function: wait +** +** Description: Block the caller and wait for a condition. +** +** Returns: None. +** +*******************************************************************************/ +void CondVar::wait (Mutex& mutex) +{ + int const res = pthread_cond_wait (&mCondition, mutex.nativeHandle()); + if (res) + { + ALOGE ("CondVar::wait: fail wait; error=0x%X", res); + } +} + + +/******************************************************************************* +** +** Function: wait +** +** Description: Block the caller and wait for a condition. +** millisec: Timeout in milliseconds. +** +** Returns: True if wait is successful; false if timeout occurs. +** +*******************************************************************************/ +bool CondVar::wait (Mutex& mutex, long millisec) +{ + bool retVal = false; + struct timespec absoluteTime; + + if (clock_gettime (CLOCK_MONOTONIC, &absoluteTime) == -1) + { + ALOGE ("CondVar::wait: fail get time; errno=0x%X", errno); + } + else + { + absoluteTime.tv_sec += millisec / 1000; + long ns = absoluteTime.tv_nsec + ((millisec % 1000) * 1000000); + if (ns > 1000000000) + { + absoluteTime.tv_sec++; + absoluteTime.tv_nsec = ns - 1000000000; + } + else + absoluteTime.tv_nsec = ns; + } + + //pthread_cond_timedwait_monotonic_np() is an Android-specific function + //declared in /development/ndk/platforms/android-9/include/pthread.h; + //it uses monotonic clock. + //the standard pthread_cond_timedwait() uses realtime clock. + int waitResult = pthread_cond_timedwait_monotonic_np (&mCondition, mutex.nativeHandle(), &absoluteTime); + if ((waitResult != 0) && (waitResult != ETIMEDOUT)) + ALOGE ("CondVar::wait: fail timed wait; error=0x%X", waitResult); + retVal = (waitResult == 0); //waited successfully + return retVal; +} + + +/******************************************************************************* +** +** Function: notifyOne +** +** Description: Unblock the waiting thread. +** +** Returns: None. +** +*******************************************************************************/ +void CondVar::notifyOne () +{ + int const res = pthread_cond_signal (&mCondition); + if (res) + { + ALOGE ("CondVar::notifyOne: fail signal; error=0x%X", res); + } +} + diff --git a/nci/jni/CondVar.h b/nci/jni/CondVar.h new file mode 100644 index 0000000..c286d5c --- /dev/null +++ b/nci/jni/CondVar.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Encapsulate a condition variable for thread synchronization. + */ + +#pragma once +#include <pthread.h> +#include "Mutex.h" + + +class CondVar +{ +public: + /******************************************************************************* + ** + ** Function: CondVar + ** + ** Description: Initialize member variables. + ** + ** Returns: None. + ** + *******************************************************************************/ + CondVar (); + + + /******************************************************************************* + ** + ** Function: ~CondVar + ** + ** Description: Cleanup all resources. + ** + ** Returns: None. + ** + *******************************************************************************/ + ~CondVar (); + + + /******************************************************************************* + ** + ** Function: wait + ** + ** Description: Block the caller and wait for a condition. + ** + ** Returns: None. + ** + *******************************************************************************/ + void wait (Mutex& mutex); + + + /******************************************************************************* + ** + ** Function: wait + ** + ** Description: Block the caller and wait for a condition. + ** millisec: Timeout in milliseconds. + ** + ** Returns: True if wait is successful; false if timeout occurs. + ** + *******************************************************************************/ + bool wait (Mutex& mutex, long millisec); + + + /******************************************************************************* + ** + ** Function: notifyOne + ** + ** Description: Unblock the waiting thread. + ** + ** Returns: None. + ** + *******************************************************************************/ + void notifyOne (); + +private: + pthread_cond_t mCondition; +}; diff --git a/nci/jni/DataQueue.cpp b/nci/jni/DataQueue.cpp new file mode 100644 index 0000000..caa2575 --- /dev/null +++ b/nci/jni/DataQueue.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Store data bytes in a variable-size queue. + */ + +#include "DataQueue.h" + + +/******************************************************************************* +** +** Function: DataQueue +** +** Description: Initialize member variables. +** +** Returns: None. +** +*******************************************************************************/ +DataQueue::DataQueue () +{ +} + + +/******************************************************************************* +** +** Function: ~DataQueue +** +** Description: Release all resources. +** +** Returns: None. +** +*******************************************************************************/ +DataQueue::~DataQueue () +{ + mMutex.lock (); + while (mQueue.empty() == false) + { + tHeader* header = mQueue.front (); + mQueue.pop_front (); + free (header); + } + mMutex.unlock (); +} + + +bool DataQueue::isEmpty() +{ + mMutex.lock (); + bool retval = mQueue.empty(); + mMutex.unlock (); + return retval; +} + + +/******************************************************************************* +** +** Function: enqueue +** +** Description: Append data to the queue. +** data: array of bytes +** dataLen: length of the data. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool DataQueue::enqueue (UINT8* data, UINT16 dataLen) +{ + if ((data == NULL) || (dataLen==0)) + return false; + + mMutex.lock (); + + bool retval = false; + tHeader* header = (tHeader*) malloc (sizeof(tHeader) + dataLen); + + if (header) + { + memset (header, 0, sizeof(tHeader)); + header->mDataLen = dataLen; + memcpy (header+1, data, dataLen); + + mQueue.push_back (header); + + retval = true; + } + else + { + ALOGE ("DataQueue::enqueue: out of memory ?????"); + } + mMutex.unlock (); + return retval; +} + + +/******************************************************************************* +** +** Function: dequeue +** +** Description: Retrieve and remove data from the front of the queue. +** buffer: array to store the data. +** bufferMaxLen: maximum size of the buffer. +** actualLen: actual length of the data. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool DataQueue::dequeue (UINT8* buffer, UINT16 bufferMaxLen, UINT16& actualLen) +{ + mMutex.lock (); + + tHeader* header = mQueue.front (); + bool retval = false; + + if (header && buffer && (bufferMaxLen>0)) + { + if (header->mDataLen <= bufferMaxLen) + { + //caller's buffer is big enough to store all data + actualLen = header->mDataLen; + char* src = (char*)(header) + sizeof(tHeader) + header->mOffset; + memcpy (buffer, src, actualLen); + + mQueue.pop_front (); + free (header); + } + else + { + //caller's buffer is too small + actualLen = bufferMaxLen; + char* src = (char*)(header) + sizeof(tHeader) + header->mOffset; + memcpy (buffer, src, actualLen); + //adjust offset so the next dequeue() will get the remainder + header->mDataLen -= actualLen; + header->mOffset += actualLen; + } + retval = true; + } + mMutex.unlock (); + return retval; +} + diff --git a/nci/jni/DataQueue.h b/nci/jni/DataQueue.h new file mode 100644 index 0000000..bfd415c --- /dev/null +++ b/nci/jni/DataQueue.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Store data bytes in a variable-size queue. + */ + +#pragma once +#include "NfcJniUtil.h" +#include "gki.h" +#include "Mutex.h" +#include <list> + + +class DataQueue +{ +public: + /******************************************************************************* + ** + ** Function: DataQueue + ** + ** Description: Initialize member variables. + ** + ** Returns: None. + ** + *******************************************************************************/ + DataQueue (); + + + /******************************************************************************* + ** + ** Function: ~DataQueue + ** + ** Description: Release all resources. + ** + ** Returns: None. + ** + *******************************************************************************/ + ~DataQueue (); + + + /******************************************************************************* + ** + ** Function: enqueue + ** + ** Description: Append data to the queue. + ** data: array of bytes + ** dataLen: length of the data. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool enqueue (UINT8* data, UINT16 dataLen); + + + /******************************************************************************* + ** + ** Function: dequeue + ** + ** Description: Retrieve and remove data from the front of the queue. + ** buffer: array to store the data. + ** bufferMaxLen: maximum size of the buffer. + ** actualLen: actual length of the data. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool dequeue (UINT8* buffer, UINT16 bufferMaxLen, UINT16& actualLen); + + + /******************************************************************************* + ** + ** Function: isEmpty + ** + ** Description: Whether the queue is empty. + ** + ** Returns: True if empty. + ** + *******************************************************************************/ + bool isEmpty(); + +private: + struct tHeader + { + UINT16 mDataLen; //number of octets of data + UINT16 mOffset; //offset of the first octet of data + }; + typedef std::list<tHeader*> Queue; + + Queue mQueue; + Mutex mMutex; +}; + diff --git a/nci/jni/HostAidRouter.cpp b/nci/jni/HostAidRouter.cpp new file mode 100644 index 0000000..0b511f8 --- /dev/null +++ b/nci/jni/HostAidRouter.cpp @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Manage listen-mode AID routing to the host. + */ +#include "OverrideLog.h" +#include "HostAidRouter.h" +#include "config.h" +#include "SecureElement.h" + + +HostAidRouter HostAidRouter::sHostAidRouter; //singleton HostAidRouter object + + +/******************************************************************************* +** +** Function: HostAidRouter +** +** Description: Private constructor to prevent public call. +** +** Returns: None. +** +*******************************************************************************/ +HostAidRouter::HostAidRouter () + : mTempHandle (NFA_HANDLE_INVALID), + mIsFeatureEnabled (true) +{ +} + + +/******************************************************************************* +** +** Function: ~HostAidRouter +** +** Description: Private destructor to prevent public call. +** +** Returns: None. +** +*******************************************************************************/ +HostAidRouter::~HostAidRouter () +{ +} + + +/******************************************************************************* +** +** Function: getInstance +** +** Description: Obtain a reference to the singleton object of HostAidRouter +** +** Returns: Reference to HostAidRouter object. +** +*******************************************************************************/ +HostAidRouter& HostAidRouter::getInstance () +{ + return sHostAidRouter; +} + + +/******************************************************************************* +** +** Function: initialize +** +** Description: Initialize all resources. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool HostAidRouter::initialize () +{ + unsigned long num = 0; + mTempHandle = NFA_HANDLE_INVALID; + mHandleDatabase.clear (); + + if (GetNumValue (NAME_REGISTER_VIRTUAL_SE, &num, sizeof (num))) + mIsFeatureEnabled = num != 0; + return true; +} + + +/******************************************************************************* +** +** Function: addPpseRoute +** +** Description: Route Proximity Payment System Environment request +** to the host. This function is called when there is no +** route data. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool HostAidRouter::addPpseRoute () +{ + static const char fn [] = "HostAidRouter::addPpseRoute"; + ALOGD ("%s: enter", fn); + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + bool retval = false; + + if (! mIsFeatureEnabled) + { + ALOGD ("%s: feature disabled", fn); + goto TheEnd; + } + + { + ALOGD ("%s: register PPSE AID", fn); + SyncEventGuard guard (mRegisterEvent); + mTempHandle = NFA_HANDLE_INVALID; + nfaStat = NFA_CeRegisterAidOnDH ((UINT8*) "2PAY.SYS.DDF01", 14, stackCallback); + if (nfaStat == NFA_STATUS_OK) + { + mRegisterEvent.wait (); //wait for NFA_CE_REGISTERED_EVT + if (mTempHandle == NFA_HANDLE_INVALID) + { + ALOGE ("%s: received invalid handle", fn); + goto TheEnd; + } + else + mHandleDatabase.push_back (mTempHandle); + } + else + { + ALOGE ("%s: fail register", fn); + goto TheEnd; + } + } + retval = true; + +TheEnd: + ALOGD ("%s: exit; ok=%u", fn, retval); + return retval; +} + + +/******************************************************************************* +** +** Function: deleteAllRoutes +** +** Description: Delete all AID routes to the host. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool HostAidRouter::deleteAllRoutes () +{ + static const char fn [] = "HostAidRouter::deleteAllRoutes"; + ALOGD ("%s: enter", fn); + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + bool retval = false; + + if (! mIsFeatureEnabled) + { + ALOGD ("%s: feature disabled", fn); + goto TheEnd; + } + + //deregister each registered AID from the stack + for (AidHandleDatabase::iterator iter1 = mHandleDatabase.begin(); iter1 != mHandleDatabase.end(); iter1++) + { + tNFA_HANDLE aidHandle = *iter1; + ALOGD ("%s: deregister h=0x%X", fn, aidHandle); + SyncEventGuard guard (mDeregisterEvent); + nfaStat = NFA_CeDeregisterAidOnDH (aidHandle); + if (nfaStat == NFA_STATUS_OK) + mDeregisterEvent.wait (); //wait for NFA_CE_DEREGISTERED_EVT + else + ALOGE ("%s: fail deregister", fn); + } + mHandleDatabase.clear (); + retval = true; + +TheEnd: + ALOGD ("%s: exit; ok=%u", fn, retval); + return retval; +} + + +/******************************************************************************* +** +** Function: startRoute +** +** Description: Begin to route AID request to the host. +** aid: Buffer that contains Application ID +** aidLen: Actual length of the buffer. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool HostAidRouter::startRoute (const UINT8* aid, UINT8 aidLen) +{ + static const char fn [] = "HostAidRouter::startRoute"; + ALOGD ("%s: enter", fn); + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + bool retval = false; + + if (! mIsFeatureEnabled) + { + ALOGD ("%s: feature disabled", fn); + goto TheEnd; + } + + { + ALOGD ("%s: register AID; len=%u", fn, aidLen); + SyncEventGuard guard (mRegisterEvent); + mTempHandle = NFA_HANDLE_INVALID; + nfaStat = NFA_CeRegisterAidOnDH ((UINT8*) aid, aidLen, stackCallback); + if (nfaStat == NFA_STATUS_OK) + { + mRegisterEvent.wait (); //wait for NFA_CE_REGISTERED_EVT + if (mTempHandle == NFA_HANDLE_INVALID) + { + ALOGE ("%s: received invalid handle", fn); + goto TheEnd; + } + else + mHandleDatabase.push_back (mTempHandle); + } + else + { + ALOGE ("%s: fail register", fn); + goto TheEnd; + } + } + +TheEnd: + ALOGD ("%s: exit; ok=%u", fn, retval); + return retval; +} + + +/******************************************************************************* +** +** Function: stackCallback +** +** Description: Receive events from the NFC stack. +** +** Returns: None. +** +*******************************************************************************/ +void HostAidRouter::stackCallback (UINT8 event, tNFA_CONN_EVT_DATA* eventData) +{ + static const char fn [] = "HostAidRouter::stackCallback"; + ALOGD("%s: event=0x%X", fn, event); + + switch (event) + { + case NFA_CE_REGISTERED_EVT: + { + tNFA_CE_REGISTERED& ce_registered = eventData->ce_registered; + ALOGD("%s: NFA_CE_REGISTERED_EVT; status=0x%X; h=0x%X", fn, ce_registered.status, ce_registered.handle); + SyncEventGuard guard (sHostAidRouter.mRegisterEvent); + if (ce_registered.status == NFA_STATUS_OK) + sHostAidRouter.mTempHandle = ce_registered.handle; + else + sHostAidRouter.mTempHandle = NFA_HANDLE_INVALID; + sHostAidRouter.mRegisterEvent.notifyOne(); + } + break; + + case NFA_CE_DEREGISTERED_EVT: + { + tNFA_CE_DEREGISTERED& ce_deregistered = eventData->ce_deregistered; + ALOGD("%s: NFA_CE_DEREGISTERED_EVT; h=0x%X", fn, ce_deregistered.handle); + SyncEventGuard guard (sHostAidRouter.mDeregisterEvent); + sHostAidRouter.mDeregisterEvent.notifyOne(); + } + break; + + case NFA_CE_DATA_EVT: + { + tNFA_CE_DATA& ce_data = eventData->ce_data; + ALOGD("%s: NFA_CE_DATA_EVT; h=0x%X; data len=%u", fn, ce_data.handle, ce_data.len); + SecureElement::getInstance().notifyTransactionListenersOfAid ((UINT8 *)"2PAY.SYS.DDF01", 14); + } + break; + } +} diff --git a/nci/jni/HostAidRouter.h b/nci/jni/HostAidRouter.h new file mode 100644 index 0000000..f653aa9 --- /dev/null +++ b/nci/jni/HostAidRouter.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Manage listen-mode AID routing to the host. + */ +#pragma once +#include "SyncEvent.h" +#include "NfcJniUtil.h" +#include "RouteDataSet.h" +#include <vector> +extern "C" +{ + #include "nfa_api.h" +} + + +class HostAidRouter +{ +public: + /******************************************************************************* + ** + ** Function: getInstance + ** + ** Description: Obtain a reference to the singleton object of HostAidRouter + ** + ** Returns: Reference to HostAidRouter object. + ** + *******************************************************************************/ + static HostAidRouter& getInstance (); + + + /******************************************************************************* + ** + ** Function: initialize + ** + ** Description: Initialize all resources. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool initialize (); + + + /******************************************************************************* + ** + ** Function: addPpseRoute + ** + ** Description: Route Proximity Payment System Environment request + ** to the host. This function is called when there is no + ** route data. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool addPpseRoute (); + + + /******************************************************************************* + ** + ** Function: deleteAllRoutes + ** + ** Description: Delete all AID routes to the host. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool deleteAllRoutes (); + + + /******************************************************************************* + ** + ** Function: isFeatureEnabled + ** + ** Description: Is AID-routing-to-host feature enabled? + ** + ** Returns: True if enabled. + ** + *******************************************************************************/ + bool isFeatureEnabled () {return mIsFeatureEnabled;}; + + + /******************************************************************************* + ** + ** Function: startRoute + ** + ** Description: Begin to route AID request to the host. + ** aid: Buffer that contains Application ID + ** aidLen: Actual length of the buffer. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool startRoute (const UINT8* aid, UINT8 aidLen); + + +private: + typedef std::vector<tNFA_HANDLE> AidHandleDatabase; + + tNFA_HANDLE mTempHandle; + bool mIsFeatureEnabled; + static HostAidRouter sHostAidRouter; //singleton object + RouteDataSet mRouteDataSet; //route data from xml file + SyncEvent mRegisterEvent; + SyncEvent mDeregisterEvent; + AidHandleDatabase mHandleDatabase; //store all AID handles that are registered with the stack + + + /******************************************************************************* + ** + ** Function: HostAidRouter + ** + ** Description: Private constructor to prevent public call. + ** + ** Returns: None. + ** + *******************************************************************************/ + HostAidRouter (); + + + /******************************************************************************* + ** + ** Function: ~HostAidRouter + ** + ** Description: Private destructor to prevent public call. + ** + ** Returns: None. + ** + *******************************************************************************/ + ~HostAidRouter (); + + + /******************************************************************************* + ** + ** Function: stackCallback + ** + ** Description: Receive events from the NFC stack. + ** + ** Returns: None. + ** + *******************************************************************************/ + static void stackCallback (UINT8 event, tNFA_CONN_EVT_DATA* eventdata); +}; diff --git a/nci/jni/IntervalTimer.cpp b/nci/jni/IntervalTimer.cpp new file mode 100644 index 0000000..f71ca8e --- /dev/null +++ b/nci/jni/IntervalTimer.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Asynchronous interval timer. + */ + +#include "IntervalTimer.h" +#include "OverrideLog.h" + + +IntervalTimer::IntervalTimer() +{ + mTimerId = NULL; + mCb = NULL; +} + + +bool IntervalTimer::set(int ms, TIMER_FUNC cb) +{ + if (mTimerId == NULL) + { + if (cb == NULL) + return false; + + if (!create(cb)) + return false; + } + if (cb != mCb) + { + kill(); + if (!create(cb)) + return false; + } + + int stat = 0; + struct itimerspec ts; + ts.it_value.tv_sec = ms / 1000; + ts.it_value.tv_nsec = (ms % 1000) * 1000000; + + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + + stat = timer_settime(mTimerId, 0, &ts, 0); + if (stat == -1) + ALOGE("IntervalTimer::set: fail set timer"); + return stat == 0; +} + + +IntervalTimer::~IntervalTimer() +{ + kill(); +} + + +void IntervalTimer::kill() +{ + if (mTimerId == NULL) + return; + + timer_delete(mTimerId); + mTimerId = NULL; + mCb = NULL; +} + + +bool IntervalTimer::create(TIMER_FUNC cb) +{ + struct sigevent se; + int stat = 0; + + /* + * Set the sigevent structure to cause the signal to be + * delivered by creating a new thread. + */ + se.sigev_notify = SIGEV_THREAD; + se.sigev_value.sival_ptr = &mTimerId; + se.sigev_notify_function = cb; + se.sigev_notify_attributes = NULL; + mCb = cb; + stat = timer_create(CLOCK_MONOTONIC, &se, &mTimerId); + if (stat == -1) + ALOGE("IntervalTimer::create: fail create timer"); + return stat == 0; +} diff --git a/nci/jni/IntervalTimer.h b/nci/jni/IntervalTimer.h new file mode 100644 index 0000000..66e345d --- /dev/null +++ b/nci/jni/IntervalTimer.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Asynchronous interval timer. + */ + +#include <time.h> + + +class IntervalTimer +{ +public: + typedef void (*TIMER_FUNC) (union sigval); + + IntervalTimer(); + ~IntervalTimer(); + bool set(int ms, TIMER_FUNC cb); + void kill(); + bool create(TIMER_FUNC ); + +private: + timer_t mTimerId; + TIMER_FUNC mCb; +}; diff --git a/nci/jni/JavaClassConstants.h b/nci/jni/JavaClassConstants.h new file mode 100644 index 0000000..30deca9 --- /dev/null +++ b/nci/jni/JavaClassConstants.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + + +namespace android +{ + extern jmethodID gCachedNfcManagerNotifyNdefMessageListeners; + extern jmethodID gCachedNfcManagerNotifyTransactionListeners; + extern jmethodID gCachedNfcManagerNotifyLlcpLinkActivation; + extern jmethodID gCachedNfcManagerNotifyLlcpLinkDeactivated; + extern jmethodID gCachedNfcManagerNotifySeFieldActivated; + extern jmethodID gCachedNfcManagerNotifySeFieldDeactivated; + extern jmethodID gCachedNfcManagerNotifySeListenActivated; + extern jmethodID gCachedNfcManagerNotifySeListenDeactivated; + + extern const char* gNativeP2pDeviceClassName; + extern const char* gNativeLlcpServiceSocketClassName; + extern const char* gNativeLlcpConnectionlessSocketClassName; + extern const char* gNativeLlcpSocketClassName; + extern const char* gNativeNfcTagClassName; + extern const char* gNativeNfcManagerClassName; + extern const char* gNativeNfcSecureElementClassName; +} diff --git a/nci/jni/Mutex.cpp b/nci/jni/Mutex.cpp new file mode 100644 index 0000000..c0b12c0 --- /dev/null +++ b/nci/jni/Mutex.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Encapsulate a mutex for thread synchronization. + */ + +#include "Mutex.h" +#include "NfcJniUtil.h" +#include <errno.h> + +/******************************************************************************* +** +** Function: Mutex +** +** Description: Initialize member variables. +** +** Returns: None. +** +*******************************************************************************/ +Mutex::Mutex () +{ + memset (&mMutex, 0, sizeof(mMutex)); + int res = pthread_mutex_init (&mMutex, NULL); + if (res != 0) + { + ALOGE ("Mutex::Mutex: fail init; error=0x%X", res); + } +} + + +/******************************************************************************* +** +** Function: ~Mutex +** +** Description: Cleanup all resources. +** +** Returns: None. +** +*******************************************************************************/ +Mutex::~Mutex () +{ + int res = pthread_mutex_destroy (&mMutex); + if (res != 0) + { + ALOGE ("Mutex::~Mutex: fail destroy; error=0x%X", res); + } +} + + +/******************************************************************************* +** +** Function: lock +** +** Description: Block the thread and try lock the mutex. +** +** Returns: None. +** +*******************************************************************************/ +void Mutex::lock () +{ + int res = pthread_mutex_lock (&mMutex); + if (res != 0) + { + ALOGE ("Mutex::lock: fail lock; error=0x%X", res); + } +} + + +/******************************************************************************* +** +** Function: unlock +** +** Description: Unlock a mutex to unblock a thread. +** +** Returns: None. +** +*******************************************************************************/ +void Mutex::unlock () +{ + int res = pthread_mutex_unlock (&mMutex); + if (res != 0) + { + ALOGE ("Mutex::unlock: fail unlock; error=0x%X", res); + } +} + + +/******************************************************************************* +** +** Function: tryLock +** +** Description: Try to lock the mutex. +** +** Returns: True if the mutex is locked. +** +*******************************************************************************/ +bool Mutex::tryLock () +{ + int res = pthread_mutex_trylock (&mMutex); + if ((res != 0) && (res != EBUSY)) + { + ALOGE ("Mutex::tryLock: error=0x%X", res); + } + return res == 0; +} + + +/******************************************************************************* +** +** Function: nativeHandle +** +** Description: Get the handle of the mutex. +** +** Returns: Handle of the mutex. +** +*******************************************************************************/ +pthread_mutex_t* Mutex::nativeHandle () +{ + return &mMutex; +} + + diff --git a/nci/jni/Mutex.h b/nci/jni/Mutex.h new file mode 100644 index 0000000..45f42de --- /dev/null +++ b/nci/jni/Mutex.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Encapsulate a mutex for thread synchronization. + */ + +#pragma once +#include <pthread.h> + + +class Mutex +{ +public: + /******************************************************************************* + ** + ** Function: Mutex + ** + ** Description: Initialize member variables. + ** + ** Returns: None. + ** + *******************************************************************************/ + Mutex (); + + + /******************************************************************************* + ** + ** Function: ~Mutex + ** + ** Description: Cleanup all resources. + ** + ** Returns: None. + ** + *******************************************************************************/ + ~Mutex (); + + + /******************************************************************************* + ** + ** Function: lock + ** + ** Description: Block the thread and try lock the mutex. + ** + ** Returns: None. + ** + *******************************************************************************/ + void lock (); + + + /******************************************************************************* + ** + ** Function: unlock + ** + ** Description: Unlock a mutex to unblock a thread. + ** + ** Returns: None. + ** + *******************************************************************************/ + void unlock (); + + + /******************************************************************************* + ** + ** Function: tryLock + ** + ** Description: Try to lock the mutex. + ** + ** Returns: True if the mutex is locked. + ** + *******************************************************************************/ + bool tryLock (); + + + /******************************************************************************* + ** + ** Function: nativeHandle + ** + ** Description: Get the handle of the mutex. + ** + ** Returns: Handle of the mutex. + ** + *******************************************************************************/ + pthread_mutex_t* nativeHandle (); + + class Autolock { + public: + inline Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); } + inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); } + inline ~Autolock() { mLock.unlock(); } + private: + Mutex& mLock; + }; + + +private: + pthread_mutex_t mMutex; +}; + +typedef Mutex::Autolock AutoMutex; diff --git a/nci/jni/NativeLlcpConnectionlessSocket.cpp b/nci/jni/NativeLlcpConnectionlessSocket.cpp new file mode 100644 index 0000000..ecc57e3 --- /dev/null +++ b/nci/jni/NativeLlcpConnectionlessSocket.cpp @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <semaphore.h> +#include <errno.h> +#include "OverrideLog.h" +#include "NfcJniUtil.h" +#include "JavaClassConstants.h" +extern "C" +{ + #include "nfa_api.h" + #include "nfa_p2p_api.h" +} + + +namespace android +{ + + +/***************************************************************************** +** +** private variables and functions +** +*****************************************************************************/ +static sem_t sConnlessRecvSem; +static jboolean sConnlessRecvWaitingForData = JNI_FALSE; +static uint8_t* sConnlessRecvBuf = NULL; +static uint32_t sConnlessRecvLen = 0; +static uint32_t sConnlessRecvRemoteSap = 0; + + +/******************************************************************************* +** +** Function: nativeLlcpConnectionlessSocket_doSendTo +** +** Description: Send data to peer. +** e: JVM environment. +** o: Java object. +** nsap: service access point. +** data: buffer for data. +** +** Returns: True if ok. +** +*******************************************************************************/ +static jboolean nativeLlcpConnectionlessSocket_doSendTo (JNIEnv *e, jobject o, jint nsap, jbyteArray data) +{ + tNFA_STATUS status = NFA_STATUS_FAILED; + jint handle = 0; + uint8_t* buf = NULL; + uint32_t len = 0; + jclass c = NULL; + jfieldID f = NULL; + + ALOGD ("%s: nsap = %d", __FUNCTION__, nsap); + + c = e->GetObjectClass (o); + f = e->GetFieldID (c, "mHandle", "I"); + handle = e->GetIntField (o, f); + + buf = (uint8_t*) e->GetByteArrayElements (data, NULL); + len = (uint32_t) e->GetArrayLength (data); + + ALOGD ("NFA_P2pSendUI: len = %d", len); + status = NFA_P2pSendUI ((tNFA_HANDLE) handle, nsap, len, buf); + + ALOGD ("%s: NFA_P2pSendUI done, status = %d", __FUNCTION__, status); + if (status != NFA_STATUS_OK) + { + ALOGE ("%s: NFA_P2pSendUI failed, status = %d", __FUNCTION__, status); + return JNI_FALSE; + } + return JNI_TRUE; +} + + +/******************************************************************************* +** +** Function: nativeLlcpConnectionlessSocket_receiveData +** +** Description: Receive data from the stack. +** data: buffer contains data. +** len: length of data. +** remoteSap: remote service access point. +** +** Returns: None +** +*******************************************************************************/ +void nativeLlcpConnectionlessSocket_receiveData (uint8_t* data, uint32_t len, uint32_t remoteSap) +{ + ALOGD ("%s: waiting for data = %d, len = %d", __FUNCTION__, sConnlessRecvWaitingForData, len); + + // Sanity... + if (sConnlessRecvLen < len) + { + len = sConnlessRecvLen; + } + + if (sConnlessRecvWaitingForData) + { + sConnlessRecvWaitingForData = JNI_FALSE; + sConnlessRecvLen = len; + memcpy (sConnlessRecvBuf, data, len); + sConnlessRecvRemoteSap = remoteSap; + + sem_post (&sConnlessRecvSem); + } +} + + +/******************************************************************************* +** +** Function: connectionlessCleanup +** +** Description: Free resources. +** +** Returns: None +** +*******************************************************************************/ +static jobject connectionlessCleanup () +{ + sConnlessRecvWaitingForData = JNI_FALSE; + sConnlessRecvLen = 0; + if (sConnlessRecvBuf != NULL) + { + free (sConnlessRecvBuf); + sConnlessRecvBuf = NULL; + } + return NULL; +} + + +/******************************************************************************* +** +** Function: nativeLlcpConnectionlessSocket_abortWait +** +** Description: Abort current operation and unblock threads. +** +** Returns: None +** +*******************************************************************************/ +void nativeLlcpConnectionlessSocket_abortWait () +{ + sem_post (&sConnlessRecvSem); +} + + +/******************************************************************************* +** +** Function: nativeLlcpConnectionlessSocket_doReceiveFrom +** +** Description: Receive data from a peer. +** e: JVM environment. +** o: Java object. +** linkMiu: max info unit +** +** Returns: LlcpPacket Java object. +** +*******************************************************************************/ +static jobject nativeLlcpConnectionlessSocket_doReceiveFrom (JNIEnv *e, jobject o, jint linkMiu) +{ + jbyteArray receivedData = NULL; + jobject llcpPacket = NULL; + jclass clsLlcpPacket = NULL; + jfieldID f = NULL; + + ALOGD ("%s: linkMiu = %d", __FUNCTION__, linkMiu); + + if (sConnlessRecvWaitingForData != JNI_FALSE) + { + ALOGD ("%s: Already waiting for incoming data", __FUNCTION__); + return NULL; + } + + sConnlessRecvBuf = (uint8_t*) malloc (linkMiu); + if (sConnlessRecvBuf == NULL) + { + ALOGD ("%s: Failed to allocate %d bytes memory buffer", __FUNCTION__, linkMiu); + return NULL; + } + sConnlessRecvLen = linkMiu; + + // Create the write semaphore + if (sem_init (&sConnlessRecvSem, 0, 0) == -1) + { + ALOGE ("%s: semaphore creation failed (errno=0x%08x)", __FUNCTION__, errno); + return connectionlessCleanup (); + } + + sConnlessRecvWaitingForData = JNI_TRUE; + + // Wait for sConnlessRecvSem completion status + if (sem_wait (&sConnlessRecvSem)) + { + ALOGE ("%s: Failed to wait for write semaphore (errno=0x%08x)", __FUNCTION__, errno); + goto TheEnd; + } + + // Create new LlcpPacket object + if (nfc_jni_cache_object (e, "com/android/nfc/LlcpPacket", &(llcpPacket)) == -1) + { + ALOGE ("%s: Find LlcpPacket class error", __FUNCTION__); + return connectionlessCleanup (); + } + + // Get NativeConnectionless class object + clsLlcpPacket = e->GetObjectClass (llcpPacket); + if (e->ExceptionCheck ()) + { + e->ExceptionClear(); + ALOGE ("%s: Get Object class error", __FUNCTION__); + return connectionlessCleanup (); + } + + // Set Llcp Packet remote SAP + f = e->GetFieldID (clsLlcpPacket, "mRemoteSap", "I"); + e->SetIntField (llcpPacket, f, (jbyte) sConnlessRecvRemoteSap); + + // Set Llcp Packet Buffer + ALOGD ("%s: Received Llcp packet buffer size = %d\n", __FUNCTION__, sConnlessRecvLen); + f = e->GetFieldID (clsLlcpPacket, "mDataBuffer", "[B"); + receivedData = e->NewByteArray (sConnlessRecvLen); + e->SetByteArrayRegion (receivedData, 0, sConnlessRecvLen, (jbyte*) sConnlessRecvBuf); + e->SetObjectField (llcpPacket, f, receivedData); + +TheEnd: + connectionlessCleanup (); + if (sem_destroy (&sConnlessRecvSem)) + { + ALOGE ("%s: Failed to destroy sConnlessRecvSem semaphore (errno=0x%08x)", __FUNCTION__, errno); + } + return llcpPacket; +} + + +/******************************************************************************* +** +** Function: nativeLlcpConnectionlessSocket_doClose +** +** Description: Close socket. +** e: JVM environment. +** o: Java object. +** +** Returns: True if ok. +** +*******************************************************************************/ +static jboolean nativeLlcpConnectionlessSocket_doClose (JNIEnv *e, jobject o) +{ + tNFA_STATUS status = NFA_STATUS_FAILED; + jint handle = 0; + jclass c = NULL; + jfieldID f = NULL; + + ALOGD ("%s", __FUNCTION__); + + c = e->GetObjectClass (o); + f = e->GetFieldID (c, "mHandle", "I"); + handle = e->GetIntField (o, f); + + status = NFA_P2pDisconnect ((tNFA_HANDLE) handle, FALSE); + if (status != NFA_STATUS_OK) + { + ALOGE ("%s: disconnect failed, status = %d", __FUNCTION__, status); + return JNI_FALSE; + } + return JNI_TRUE; +} + + +/***************************************************************************** +** +** Description: JNI functions +** +*****************************************************************************/ +static JNINativeMethod gMethods[] = +{ + {"doSendTo", "(I[B)Z", (void*) nativeLlcpConnectionlessSocket_doSendTo}, + {"doReceiveFrom", "(I)Lcom/android/nfc/LlcpPacket;", (void*) nativeLlcpConnectionlessSocket_doReceiveFrom}, + {"doClose", "()Z", (void*) nativeLlcpConnectionlessSocket_doClose}, +}; + + +/******************************************************************************* +** +** Function: register_com_android_nfc_NativeLlcpConnectionlessSocket +** +** Description: Regisgter JNI functions with Java Virtual Machine. +** e: Environment of JVM. +** +** Returns: Status of registration. +** +*******************************************************************************/ +int register_com_android_nfc_NativeLlcpConnectionlessSocket (JNIEnv *e) +{ + return jniRegisterNativeMethods (e, gNativeLlcpConnectionlessSocketClassName, gMethods, NELEM(gMethods)); +} + + +} // android namespace diff --git a/nci/jni/NativeLlcpServiceSocket.cpp b/nci/jni/NativeLlcpServiceSocket.cpp new file mode 100644 index 0000000..e1c2bb5 --- /dev/null +++ b/nci/jni/NativeLlcpServiceSocket.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "OverrideLog.h" +#include "NfcJniUtil.h" +#include "NfcAdaptation.h" +#include "PeerToPeer.h" +#include "JavaClassConstants.h" +extern "C" +{ + #include "nfa_api.h" + #include "nfa_p2p_api.h" +} + + +namespace android +{ + + +/******************************************************************************* +** +** Function: nativeLlcpServiceSocket_doAccept +** +** Description: Accept a connection request from a peer. +** e: JVM environment. +** o: Java object. +** miu: Maximum information unit. +** rw: Receive window. +** linearBufferLength: Not used. +** +** Returns: LlcpSocket Java object. +** +*******************************************************************************/ +static jobject nativeLlcpServiceSocket_doAccept(JNIEnv *e, jobject o, jint miu, jint rw, jint linearBufferLength) +{ + jobject clientSocket = NULL; + jclass clsNativeLlcpSocket = NULL; + jfieldID f = 0; + PeerToPeer::tJNI_HANDLE serverHandle; //handle of the local server + bool stat = false; + PeerToPeer::tJNI_HANDLE connHandle = PeerToPeer::getInstance().getNewJniHandle (); + + ALOGD ("%s: enter", __FUNCTION__); + + serverHandle = (PeerToPeer::tJNI_HANDLE) nfc_jni_get_nfc_socket_handle (e, o); + + stat = PeerToPeer::getInstance().accept (serverHandle, connHandle, miu, rw); + + if (! stat) + { + ALOGE ("%s: fail accept", __FUNCTION__); + goto TheEnd; + } + + /* Create new LlcpSocket object */ + if (nfc_jni_cache_object(e, gNativeLlcpSocketClassName, &(clientSocket)) == -1) + { + ALOGE ("%s: fail create NativeLlcpSocket", __FUNCTION__); + goto TheEnd; + } + + /* Get NativeConnectionOriented class object */ + clsNativeLlcpSocket = e->GetObjectClass (clientSocket); + if (e->ExceptionCheck()) + { + e->ExceptionClear(); + ALOGE ("%s: get class object error", __FUNCTION__); + goto TheEnd; + } + + /* Set socket handle */ + f = e->GetFieldID (clsNativeLlcpSocket, "mHandle", "I"); + e->SetIntField (clientSocket, f, (jint) connHandle); + + /* Set socket MIU */ + f = e->GetFieldID (clsNativeLlcpSocket, "mLocalMiu", "I"); + e->SetIntField (clientSocket, f, (jint)miu); + + /* Set socket RW */ + f = e->GetFieldID (clsNativeLlcpSocket, "mLocalRw", "I"); + e->SetIntField (clientSocket, f, (jint)rw); + +TheEnd: + ALOGD ("%s: exit", __FUNCTION__); + return clientSocket; +} + + +/******************************************************************************* +** +** Function: nativeLlcpServiceSocket_doClose +** +** Description: Close a server socket. +** e: JVM environment. +** o: Java object. +** +** Returns: True if ok. +** +*******************************************************************************/ +static jboolean nativeLlcpServiceSocket_doClose(JNIEnv *e, jobject o) +{ + ALOGD ("%s: enter", __FUNCTION__); + PeerToPeer::tJNI_HANDLE jniServerHandle = 0; + bool stat = false; + + jniServerHandle = nfc_jni_get_nfc_socket_handle (e, o); + + stat = PeerToPeer::getInstance().deregisterServer (jniServerHandle); + + ALOGD ("%s: exit", __FUNCTION__); + return JNI_TRUE; +} + + +/***************************************************************************** +** +** Description: JNI functions +** +*****************************************************************************/ +static JNINativeMethod gMethods[] = +{ + {"doAccept", "(III)Lcom/android/nfc/dhimpl/NativeLlcpSocket;", (void*) nativeLlcpServiceSocket_doAccept}, + {"doClose", "()Z", (void*) nativeLlcpServiceSocket_doClose}, +}; + + +/******************************************************************************* +** +** Function: register_com_android_nfc_NativeLlcpServiceSocket +** +** Description: Regisgter JNI functions with Java Virtual Machine. +** e: Environment of JVM. +** +** Returns: Status of registration. +** +*******************************************************************************/ +int register_com_android_nfc_NativeLlcpServiceSocket (JNIEnv* e) +{ + return jniRegisterNativeMethods (e, gNativeLlcpServiceSocketClassName, + gMethods, NELEM(gMethods)); +} + + +} //namespace android + diff --git a/nci/jni/NativeLlcpSocket.cpp b/nci/jni/NativeLlcpSocket.cpp new file mode 100644 index 0000000..74a59b9 --- /dev/null +++ b/nci/jni/NativeLlcpSocket.cpp @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "OverrideLog.h" +#include "PeerToPeer.h" +#include "JavaClassConstants.h" + + +namespace android +{ + + +/******************************************************************************* +** +** Function: nativeLlcpSocket_doConnect +** +** Description: Establish a connection to the peer. +** e: JVM environment. +** o: Java object. +** nSap: Service access point. +** +** Returns: True if ok. +** +*******************************************************************************/ +static jboolean nativeLlcpSocket_doConnect (JNIEnv* e, jobject o, jint nSap) +{ + bool stat = false; + jboolean retVal = JNI_FALSE; + + ALOGD ("%s: enter; sap=%d", __FUNCTION__, nSap); + + PeerToPeer::tJNI_HANDLE jniHandle = (PeerToPeer::tJNI_HANDLE) nfc_jni_get_nfc_socket_handle (e,o); + + stat = PeerToPeer::getInstance().connectConnOriented (jniHandle, nSap); + + if (stat) + retVal = JNI_TRUE; + + ALOGD ("%s: exit", __FUNCTION__); + return retVal; +} + + +/******************************************************************************* +** +** Function: nativeLlcpSocket_doConnectBy +** +** Description: Establish a connection to the peer. +** e: JVM environment. +** o: Java object. +** sn: Service name. +** +** Returns: True if ok. +** +*******************************************************************************/ +static jboolean nativeLlcpSocket_doConnectBy (JNIEnv* e, jobject o, jstring sn) +{ + ALOGD ("%s: enter", __FUNCTION__); + bool stat = false; + jboolean retVal = JNI_FALSE; + + PeerToPeer::tJNI_HANDLE jniHandle = (PeerToPeer::tJNI_HANDLE) nfc_jni_get_nfc_socket_handle (e,o); + + const char* serviceName = e->GetStringUTFChars (sn, JNI_FALSE); //convert jstring, which is unicode, into char* + + stat = PeerToPeer::getInstance().connectConnOriented (jniHandle, serviceName); + + e->ReleaseStringUTFChars (sn, serviceName); //free the string + + if (stat) + retVal = JNI_TRUE; + + ALOGD ("%s: exit", __FUNCTION__); + return retVal; +} + + +/******************************************************************************* +** +** Function: nativeLlcpSocket_doClose +** +** Description: Close socket. +** e: JVM environment. +** o: Java object. +** +** Returns: True if ok. +** +*******************************************************************************/ +static jboolean nativeLlcpSocket_doClose(JNIEnv *e, jobject o) +{ + ALOGD ("%s: enter", __FUNCTION__); + bool stat = false; + jboolean retVal = JNI_FALSE; + + PeerToPeer::tJNI_HANDLE jniHandle = (PeerToPeer::tJNI_HANDLE) nfc_jni_get_nfc_socket_handle (e,o); + + stat = PeerToPeer::getInstance().disconnectConnOriented (jniHandle); + + retVal = JNI_TRUE; + ALOGD ("%s: exit", __FUNCTION__); + return retVal; +} + + +/******************************************************************************* +** +** Function: nativeLlcpSocket_doSend +** +** Description: Send data to peer. +** e: JVM environment. +** o: Java object. +** data: Buffer of data. +** +** Returns: True if sent ok. +** +*******************************************************************************/ +static jboolean nativeLlcpSocket_doSend (JNIEnv* e, jobject o, jbyteArray data) +{ + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: enter", __FUNCTION__); + uint8_t* dataBuffer = (uint8_t*) e->GetByteArrayElements (data, NULL); + uint32_t dataBufferLen = (uint32_t) e->GetArrayLength (data); + bool stat = false; + + PeerToPeer::tJNI_HANDLE jniHandle = (PeerToPeer::tJNI_HANDLE) nfc_jni_get_nfc_socket_handle (e,o); + + stat = PeerToPeer::getInstance().send (jniHandle, dataBuffer, dataBufferLen); + + e->ReleaseByteArrayElements (data, (jbyte*) dataBuffer, JNI_ABORT); + + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: exit", __FUNCTION__); + return stat ? JNI_TRUE : JNI_FALSE; +} + + +/******************************************************************************* +** +** Function: nativeLlcpSocket_doReceive +** +** Description: Receive data from peer. +** e: JVM environment. +** o: Java object. +** origBuffer: Buffer to put received data. +** +** Returns: Number of bytes received. +** +*******************************************************************************/ +static jint nativeLlcpSocket_doReceive(JNIEnv *e, jobject o, jbyteArray origBuffer) +{ + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: enter", __FUNCTION__); + uint8_t* dataBuffer = (uint8_t*) e->GetByteArrayElements (origBuffer, NULL); + uint32_t dataBufferLen = (uint32_t) e->GetArrayLength (origBuffer); + uint16_t actualLen = 0; + bool stat = false; + jint retval = 0; + + PeerToPeer::tJNI_HANDLE jniHandle = (PeerToPeer::tJNI_HANDLE) nfc_jni_get_nfc_socket_handle (e,o); + + stat = PeerToPeer::getInstance().receive (jniHandle, dataBuffer, dataBufferLen, actualLen); + + if (stat && (actualLen>0)) + { + retval = actualLen; + } + else + retval = -1; + + e->ReleaseByteArrayElements (origBuffer, (jbyte*) dataBuffer, 0); + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: exit; actual len=%d", __FUNCTION__, retval); + return retval; +} + + +/******************************************************************************* +** +** Function: nativeLlcpSocket_doGetRemoteSocketMIU +** +** Description: Get peer's maximum information unit. +** e: JVM environment. +** o: Java object. +** +** Returns: Peer's maximum information unit. +** +*******************************************************************************/ +static jint nativeLlcpSocket_doGetRemoteSocketMIU (JNIEnv* e, jobject o) +{ + ALOGD ("%s: enter", __FUNCTION__); + bool stat = false; + + PeerToPeer::tJNI_HANDLE jniHandle = (PeerToPeer::tJNI_HANDLE) nfc_jni_get_nfc_socket_handle (e,o); + jint miu = 0; + + miu = PeerToPeer::getInstance().getRemoteMaxInfoUnit (jniHandle); + + ALOGD ("%s: exit", __FUNCTION__); + return miu; +} + + +/******************************************************************************* +** +** Function: nativeLlcpSocket_doGetRemoteSocketRW +** +** Description: Get peer's receive window size. +** e: JVM environment. +** o: Java object. +** +** Returns: Peer's receive window size. +** +*******************************************************************************/ +static jint nativeLlcpSocket_doGetRemoteSocketRW (JNIEnv* e, jobject o) +{ + ALOGD ("%s: enter", __FUNCTION__); + bool stat = false; + jint rw = 0; + + PeerToPeer::tJNI_HANDLE jniHandle = (PeerToPeer::tJNI_HANDLE) nfc_jni_get_nfc_socket_handle (e,o); + + rw = PeerToPeer::getInstance().getRemoteRecvWindow (jniHandle); + + ALOGD ("%s: exit", __FUNCTION__); + return rw; +} + + +/***************************************************************************** +** +** Description: JNI functions +** +*****************************************************************************/ +static JNINativeMethod gMethods[] = +{ + {"doConnect", "(I)Z", (void * ) nativeLlcpSocket_doConnect}, + {"doConnectBy", "(Ljava/lang/String;)Z", (void*) nativeLlcpSocket_doConnectBy}, + {"doClose", "()Z", (void *) nativeLlcpSocket_doClose}, + {"doSend", "([B)Z", (void *) nativeLlcpSocket_doSend}, + {"doReceive", "([B)I", (void *) nativeLlcpSocket_doReceive}, + {"doGetRemoteSocketMiu", "()I", (void *) nativeLlcpSocket_doGetRemoteSocketMIU}, + {"doGetRemoteSocketRw", "()I", (void *) nativeLlcpSocket_doGetRemoteSocketRW}, +}; + + +/******************************************************************************* +** +** Function: register_com_android_nfc_NativeLlcpSocket +** +** Description: Regisgter JNI functions with Java Virtual Machine. +** e: Environment of JVM. +** +** Returns: Status of registration. +** +*******************************************************************************/ +int register_com_android_nfc_NativeLlcpSocket (JNIEnv* e) +{ + return jniRegisterNativeMethods (e, gNativeLlcpSocketClassName, gMethods, NELEM(gMethods)); +} + + +} //namespace android + diff --git a/nci/jni/NativeNfcManager.cpp b/nci/jni/NativeNfcManager.cpp new file mode 100755 index 0000000..f15c92f --- /dev/null +++ b/nci/jni/NativeNfcManager.cpp @@ -0,0 +1,1816 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <semaphore.h> +#include <errno.h> +#include "OverrideLog.h" +#include "NfcJniUtil.h" +#include "NfcAdaptation.h" +#include "SyncEvent.h" +#include "PeerToPeer.h" +#include "SecureElement.h" +#include "NfcTag.h" +#include "config.h" +#include "PowerSwitch.h" +#include "JavaClassConstants.h" +#include "Pn544Interop.h" + +extern "C" +{ + #include "nfa_api.h" + #include "nfa_p2p_api.h" + #include "rw_api.h" + #include "nfa_ee_api.h" + #include "nfc_brcm_defs.h" + #include "nfa_cho_api.h" + #include "ce_api.h" +} + +extern UINT8 *p_nfa_dm_lptd_cfg; +extern UINT8 *p_nfa_dm_start_up_cfg; +extern const UINT8 nfca_version_string []; +namespace android +{ + extern bool gIsTagDeactivating; + extern bool gIsSelectingRfInterface; + extern void nativeNfcTag_doTransceiveStatus (uint8_t * buf, uint32_t buflen); + extern void nativeNfcTag_doConnectStatus (jboolean is_connect_ok); + extern void nativeNfcTag_doDeactivateStatus (int status); + extern void nativeNfcTag_doWriteStatus (jboolean is_write_ok); + extern void nativeNfcTag_doCheckNdefResult (tNFA_STATUS status, uint32_t max_size, uint32_t current_size, uint8_t flags); + extern void nativeNfcTag_doMakeReadonlyResult (tNFA_STATUS status); + extern void nativeNfcTag_doPresenceCheckResult (tNFA_STATUS status); + extern void nativeNfcTag_formatStatus (bool is_ok); + extern void nativeNfcTag_resetPresenceCheck (); + extern void nativeNfcTag_doReadCompleted (tNFA_STATUS status); + extern void nativeNfcTag_abortWaits (); + extern void nativeLlcpConnectionlessSocket_abortWait (); + extern void nativeNfcTag_registerNdefTypeHandler (); + extern void nativeLlcpConnectionlessSocket_receiveData (uint8_t* data, uint32_t len, uint32_t remote_sap); +} + + +/***************************************************************************** +** +** public variables and functions +** +*****************************************************************************/ + +namespace android +{ + int gGeneralTransceiveTimeout = 1000; + jmethodID gCachedNfcManagerNotifyNdefMessageListeners; + jmethodID gCachedNfcManagerNotifyTransactionListeners; + jmethodID gCachedNfcManagerNotifyLlcpLinkActivation; + jmethodID gCachedNfcManagerNotifyLlcpLinkDeactivated; + jmethodID gCachedNfcManagerNotifySeFieldActivated; + jmethodID gCachedNfcManagerNotifySeFieldDeactivated; + jmethodID gCachedNfcManagerNotifySeListenActivated; + jmethodID gCachedNfcManagerNotifySeListenDeactivated; + const char* gNativeP2pDeviceClassName = "com/android/nfc/dhimpl/NativeP2pDevice"; + const char* gNativeLlcpServiceSocketClassName = "com/android/nfc/dhimpl/NativeLlcpServiceSocket"; + const char* gNativeLlcpConnectionlessSocketClassName = "com/android/nfc/dhimpl/NativeLlcpConnectionlessSocket"; + const char* gNativeLlcpSocketClassName = "com/android/nfc/dhimpl/NativeLlcpSocket"; + const char* gNativeNfcTagClassName = "com/android/nfc/dhimpl/NativeNfcTag"; + const char* gNativeNfcManagerClassName = "com/android/nfc/dhimpl/NativeNfcManager"; + const char* gNativeNfcSecureElementClassName = "com/android/nfc/dhimpl/NativeNfcSecureElement"; + void doStartupConfig (); + void startStopPolling (bool isStartPolling); + void startRfDiscovery (bool isStart); +} + + +/***************************************************************************** +** +** private variables and functions +** +*****************************************************************************/ +namespace android +{ +static jint sLastError = ERROR_BUFFER_TOO_SMALL; +static jmethodID sCachedNfcManagerNotifySeApduReceived; +static jmethodID sCachedNfcManagerNotifySeMifareAccess; +static jmethodID sCachedNfcManagerNotifySeEmvCardRemoval; +static jmethodID sCachedNfcManagerNotifyTargetDeselected; +static SyncEvent sNfaEnableEvent; //event for NFA_Enable() +static SyncEvent sNfaDisableEvent; //event for NFA_Disable() +static SyncEvent sNfaEnableDisablePollingEvent; //event for NFA_EnablePolling(), NFA_DisablePolling() +static SyncEvent sNfaSetConfigEvent; // event for Set_Config.... +static bool sIsNfaEnabled = false; +static bool sDiscoveryEnabled = false; //is polling for tag? +static bool sIsDisabling = false; +static bool sRfEnabled = false; // whether RF discovery is enabled +static bool sSeRfActive = false; // whether RF with SE is likely active +static bool sP2pActive = false; // whether p2p was last active +static int sConnlessSap = 0; +static int sConnlessLinkMiu = 0; +static bool sAbortConnlessWait = false; +static bool sIsSecElemSelected = false; //has NFC service selected a sec elem +static UINT8 * sOriginalLptdCfg = NULL; +static UINT8 sNewLptdCfg[LPTD_PARAM_LEN]; +static UINT32 sConfigUpdated = 0; +#define CONFIG_UPDATE_LPTD (1 << 0) +#define CONFIG_UPDATE_TECH_MASK (1 << 1) +#define DEFAULT_TECH_MASK (NFA_TECHNOLOGY_MASK_A \ + | NFA_TECHNOLOGY_MASK_B \ + | NFA_TECHNOLOGY_MASK_F \ + | NFA_TECHNOLOGY_MASK_ISO15693 \ + | NFA_TECHNOLOGY_MASK_B_PRIME \ + | NFA_TECHNOLOGY_MASK_A_ACTIVE \ + | NFA_TECHNOLOGY_MASK_F_ACTIVE) + + +static void nfaConnectionCallback (UINT8 event, tNFA_CONN_EVT_DATA *eventData); +static void nfaDeviceManagementCallback (UINT8 event, tNFA_DM_CBACK_DATA *eventData); +static bool isPeerToPeer (tNFA_ACTIVATED& activated); +static bool isListenMode(tNFA_ACTIVATED& activated); + +///////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////// + + +/******************************************************************************* +** +** Function: getNative +** +** Description: Get native data +** +** Returns: Native data structure. +** +*******************************************************************************/ +nfc_jni_native_data *getNative (JNIEnv* e, jobject o) +{ + static struct nfc_jni_native_data *sCachedNat = NULL; + if (e) + { + sCachedNat = nfc_jni_get_nat(e, o); + } + return sCachedNat; +} + + +/******************************************************************************* +** +** Function: handleRfDiscoveryEvent +** +** Description: Handle RF-discovery events from the stack. +** discoveredDevice: Discovered device. +** +** Returns: None +** +*******************************************************************************/ +static void handleRfDiscoveryEvent (tNFC_RESULT_DEVT* discoveredDevice) +{ + if (discoveredDevice->more) + { + //there is more discovery notification coming + return; + } + + bool isP2p = NfcTag::getInstance ().isP2pDiscovered (); + if (isP2p) + { + //select the peer that supports P2P + NfcTag::getInstance ().selectP2p(); + } + else + { + //select the first of multiple tags that is discovered + NfcTag::getInstance ().selectFirstTag(); + } +} + + +/******************************************************************************* +** +** Function: nfaConnectionCallback +** +** Description: Receive connection-related events from stack. +** connEvent: Event code. +** eventData: Event data. +** +** Returns: None +** +*******************************************************************************/ +static void nfaConnectionCallback (UINT8 connEvent, tNFA_CONN_EVT_DATA* eventData) +{ + tNFA_STATUS status = NFA_STATUS_FAILED; + ALOGD("%s: event= %u", __FUNCTION__, connEvent); + + if (gIsTagDeactivating && connEvent != NFA_DEACTIVATED_EVT && connEvent != NFA_PRESENCE_CHECK_EVT && connEvent != NFA_DATA_EVT) + { + // special case to switching frame interface for ISO_DEP tags + gIsTagDeactivating = false; + ALOGD("%s: deactivating, should get NFA_DEACTIVATED_EVT", __FUNCTION__); + nativeNfcTag_doDeactivateStatus(1); + } + + switch (connEvent) + { + case NFA_POLL_ENABLED_EVT: // whether polling successfully started + { + ALOGD("%s: NFA_POLL_ENABLED_EVT: status = %u", __FUNCTION__, eventData->status); + + SyncEventGuard guard (sNfaEnableDisablePollingEvent); + sNfaEnableDisablePollingEvent.notifyOne (); + } + break; + + case NFA_POLL_DISABLED_EVT: // Listening/Polling stopped + { + ALOGD("%s: NFA_POLL_DISABLED_EVT: status = %u", __FUNCTION__, eventData->status); + + SyncEventGuard guard (sNfaEnableDisablePollingEvent); + sNfaEnableDisablePollingEvent.notifyOne (); + } + break; + + case NFA_RF_DISCOVERY_STARTED_EVT: // RF Discovery started + { + ALOGD("%s: NFA_RF_DISCOVERY_STARTED_EVT: status = %u", __FUNCTION__, eventData->status); + + SyncEventGuard guard (sNfaEnableDisablePollingEvent); + sNfaEnableDisablePollingEvent.notifyOne (); + } + break; + + case NFA_RF_DISCOVERY_STOPPED_EVT: // RF Discovery stopped event + { + ALOGD("%s: NFA_RF_DISCOVERY_STOPPED_EVT: status = %u", __FUNCTION__, eventData->status); + + SyncEventGuard guard (sNfaEnableDisablePollingEvent); + sNfaEnableDisablePollingEvent.notifyOne (); + } + break; + + case NFA_DISC_RESULT_EVT: // NFC link/protocol discovery notificaiton + status = eventData->disc_result.status; + ALOGD("%s: NFA_DISC_RESULT_EVT: status = %d", __FUNCTION__, status); + if (status != NFA_STATUS_OK) + { + ALOGE("%s: NFA_DISC_RESULT_EVT error: status = %d", __FUNCTION__, status); + } + else + { + NfcTag::getInstance().connectionEventHandler(connEvent, eventData); + handleRfDiscoveryEvent(&eventData->disc_result.discovery_ntf); + } + break; + + case NFA_SELECT_RESULT_EVT: // NFC link/protocol discovery select response + ALOGD("%s: NFA_SELECT_RESULT_EVT: status = %d, gIsSelectingRfInterface = %d, sIsDisabling=%d", __FUNCTION__, eventData->status, gIsSelectingRfInterface, sIsDisabling); + + if (sIsDisabling) + break; + + if (eventData->status != NFA_STATUS_OK) + { + if (gIsSelectingRfInterface) + { + nativeNfcTag_doConnectStatus(false); + } + + ALOGE("%s: NFA_SELECT_RESULT_EVT error: status = %d", __FUNCTION__, eventData->status); + NFA_Deactivate (FALSE); + } + break; + + case NFA_DEACTIVATE_FAIL_EVT: + ALOGD("%s: NFA_DEACTIVATE_FAIL_EVT: status = %d", __FUNCTION__, eventData->status); + break; + + case NFA_ACTIVATED_EVT: // NFC link/protocol activated + ALOGD("%s: NFA_ACTIVATED_EVT: gIsSelectingRfInterface=%d, sIsDisabling=%d", __FUNCTION__, gIsSelectingRfInterface, sIsDisabling); + if (sIsDisabling) + break; + + NfcTag::getInstance().setActivationState (); + if (gIsSelectingRfInterface) + { + nativeNfcTag_doConnectStatus(true); + break; + } + + nativeNfcTag_resetPresenceCheck(); + if (isPeerToPeer(eventData->activated)) + { + sP2pActive = true; + ALOGD("%s: NFA_ACTIVATED_EVT; is p2p", __FUNCTION__); + // Disable RF field events in case of p2p + UINT8 nfa_disable_rf_events[] = { 0x00 }; + ALOGD ("%s: Disabling RF field events", __FUNCTION__); + status = NFA_SetConfig(NCI_PARAM_ID_RF_FIELD_INFO, sizeof(nfa_disable_rf_events), + &nfa_disable_rf_events[0]); + if (status == NFA_STATUS_OK) { + ALOGD ("%s: Disabled RF field events", __FUNCTION__); + } else { + ALOGE ("%s: Failed to disable RF field events", __FUNCTION__); + } + } + else if (pn544InteropIsBusy() == false) + { + NfcTag::getInstance().connectionEventHandler (connEvent, eventData); + + // We know it is not activating for P2P. If it activated in + // listen mode then it is likely for and SE transaction. + // Send the RF Event. + if (isListenMode(eventData->activated)) + { + sSeRfActive = true; + SecureElement::getInstance().notifyListenModeState (true); + } + } + + break; + + case NFA_DEACTIVATED_EVT: // NFC link/protocol deactivated + ALOGD("%s: NFA_DEACTIVATED_EVT Type: %u, gIsTagDeactivating: %d", __FUNCTION__, eventData->deactivated.type,gIsTagDeactivating); + NfcTag::getInstance().setDeactivationState (eventData->deactivated); + if (eventData->deactivated.type != NFA_DEACTIVATE_TYPE_SLEEP) + { + nativeNfcTag_resetPresenceCheck(); + NfcTag::getInstance().connectionEventHandler (connEvent, eventData); + nativeNfcTag_abortWaits(); + NfcTag::getInstance().abort (); + } + else if (gIsTagDeactivating) + { + nativeNfcTag_doDeactivateStatus(0); + } + + // If RF is activated for what we think is a Secure Element transaction + // and it is deactivated to either IDLE or DISCOVERY mode, notify w/event. + if ((eventData->deactivated.type == NFA_DEACTIVATE_TYPE_IDLE) + || (eventData->deactivated.type == NFA_DEACTIVATE_TYPE_DISCOVERY)) + { + if (sSeRfActive) { + sSeRfActive = false; + SecureElement::getInstance().notifyListenModeState (false); + } else if (sP2pActive) { + sP2pActive = false; + // Make sure RF field events are re-enabled + ALOGD("%s: NFA_ACTIVATED_EVT; is p2p", __FUNCTION__); + // Disable RF field events in case of p2p + UINT8 nfa_enable_rf_events[] = { 0x01 }; + + ALOGD ("%s: Enabling RF field events", __FUNCTION__); + status = NFA_SetConfig(NCI_PARAM_ID_RF_FIELD_INFO, sizeof(nfa_enable_rf_events), + &nfa_enable_rf_events[0]); + if (status == NFA_STATUS_OK) { + ALOGD ("%s: Enabled RF field events", __FUNCTION__); + } else { + ALOGE ("%s: Failed to enable RF field events", __FUNCTION__); + } + } + } + + break; + + case NFA_TLV_DETECT_EVT: // TLV Detection complete + status = eventData->tlv_detect.status; + ALOGD("%s: NFA_TLV_DETECT_EVT: status = %d, protocol = %d, num_tlvs = %d, num_bytes = %d", + __FUNCTION__, status, eventData->tlv_detect.protocol, + eventData->tlv_detect.num_tlvs, eventData->tlv_detect.num_bytes); + if (status != NFA_STATUS_OK) + { + ALOGE("%s: NFA_TLV_DETECT_EVT error: status = %d", __FUNCTION__, status); + } + break; + + case NFA_NDEF_DETECT_EVT: // NDEF Detection complete; + //if status is failure, it means the tag does not contain any or valid NDEF data; + //pass the failure status to the NFC Service; + status = eventData->ndef_detect.status; + ALOGD("%s: NFA_NDEF_DETECT_EVT: status = 0x%X, protocol = %u, " + "max_size = %lu, cur_size = %lu, flags = 0x%X", __FUNCTION__, + status, + eventData->ndef_detect.protocol, eventData->ndef_detect.max_size, + eventData->ndef_detect.cur_size, eventData->ndef_detect.flags); + NfcTag::getInstance().connectionEventHandler (connEvent, eventData); + nativeNfcTag_doCheckNdefResult(status, + eventData->ndef_detect.max_size, eventData->ndef_detect.cur_size, + eventData->ndef_detect.flags); + break; + + case NFA_DATA_EVT: // Data message received (for non-NDEF reads) + ALOGD("%s: NFA_DATA_EVT: len = %d", __FUNCTION__, eventData->data.len); + nativeNfcTag_doTransceiveStatus(eventData->data.p_data,eventData->data.len); + break; + + case NFA_SELECT_CPLT_EVT: // Select completed + status = eventData->status; + ALOGD("%s: NFA_SELECT_CPLT_EVT: status = %d", __FUNCTION__, status); + if (status != NFA_STATUS_OK) + { + ALOGE("%s: NFA_SELECT_CPLT_EVT error: status = %d", __FUNCTION__, status); + } + break; + + case NFA_READ_CPLT_EVT: // NDEF-read or tag-specific-read completed + ALOGD("%s: NFA_READ_CPLT_EVT: status = 0x%X", __FUNCTION__, eventData->status); + nativeNfcTag_doReadCompleted (eventData->status); + NfcTag::getInstance().connectionEventHandler (connEvent, eventData); + break; + + case NFA_WRITE_CPLT_EVT: // Write completed + ALOGD("%s: NFA_WRITE_CPLT_EVT: status = %d", __FUNCTION__, eventData->status); + nativeNfcTag_doWriteStatus (eventData->status == NFA_STATUS_OK); + break; + + case NFA_SET_TAG_RO_EVT: // Tag set as Read only + ALOGD("%s: NFA_SET_TAG_RO_EVT: status = %d", __FUNCTION__, eventData->status); + nativeNfcTag_doMakeReadonlyResult(eventData->status); + break; + + case NFA_CE_NDEF_WRITE_START_EVT: // NDEF write started + ALOGD("%s: NFA_CE_NDEF_WRITE_START_EVT: status: %d", __FUNCTION__, eventData->status); + + if (eventData->status != NFA_STATUS_OK) + ALOGE("%s: NFA_CE_NDEF_WRITE_START_EVT error: status = %d", __FUNCTION__, eventData->status); + break; + + case NFA_CE_NDEF_WRITE_CPLT_EVT: // NDEF write completed + ALOGD("%s: FA_CE_NDEF_WRITE_CPLT_EVT: len = %lu", __FUNCTION__, eventData->ndef_write_cplt.len); + break; + + case NFA_LLCP_ACTIVATED_EVT: // LLCP link is activated + ALOGD("%s: NFA_LLCP_ACTIVATED_EVT: is_initiator: %d remote_wks: %d, remote_lsc: %d, remote_link_miu: %d, local_link_miu: %d", + __FUNCTION__, + eventData->llcp_activated.is_initiator, + eventData->llcp_activated.remote_wks, + eventData->llcp_activated.remote_lsc, + eventData->llcp_activated.remote_link_miu, + eventData->llcp_activated.local_link_miu); + + PeerToPeer::getInstance().llcpActivatedHandler (getNative(0, 0), eventData->llcp_activated); + break; + + case NFA_LLCP_DEACTIVATED_EVT: // LLCP link is deactivated + ALOGD("%s: NFA_LLCP_DEACTIVATED_EVT", __FUNCTION__); + PeerToPeer::getInstance().llcpDeactivatedHandler (getNative(0, 0), eventData->llcp_deactivated); + break; + + case NFA_PRESENCE_CHECK_EVT: + ALOGD("%s: NFA_PRESENCE_CHECK_EVT", __FUNCTION__); + nativeNfcTag_doPresenceCheckResult (eventData->status); + break; + + case NFA_FORMAT_CPLT_EVT: + ALOGD("%s: NFA_FORMAT_CPLT_EVT: status=0x%X", __FUNCTION__, eventData->status); + nativeNfcTag_formatStatus (eventData->status == NFA_STATUS_OK); + break; + + case NFA_I93_CMD_CPLT_EVT: + ALOGD("%s: NFA_I93_CMD_CPLT_EVT: status=0x%X", __FUNCTION__, eventData->status); + break; + + case NFA_CE_UICC_LISTEN_CONFIGURED_EVT : + ALOGD("%s: NFA_CE_UICC_LISTEN_CONFIGURED_EVT : status=0x%X", __FUNCTION__, eventData->status); + SecureElement::getInstance().connectionEventHandler (connEvent, eventData); + break; + + case NFA_SET_P2P_LISTEN_TECH_EVT: + ALOGD("%s: NFA_SET_P2P_LISTEN_TECH_EVT", __FUNCTION__); + PeerToPeer::getInstance().connectionEventHandler (connEvent, eventData); + break; + + default: + ALOGE("%s: unknown event ????", __FUNCTION__); + break; + } +} + + +/******************************************************************************* +** +** Function: nfcManager_initNativeStruc +** +** Description: Initialize variables. +** e: JVM environment. +** o: Java object. +** +** Returns: True if ok. +** +*******************************************************************************/ +static jboolean nfcManager_initNativeStruc (JNIEnv* e, jobject o) +{ + nfc_jni_native_data* nat = NULL; + jclass cls = NULL; + jobject obj = NULL; + jfieldID f = 0; + + ALOGD ("%s: enter", __FUNCTION__); + + nat = (nfc_jni_native_data*)malloc(sizeof(struct nfc_jni_native_data)); + if (nat == NULL) + { + ALOGE ("%s: fail allocate native data", __FUNCTION__); + return JNI_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 */ + gCachedNfcManagerNotifyNdefMessageListeners = e->GetMethodID (cls, + "notifyNdefMessageListeners", "(Lcom/android/nfc/dhimpl/NativeNfcTag;)V"); + gCachedNfcManagerNotifyTransactionListeners = e->GetMethodID (cls, + "notifyTransactionListeners", "([B)V"); + gCachedNfcManagerNotifyLlcpLinkActivation = e->GetMethodID (cls, + "notifyLlcpLinkActivation", "(Lcom/android/nfc/dhimpl/NativeP2pDevice;)V"); + gCachedNfcManagerNotifyLlcpLinkDeactivated = e->GetMethodID (cls, + "notifyLlcpLinkDeactivated", "(Lcom/android/nfc/dhimpl/NativeP2pDevice;)V"); + sCachedNfcManagerNotifyTargetDeselected = e->GetMethodID (cls, + "notifyTargetDeselected","()V"); + gCachedNfcManagerNotifySeFieldActivated = e->GetMethodID (cls, + "notifySeFieldActivated", "()V"); + gCachedNfcManagerNotifySeFieldDeactivated = e->GetMethodID (cls, + "notifySeFieldDeactivated", "()V"); + gCachedNfcManagerNotifySeListenActivated = e->GetMethodID (cls, + "notifySeListenActivated", "()V"); + gCachedNfcManagerNotifySeListenDeactivated = e->GetMethodID (cls, + "notifySeListenDeactivated", "()V"); + + sCachedNfcManagerNotifySeApduReceived = e->GetMethodID(cls, + "notifySeApduReceived", "([B)V"); + + sCachedNfcManagerNotifySeMifareAccess = e->GetMethodID(cls, + "notifySeMifareAccess", "([B)V"); + + sCachedNfcManagerNotifySeEmvCardRemoval = e->GetMethodID(cls, + "notifySeEmvCardRemoval", "()V"); + + if (nfc_jni_cache_object(e,gNativeNfcTagClassName, &(nat->cached_NfcTag)) == -1) + { + ALOGE ("%s: fail cache NativeNfcTag", __FUNCTION__); + return JNI_FALSE; + } + + if (nfc_jni_cache_object(e,gNativeP2pDeviceClassName, &(nat->cached_P2pDevice)) == -1) + { + ALOGE ("%s: fail cache NativeP2pDevice", __FUNCTION__); + return JNI_FALSE; + } + + ALOGD ("%s: exit", __FUNCTION__); + return JNI_TRUE; +} + + +/******************************************************************************* +** +** Function: nfaDeviceManagementCallback +** +** Description: Receive device management events from stack. +** dmEvent: Device-management event ID. +** eventData: Data associated with event ID. +** +** Returns: None +** +*******************************************************************************/ +void nfaDeviceManagementCallback (UINT8 dmEvent, tNFA_DM_CBACK_DATA* eventData) +{ + ALOGD ("%s: enter; event=0x%X", __FUNCTION__, dmEvent); + + switch (dmEvent) + { + case NFA_DM_ENABLE_EVT: /* Result of NFA_Enable */ + { + SyncEventGuard guard (sNfaEnableEvent); + ALOGD ("%s: NFA_DM_ENABLE_EVT; status=0x%X", + __FUNCTION__, eventData->status); + sIsNfaEnabled = eventData->status == NFA_STATUS_OK; + sIsDisabling = false; + sNfaEnableEvent.notifyOne (); + } + break; + + case NFA_DM_DISABLE_EVT: /* Result of NFA_Disable */ + { + SyncEventGuard guard (sNfaDisableEvent); + ALOGD ("%s: NFA_DM_DISABLE_EVT", __FUNCTION__); + sIsNfaEnabled = false; + sIsDisabling = false; + sNfaDisableEvent.notifyOne (); + } + break; + + case NFA_DM_SET_CONFIG_EVT: //result of NFA_SetConfig + ALOGD ("%s: NFA_DM_SET_CONFIG_EVT", __FUNCTION__); + { + SyncEventGuard guard (sNfaSetConfigEvent); + sNfaSetConfigEvent.notifyOne(); + } + break; + + case NFA_DM_GET_CONFIG_EVT: /* Result of NFA_GetConfig */ + ALOGD ("%s: NFA_DM_GET_CONFIG_EVT", __FUNCTION__); + break; + + case NFA_DM_RF_FIELD_EVT: + ALOGD ("%s: NFA_DM_RF_FIELD_EVT; status=0x%X; field status=%u", __FUNCTION__, + eventData->rf_field.status, eventData->rf_field.rf_field_status); + + if (!sIsDisabling && eventData->rf_field.status == NFA_STATUS_OK) + SecureElement::getInstance().notifyRfFieldEvent (eventData->rf_field.rf_field_status == NFA_DM_RF_FIELD_ON); + break; + + case NFA_DM_NFCC_TRANSPORT_ERR_EVT: + case NFA_DM_NFCC_TIMEOUT_EVT: + { + if (dmEvent == NFA_DM_NFCC_TIMEOUT_EVT) + ALOGD ("%s: NFA_DM_NFCC_TIMEOUT_EVT; abort all outstanding operations", __FUNCTION__); + else + ALOGD ("%s: NFA_DM_NFCC_TRANSPORT_ERR_EVT; abort all outstanding operations", __FUNCTION__); + + nativeNfcTag_abortWaits(); + NfcTag::getInstance().abort (); + sAbortConnlessWait = true; + nativeLlcpConnectionlessSocket_abortWait(); + { + ALOGD ("%s: aborting sNfaEnableDisablePollingEvent", __FUNCTION__); + SyncEventGuard guard (sNfaEnableDisablePollingEvent); + sNfaEnableDisablePollingEvent.notifyOne(); + } + { + ALOGD ("%s: aborting sNfaEnableEvent", __FUNCTION__); + SyncEventGuard guard (sNfaEnableEvent); + sNfaEnableEvent.notifyOne(); + } + { + ALOGD ("%s: aborting sNfaDisableEvent", __FUNCTION__); + SyncEventGuard guard (sNfaDisableEvent); + sNfaDisableEvent.notifyOne(); + } + sDiscoveryEnabled = false; + PowerSwitch::getInstance ().abort (); + + if (!sIsDisabling && sIsNfaEnabled) + { + NFA_Disable(FALSE); + sIsDisabling = true; + } + else + { + sIsNfaEnabled = false; + sIsDisabling = false; + } + PowerSwitch::getInstance ().initialize (PowerSwitch::UNKNOWN_LEVEL); + ALOGD ("%s: aborted all waiting events", __FUNCTION__); + } + break; + + case NFA_DM_PWR_MODE_CHANGE_EVT: + PowerSwitch::getInstance ().deviceManagementCallback (dmEvent, eventData); + break; + + default: + ALOGD ("%s: unhandled event", __FUNCTION__); + break; + } +} + + +/******************************************************************************* +** +** Function: nfcManager_doInitialize +** +** Description: Turn on NFC. +** e: JVM environment. +** o: Java object. +** +** Returns: True if ok. +** +*******************************************************************************/ +static jboolean nfcManager_doInitialize (JNIEnv* e, jobject o) +{ + ALOGD ("%s: enter; NCI_VERSION=0x%02X", __FUNCTION__, NCI_VERSION); + tNFA_STATUS stat = NFA_STATUS_OK; + + if (sIsNfaEnabled) + { + ALOGD ("%s: already enabled", __FUNCTION__); + goto TheEnd; + } + + PowerSwitch::getInstance ().initialize (PowerSwitch::FULL_POWER); + + { + unsigned long num = 0; + + NfcAdaptation& theInstance = NfcAdaptation::GetInstance(); + theInstance.Initialize(); //start GKI, NCI task, NFC task + + { + SyncEventGuard guard (sNfaEnableEvent); + tHAL_NFC_ENTRY* halFuncEntries = theInstance.GetHalEntryFuncs (); + + NFA_Init (halFuncEntries); + + stat = NFA_Enable (nfaDeviceManagementCallback, nfaConnectionCallback); + if (stat == NFA_STATUS_OK) + { + num = initializeGlobalAppLogLevel (); + CE_SetTraceLevel (num); + LLCP_SetTraceLevel (num); + NFC_SetTraceLevel (num); + RW_SetTraceLevel (num); + NFA_SetTraceLevel (num); + NFA_ChoSetTraceLevel (num); + NFA_P2pSetTraceLevel (num); + NFA_SnepSetTraceLevel (num); + sNfaEnableEvent.wait(); //wait for NFA command to finish + } + } + + if (stat == NFA_STATUS_OK) + { + //sIsNfaEnabled indicates whether stack started successfully + if (sIsNfaEnabled) + { + SecureElement::getInstance().initialize (getNative(e, o)); + nativeNfcTag_registerNdefTypeHandler (); + NfcTag::getInstance().initialize (getNative(e, o)); + PeerToPeer::getInstance().initialize (); + PeerToPeer::getInstance().handleNfcOnOff (true); + + ///////////////////////////////////////////////////////////////////////////////// + // Add extra configuration here (work-arounds, etc.) + + struct nfc_jni_native_data *nat = getNative(e, o); + + if ( nat ) + { + if (GetNumValue(NAME_POLLING_TECH_MASK, &num, sizeof(num))) + nat->tech_mask = num; + else + nat->tech_mask = DEFAULT_TECH_MASK; + + ALOGD ("%s: tag polling tech mask=0x%X", __FUNCTION__, nat->tech_mask); + } + + // Always restore LPTD Configuration to the stack default. + if (sOriginalLptdCfg != NULL) + p_nfa_dm_lptd_cfg = sOriginalLptdCfg; + + // if this value exists, set polling interval. + if (GetNumValue(NAME_NFA_DM_DISC_DURATION_POLL, &num, sizeof(num))) + NFA_SetRfDiscoveryDuration(num); + + // Do custom NFCA startup configuration. + doStartupConfig(); + goto TheEnd; + } + } + + ALOGE ("%s: fail nfa enable; error=0x%X", __FUNCTION__, stat); + + if (sIsNfaEnabled) + stat = NFA_Disable (FALSE /* ungraceful */); + + theInstance.Finalize(); + } + +TheEnd: + if (sIsNfaEnabled) + PowerSwitch::getInstance ().setLevel (PowerSwitch::LOW_POWER); + ALOGD ("%s: exit", __FUNCTION__); + return sIsNfaEnabled ? JNI_TRUE : JNI_FALSE; +} + + +/******************************************************************************* +** +** Function: nfcManager_enableDiscovery +** +** Description: Start polling and listening for devices. +** e: JVM environment. +** o: Java object. +** mode: Not used. +** +** Returns: None +** +*******************************************************************************/ +static void nfcManager_enableDiscovery (JNIEnv* e, jobject o) +{ + tNFA_TECHNOLOGY_MASK tech_mask = DEFAULT_TECH_MASK; + struct nfc_jni_native_data *nat = getNative(e, o); + + if (nat) + tech_mask = (tNFA_TECHNOLOGY_MASK)nat->tech_mask; + + ALOGD ("%s: enter; tech_mask = %02x", __FUNCTION__, tech_mask); + + if (sDiscoveryEnabled) + { + ALOGE ("%s: already polling", __FUNCTION__); + return; + } + + tNFA_STATUS stat = NFA_STATUS_OK; + + ALOGD ("%s: sIsSecElemSelected=%u", __FUNCTION__, sIsSecElemSelected); + + PowerSwitch::getInstance ().setLevel (PowerSwitch::FULL_POWER); + + if (sRfEnabled) { + // Stop RF discovery to reconfigure + startRfDiscovery(false); + } + + { + SyncEventGuard guard (sNfaEnableDisablePollingEvent); + stat = NFA_EnablePolling (tech_mask); + if (stat == NFA_STATUS_OK) + { + ALOGD ("%s: wait for enable event", __FUNCTION__); + sDiscoveryEnabled = true; + sNfaEnableDisablePollingEvent.wait (); //wait for NFA_POLL_ENABLED_EVT + ALOGD ("%s: got enabled event", __FUNCTION__); + } + else + { + ALOGE ("%s: fail enable discovery; error=0x%X", __FUNCTION__, stat); + } + } + + // Start P2P listening if tag polling was enabled or the mask was 0. + if (sDiscoveryEnabled || (tech_mask == 0)) + { + ALOGD ("%s: Enable p2pListening", __FUNCTION__); + PeerToPeer::getInstance().enableP2pListening (true); + + //if NFC service has deselected the sec elem, then apply default routes + if (!sIsSecElemSelected) + stat = SecureElement::getInstance().routeToDefault (); + } + + // Actually start discovery. + startRfDiscovery (true); + + PowerSwitch::getInstance ().setModeOn (PowerSwitch::DISCOVERY); + + ALOGD ("%s: exit", __FUNCTION__); +} + + +/******************************************************************************* +** +** Function: nfcManager_disableDiscovery +** +** Description: Stop polling and listening for devices. +** e: JVM environment. +** o: Java object. +** +** Returns: None +** +*******************************************************************************/ +void nfcManager_disableDiscovery (JNIEnv* e, jobject o) +{ + tNFA_STATUS status = NFA_STATUS_OK; + ALOGD ("%s: enter;", __FUNCTION__); + + pn544InteropAbortNow (); + if (sDiscoveryEnabled == false) + { + ALOGD ("%s: already disabled", __FUNCTION__); + goto TheEnd; + } + + // Stop RF Discovery. + startRfDiscovery (false); + + if (sDiscoveryEnabled) + { + SyncEventGuard guard (sNfaEnableDisablePollingEvent); + status = NFA_DisablePolling (); + if (status == NFA_STATUS_OK) + { + sDiscoveryEnabled = false; + sNfaEnableDisablePollingEvent.wait (); //wait for NFA_POLL_DISABLED_EVT + } + else + ALOGE ("%s: Failed to disable polling; error=0x%X", __FUNCTION__, status); + } + + PeerToPeer::getInstance().enableP2pListening (false); + + //if nothing is active after this, then tell the controller to power down + if (! PowerSwitch::getInstance ().setModeOff (PowerSwitch::DISCOVERY)) + PowerSwitch::getInstance ().setLevel (PowerSwitch::LOW_POWER); + +TheEnd: + ALOGD ("%s: exit", __FUNCTION__); +} + +/******************************************************************************* +** +** Function nfc_jni_cache_object_local +** +** Description Allocates a java object and calls it's constructor +** +** Returns -1 on failure, 0 on success +** +*******************************************************************************/ +int nfc_jni_cache_object_local (JNIEnv *e, const char *className, jobject *cachedObj) +{ + jclass cls = NULL; + jobject obj = NULL; + jmethodID ctor = 0; + + cls = e->FindClass (className); + if(cls == NULL) + { + ALOGE ("%s: find class error", __FUNCTION__); + return -1; + } + + ctor = e->GetMethodID (cls, "<init>", "()V"); + obj = e->NewObject (cls, ctor); + if (obj == NULL) + { + ALOGE ("%s: create object error", __FUNCTION__); + return -1; + } + + *cachedObj = e->NewLocalRef (obj); + if (*cachedObj == NULL) + { + e->DeleteLocalRef (obj); + ALOGE ("%s: global ref error", __FUNCTION__); + return -1; + } + e->DeleteLocalRef (obj); + return 0; +} + + +/******************************************************************************* +** +** Function: nfcManager_doCreateLlcpServiceSocket +** +** Description: Create a new LLCP server socket. +** e: JVM environment. +** o: Java object. +** nSap: Service access point. +** sn: Service name +** miu: Maximum information unit. +** rw: Receive window size. +** linearBufferLength: Max buffer size. +** +** Returns: NativeLlcpServiceSocket Java object. +** +*******************************************************************************/ +static jobject nfcManager_doCreateLlcpServiceSocket (JNIEnv* e, jobject o, jint nSap, jstring sn, jint miu, jint rw, jint linearBufferLength) +{ + bool stat = false; + jobject serviceSocket = NULL; + jclass clsNativeLlcpServiceSocket = NULL; + jfieldID f = 0; + PeerToPeer::tJNI_HANDLE jniHandle = PeerToPeer::getInstance().getNewJniHandle (); + const char* serviceName = e->GetStringUTFChars (sn, JNI_FALSE); //convert jstring, which is unicode, into char* + std::string serviceName2 (serviceName); + + e->ReleaseStringUTFChars (sn, serviceName); //free the string + ALOGD ("%s: enter: sap=%i; name=%s; miu=%i; rw=%i; buffLen=%i", __FUNCTION__, nSap, serviceName2.c_str(), miu, rw, linearBufferLength); + + /* Create new NativeLlcpServiceSocket object */ + if (nfc_jni_cache_object(e, gNativeLlcpServiceSocketClassName, &(serviceSocket)) == -1) + { + ALOGE ("%s: Llcp socket object creation error", __FUNCTION__); + return NULL; + } + + /* Get NativeLlcpServiceSocket class object */ + clsNativeLlcpServiceSocket = e->GetObjectClass (serviceSocket); + if (e->ExceptionCheck()) + { + e->ExceptionClear(); + ALOGE("%s: Llcp Socket get object class error", __FUNCTION__); + return NULL; + } + + if (!PeerToPeer::getInstance().registerServer (jniHandle, serviceName2.c_str())) + { + ALOGE("%s: RegisterServer error", __FUNCTION__); + return NULL; + } + + /* Set socket handle to be the same as the NfaHandle*/ + f = e->GetFieldID (clsNativeLlcpServiceSocket, "mHandle", "I"); + e->SetIntField (serviceSocket, f, (jint) jniHandle); + ALOGD ("%s: socket Handle = 0x%X", __FUNCTION__, jniHandle); + + /* Set socket linear buffer length */ + f = e->GetFieldID (clsNativeLlcpServiceSocket, "mLocalLinearBufferLength", "I"); + e->SetIntField (serviceSocket, f,(jint)linearBufferLength); + ALOGD ("%s: buffer length = %d", __FUNCTION__, linearBufferLength); + + /* Set socket MIU */ + f = e->GetFieldID (clsNativeLlcpServiceSocket, "mLocalMiu", "I"); + e->SetIntField (serviceSocket, f,(jint)miu); + ALOGD ("%s: MIU = %d", __FUNCTION__, miu); + + /* Set socket RW */ + f = e->GetFieldID (clsNativeLlcpServiceSocket, "mLocalRw", "I"); + e->SetIntField (serviceSocket, f,(jint)rw); + ALOGD ("%s: RW = %d", __FUNCTION__, rw); + + sLastError = 0; + ALOGD ("%s: exit", __FUNCTION__); + return serviceSocket; +} + + +/******************************************************************************* +** +** Function: nfcManager_doGetLastError +** +** Description: Get the last error code. +** e: JVM environment. +** o: Java object. +** +** Returns: Last error code. +** +*******************************************************************************/ +static jint nfcManager_doGetLastError(JNIEnv* e, jobject o) +{ + ALOGD ("%s: last error=%i", __FUNCTION__, sLastError); + return sLastError; +} + + +/******************************************************************************* +** +** Function: nfcManager_doDeinitialize +** +** Description: Turn off NFC. +** e: JVM environment. +** o: Java object. +** +** Returns: True if ok. +** +*******************************************************************************/ +static jboolean nfcManager_doDeinitialize (JNIEnv* e, jobject o) +{ + ALOGD ("%s: enter", __FUNCTION__); + + sIsDisabling = true; + pn544InteropAbortNow (); + SecureElement::getInstance().finalize (); + + if (sIsNfaEnabled) + { + SyncEventGuard guard (sNfaDisableEvent); + tNFA_STATUS stat = NFA_Disable (TRUE /* graceful */); + if (stat == NFA_STATUS_OK) + { + ALOGD ("%s: wait for completion", __FUNCTION__); + sNfaDisableEvent.wait (); //wait for NFA command to finish + PeerToPeer::getInstance ().handleNfcOnOff (false); + } + else + { + ALOGE ("%s: fail disable; error=0x%X", __FUNCTION__, stat); + } + } + nativeNfcTag_abortWaits(); + NfcTag::getInstance().abort (); + sAbortConnlessWait = true; + nativeLlcpConnectionlessSocket_abortWait(); + sIsNfaEnabled = false; + sDiscoveryEnabled = false; + sIsDisabling = false; + sIsSecElemSelected = false; + + { + //unblock NFA_EnablePolling() and NFA_DisablePolling() + SyncEventGuard guard (sNfaEnableDisablePollingEvent); + sNfaEnableDisablePollingEvent.notifyOne (); + } + + NfcAdaptation& theInstance = NfcAdaptation::GetInstance(); + theInstance.Finalize(); + + ALOGD ("%s: exit", __FUNCTION__); + return JNI_TRUE; +} + + +/******************************************************************************* +** +** Function: nfcManager_doCreateLlcpSocket +** +** Description: Create a LLCP connection-oriented socket. +** e: JVM environment. +** o: Java object. +** nSap: Service access point. +** miu: Maximum information unit. +** rw: Receive window size. +** linearBufferLength: Max buffer size. +** +** Returns: NativeLlcpSocket Java object. +** +*******************************************************************************/ +static jobject nfcManager_doCreateLlcpSocket (JNIEnv* e, jobject o, jint nSap, jint miu, jint rw, jint linearBufferLength) +{ + ALOGD ("%s: enter; sap=%d; miu=%d; rw=%d; buffer len=%d", __FUNCTION__, nSap, miu, rw, linearBufferLength); + jobject clientSocket = NULL; + jclass clsNativeLlcpSocket; + jfieldID f; + PeerToPeer::tJNI_HANDLE jniHandle = PeerToPeer::getInstance().getNewJniHandle (); + bool stat = false; + + stat = PeerToPeer::getInstance().createClient (jniHandle, miu, rw); + + /* Create new NativeLlcpSocket object */ + if (nfc_jni_cache_object_local(e, gNativeLlcpSocketClassName, &(clientSocket)) == -1) + { + ALOGE ("%s: fail Llcp socket creation", __FUNCTION__); + goto TheEnd; + } + + /* Get NativeConnectionless class object */ + clsNativeLlcpSocket = e->GetObjectClass (clientSocket); + if (e->ExceptionCheck()) + { + e->ExceptionClear(); + ALOGE ("%s: fail get class object", __FUNCTION__); + goto TheEnd; + } + + /* Set socket SAP */ + f = e->GetFieldID (clsNativeLlcpSocket, "mSap", "I"); + e->SetIntField (clientSocket, f, (jint) nSap); + + /* Set socket handle */ + f = e->GetFieldID (clsNativeLlcpSocket, "mHandle", "I"); + e->SetIntField (clientSocket, f, (jint) jniHandle); + + /* Set socket MIU */ + f = e->GetFieldID (clsNativeLlcpSocket, "mLocalMiu", "I"); + e->SetIntField (clientSocket, f, (jint) miu); + + /* Set socket RW */ + f = e->GetFieldID (clsNativeLlcpSocket, "mLocalRw", "I"); + e->SetIntField (clientSocket, f, (jint) rw); + +TheEnd: + ALOGD ("%s: exit", __FUNCTION__); + return clientSocket; +} + + +/******************************************************************************* +** +** Function: nfcManager_doCreateLlcpConnectionlessSocket +** +** Description: Create a connection-less socket. +** e: JVM environment. +** o: Java object. +** nSap: Service access point. +** sn: Service name. +** +** Returns: NativeLlcpConnectionlessSocket Java object. +** +*******************************************************************************/ +static jobject nfcManager_doCreateLlcpConnectionlessSocket (JNIEnv *e, jobject o, jint nSap, jstring sn) +{ + ALOGD ("%s: nSap=0x%X", __FUNCTION__, nSap); + return NULL; +} + + +/******************************************************************************* +** +** Function: nfcManager_doGetSecureElementList +** +** Description: Get a list of secure element handles. +** e: JVM environment. +** o: Java object. +** +** Returns: List of secure element handles. +** +*******************************************************************************/ +static jintArray nfcManager_doGetSecureElementList(JNIEnv *e, jobject o) +{ + ALOGD ("%s", __FUNCTION__); + return SecureElement::getInstance().getListOfEeHandles (e); +} + + +/******************************************************************************* +** +** Function: nfcManager_doSelectSecureElement +** +** Description: NFC controller starts routing data in listen mode. +** e: JVM environment. +** o: Java object. +** +** Returns: None +** +*******************************************************************************/ +static void nfcManager_doSelectSecureElement(JNIEnv *e, jobject o) +{ + ALOGD ("%s: enter", __FUNCTION__); + bool stat = true; + + PowerSwitch::getInstance ().setLevel (PowerSwitch::FULL_POWER); + + if (sRfEnabled) { + // Stop RF Discovery if we were polling + startRfDiscovery (false); + } + + if (sIsSecElemSelected) + { + ALOGD ("%s: already selected", __FUNCTION__); + goto TheEnd; + } + + stat = SecureElement::getInstance().activate (0xABCDEF); + if (stat) + SecureElement::getInstance().routeToSecureElement (); + sIsSecElemSelected = true; + +TheEnd: + startRfDiscovery (true); + + PowerSwitch::getInstance ().setModeOn (PowerSwitch::SE_ROUTING); + + ALOGD ("%s: exit", __FUNCTION__); +} + + +/******************************************************************************* +** +** Function: nfcManager_doDeselectSecureElement +** +** Description: NFC controller stops routing data in listen mode. +** e: JVM environment. +** o: Java object. +** +** Returns: None +** +*******************************************************************************/ +static void nfcManager_doDeselectSecureElement(JNIEnv *e, jobject o) +{ + ALOGD ("%s: enter", __FUNCTION__); + bool stat = false; + + if (! sIsSecElemSelected) + { + ALOGE ("%s: already deselected", __FUNCTION__); + goto TheEnd; + } + + if (PowerSwitch::getInstance ().getLevel() == PowerSwitch::LOW_POWER) + { + ALOGD ("%s: do not deselect while power is OFF", __FUNCTION__); + sIsSecElemSelected = false; + goto TheEnd; + } + + stat = SecureElement::getInstance().routeToDefault (); + sIsSecElemSelected = false; + + //if controller is not routing to sec elems AND there is no pipe connected, + //then turn off the sec elems + if (SecureElement::getInstance().isBusy() == false) + SecureElement::getInstance().deactivate (0xABCDEF); + +TheEnd: + //if nothing is active after this, then tell the controller to power down + if (! PowerSwitch::getInstance ().setModeOff (PowerSwitch::SE_ROUTING)) + PowerSwitch::getInstance ().setLevel (PowerSwitch::LOW_POWER); + + ALOGD ("%s: exit", __FUNCTION__); +} + + +/******************************************************************************* +** +** Function: isPeerToPeer +** +** Description: Whether the activation data indicates the peer supports NFC-DEP. +** activated: Activation data. +** +** Returns: True if the peer supports NFC-DEP. +** +*******************************************************************************/ +static bool isPeerToPeer (tNFA_ACTIVATED& activated) +{ + return activated.activate_ntf.protocol == NFA_PROTOCOL_NFC_DEP; +} + +/******************************************************************************* +** +** Function: isListenMode +** +** Description: Indicates whether the activation data indicates it is +** listen mode. +** +** Returns: True if this listen mode. +** +*******************************************************************************/ +static bool isListenMode(tNFA_ACTIVATED& activated) +{ + return ((NFC_DISCOVERY_TYPE_LISTEN_A == activated.activate_ntf.rf_tech_param.mode) + || (NFC_DISCOVERY_TYPE_LISTEN_B == activated.activate_ntf.rf_tech_param.mode) + || (NFC_DISCOVERY_TYPE_LISTEN_F == activated.activate_ntf.rf_tech_param.mode) + || (NFC_DISCOVERY_TYPE_LISTEN_A_ACTIVE == activated.activate_ntf.rf_tech_param.mode) + || (NFC_DISCOVERY_TYPE_LISTEN_F_ACTIVE == activated.activate_ntf.rf_tech_param.mode) + || (NFC_DISCOVERY_TYPE_LISTEN_ISO15693 == activated.activate_ntf.rf_tech_param.mode) + || (NFC_DISCOVERY_TYPE_LISTEN_B_PRIME == activated.activate_ntf.rf_tech_param.mode)); +} + +/******************************************************************************* +** +** Function: nfcManager_doCheckLlcp +** +** Description: Not used. +** +** Returns: True +** +*******************************************************************************/ +static jboolean nfcManager_doCheckLlcp(JNIEnv *e, jobject o) +{ + ALOGD("%s", __FUNCTION__); + return JNI_TRUE; +} + + +/******************************************************************************* +** +** Function: nfcManager_doActivateLlcp +** +** Description: Not used. +** +** Returns: True +** +*******************************************************************************/ +static jboolean nfcManager_doActivateLlcp(JNIEnv *e, jobject o) +{ + ALOGD("%s", __FUNCTION__); + return JNI_TRUE; +} + + +/******************************************************************************* +** +** Function: nfcManager_doAbort +** +** Description: Not used. +** +** Returns: None +** +*******************************************************************************/ +static void nfcManager_doAbort(JNIEnv *e, jobject o) +{ + ALOGE("%s: abort()", __FUNCTION__); + abort(); +} + + +/******************************************************************************* +** +** Function: nfcManager_doDownload +** +** Description: Not used. +** +** Returns: True +** +*******************************************************************************/ +static jboolean nfcManager_doDownload(JNIEnv *e, jobject o) +{ + ALOGD("%s", __FUNCTION__); + return JNI_TRUE; +} + + +/******************************************************************************* +** +** Function: nfcManager_doResetTimeouts +** +** Description: Not used. +** +** Returns: None +** +*******************************************************************************/ +static void nfcManager_doResetTimeouts(JNIEnv *e, jobject o) +{ + ALOGD ("%s: %d millisec", __FUNCTION__, DEFAULT_GENERAL_TRANS_TIMEOUT); + gGeneralTransceiveTimeout = DEFAULT_GENERAL_TRANS_TIMEOUT; +} + + +/******************************************************************************* +** +** Function: nfcManager_doSetTimeout +** +** Description: Set timeout value. +** e: JVM environment. +** o: Java object. +** timeout: Timeout value. +** +** Returns: True if ok. +** +*******************************************************************************/ +static bool nfcManager_doSetTimeout(JNIEnv *e, jobject o, jint tech, jint timeout) +{ + if (timeout <= 0) + { + ALOGE("%s: Timeout must be positive.",__FUNCTION__); + return false; + } + + ALOGD ("%s: timeout=%d", __FUNCTION__, timeout); + gGeneralTransceiveTimeout = timeout; + return true; +} + + +/******************************************************************************* +** +** Function: nfcManager_doGetTimeout +** +** Description: Get timeout value. +** e: JVM environment. +** o: Java object. +** tech: Not used. +** +** Returns: Timeout value. +** +*******************************************************************************/ +static jint nfcManager_doGetTimeout(JNIEnv *e, jobject o, jint tech) +{ + ALOGD ("%s: timeout=%d", __FUNCTION__, gGeneralTransceiveTimeout); + return gGeneralTransceiveTimeout; +} + + +/******************************************************************************* +** +** Function: nfcManager_doDump +** +** Description: Not used. +** e: JVM environment. +** o: Java object. +** +** Returns: Text dump. +** +*******************************************************************************/ +static jstring nfcManager_doDump(JNIEnv *e, jobject o) +{ + char buffer[100]; + snprintf(buffer, sizeof(buffer), "libnfc llc error_count=%u", /*libnfc_llc_error_count*/ 0); + return e->NewStringUTF(buffer); +} + + +/******************************************************************************* +** +** Function: nfcManager_doSetP2pInitiatorModes +** +** Description: Set P2P initiator's activation modes. +** e: JVM environment. +** o: Java object. +** modes: Active and/or passive modes. The values are specified +** in external/libnfc-nxp/inc/phNfcTypes.h. See +** enum phNfc_eP2PMode_t. +** +** Returns: None. +** +*******************************************************************************/ +static void nfcManager_doSetP2pInitiatorModes (JNIEnv *e, jobject o, jint modes) +{ + ALOGD ("%s: modes=0x%X", __FUNCTION__, modes); + struct nfc_jni_native_data *nat = getNative(e, o); + + tNFA_TECHNOLOGY_MASK mask = 0; + if (modes & 0x01) mask |= NFA_TECHNOLOGY_MASK_A; + if (modes & 0x02) mask |= NFA_TECHNOLOGY_MASK_F; + if (modes & 0x04) mask |= NFA_TECHNOLOGY_MASK_F; + if (modes & 0x08) mask |= NFA_TECHNOLOGY_MASK_A_ACTIVE; + if (modes & 0x10) mask |= NFA_TECHNOLOGY_MASK_F_ACTIVE; + if (modes & 0x20) mask |= NFA_TECHNOLOGY_MASK_F_ACTIVE; + nat->tech_mask = mask; + + //this function is not called by the NFC service nor exposed by public API. +} + + +/******************************************************************************* +** +** Function: nfcManager_doSetP2pTargetModes +** +** Description: Set P2P target's activation modes. +** e: JVM environment. +** o: Java object. +** modes: Active and/or passive modes. +** +** Returns: None. +** +*******************************************************************************/ +static void nfcManager_doSetP2pTargetModes (JNIEnv *e, jobject o, jint modes) +{ + ALOGD ("%s: modes=0x%X", __FUNCTION__, modes); + // Map in the right modes + tNFA_TECHNOLOGY_MASK mask = 0; + if (modes & 0x01) mask |= NFA_TECHNOLOGY_MASK_A; + if (modes & 0x02) mask |= NFA_TECHNOLOGY_MASK_F; + if (modes & 0x04) mask |= NFA_TECHNOLOGY_MASK_F; + if (modes & 0x08) mask |= NFA_TECHNOLOGY_MASK_A_ACTIVE | NFA_TECHNOLOGY_MASK_F_ACTIVE; + + PeerToPeer::getInstance().setP2pListenMask(mask); + //this function is not called by the NFC service nor exposed by public API. +} + +/***************************************************************************** +** +** JNI functions for android-4.0.1_r1 +** +*****************************************************************************/ +static JNINativeMethod gMethods[] = +{ + {"doDownload", "()Z", + (void *)nfcManager_doDownload}, + + {"initializeNativeStructure", "()Z", + (void*) nfcManager_initNativeStruc}, + + {"doInitialize", "()Z", + (void*) nfcManager_doInitialize}, + + {"doDeinitialize", "()Z", + (void*) nfcManager_doDeinitialize}, + + {"enableDiscovery", "()V", + (void*) nfcManager_enableDiscovery}, + + {"doGetSecureElementList", "()[I", + (void *)nfcManager_doGetSecureElementList}, + + {"doSelectSecureElement", "()V", + (void *)nfcManager_doSelectSecureElement}, + + {"doDeselectSecureElement", "()V", + (void *)nfcManager_doDeselectSecureElement}, + + {"doCheckLlcp", "()Z", + (void *)nfcManager_doCheckLlcp}, + + {"doActivateLlcp", "()Z", + (void *)nfcManager_doActivateLlcp}, + + {"doCreateLlcpConnectionlessSocket", "(ILjava/lang/String;)Lcom/android/nfc/dhimpl/NativeLlcpConnectionlessSocket;", + (void *)nfcManager_doCreateLlcpConnectionlessSocket}, + + {"doCreateLlcpServiceSocket", "(ILjava/lang/String;III)Lcom/android/nfc/dhimpl/NativeLlcpServiceSocket;", + (void*) nfcManager_doCreateLlcpServiceSocket}, + + {"doCreateLlcpSocket", "(IIII)Lcom/android/nfc/dhimpl/NativeLlcpSocket;", + (void*) nfcManager_doCreateLlcpSocket}, + + {"doGetLastError", "()I", + (void*) nfcManager_doGetLastError}, + + {"disableDiscovery", "()V", + (void*) nfcManager_disableDiscovery}, + + {"doSetTimeout", "(II)Z", + (void *)nfcManager_doSetTimeout}, + + {"doGetTimeout", "(I)I", + (void *)nfcManager_doGetTimeout}, + + {"doResetTimeouts", "()V", + (void *)nfcManager_doResetTimeouts}, + + {"doAbort", "()V", + (void *)nfcManager_doAbort}, + + {"doSetP2pInitiatorModes", "(I)V", + (void *)nfcManager_doSetP2pInitiatorModes}, + + {"doSetP2pTargetModes", "(I)V", + (void *)nfcManager_doSetP2pTargetModes}, + + {"doDump", "()Ljava/lang/String;", + (void *)nfcManager_doDump}, +}; + + +/******************************************************************************* +** +** Function: register_com_android_nfc_NativeNfcManager +** +** Description: Regisgter JNI functions with Java Virtual Machine. +** e: Environment of JVM. +** +** Returns: Status of registration. +** +*******************************************************************************/ +int register_com_android_nfc_NativeNfcManager (JNIEnv *e) +{ + ALOGD ("%s: enter", __FUNCTION__); + PowerSwitch::getInstance ().initialize (PowerSwitch::UNKNOWN_LEVEL); + ALOGD ("%s: exit", __FUNCTION__); + return jniRegisterNativeMethods (e, gNativeNfcManagerClassName, gMethods, NELEM (gMethods)); +} + + +/******************************************************************************* +** +** Function: startRfDiscovery +** +** Description: Ask stack to start polling and listening for devices. +** isStart: Whether to start. +** +** Returns: None +** +*******************************************************************************/ +void startRfDiscovery(bool isStart) +{ + tNFA_STATUS status = NFA_STATUS_FAILED; + + ALOGD ("%s: is start=%d", __FUNCTION__, isStart); + SyncEventGuard guard (sNfaEnableDisablePollingEvent); + status = isStart ? NFA_StartRfDiscovery () : NFA_StopRfDiscovery (); + if (status == NFA_STATUS_OK) + { + sNfaEnableDisablePollingEvent.wait (); //wait for NFA_RF_DISCOVERY_xxxx_EVT + sRfEnabled = isStart; + } + else + { + ALOGE ("%s: Failed to start/stop RF discovery; error=0x%X", __FUNCTION__, status); + } +} + + +/******************************************************************************* +** +** Function: doStartupConfig +** +** Description: Configure the NFC controller. +** +** Returns: None +** +*******************************************************************************/ +void doStartupConfig() +{ + unsigned long num = 0; + struct nfc_jni_native_data *nat = getNative(0, 0); + tNFA_STATUS stat = NFA_STATUS_FAILED; + + // Enable the "RC workaround" to allow our stack/firmware to work with a retail + // Nexus S that causes IOP issues. Only enable if value exists and set to 1. + if (GetNumValue(NAME_USE_NXP_P2P_RC_WORKAROUND, &num, sizeof(num)) && (num == 1)) + { +#if (NCI_VERSION > NCI_VERSION_20791B0) + UINT8 nfa_dm_rc_workaround[] = { 0x03, 0x0f, 0xab }; +#else + UINT8 nfa_dm_rc_workaround[] = { 0x01, 0x0f, 0xab, 0x01 }; +#endif + + ALOGD ("%s: Configure RC work-around", __FUNCTION__); + SyncEventGuard guard (sNfaSetConfigEvent); + stat = NFA_SetConfig(NCI_PARAM_ID_FW_WORKAROUND, sizeof(nfa_dm_rc_workaround), &nfa_dm_rc_workaround[0]); + if (stat == NFA_STATUS_OK) + sNfaSetConfigEvent.wait (); + } + + // If polling for Active mode, set the ordering so that we choose Active over Passive mode first. + if (nat && (nat->tech_mask & (NFA_TECHNOLOGY_MASK_A_ACTIVE | NFA_TECHNOLOGY_MASK_F_ACTIVE))) + { + UINT8 act_mode_order_param[] = { 0x01 }; + SyncEventGuard guard (sNfaSetConfigEvent); + stat = NFA_SetConfig(NCI_PARAM_ID_ACT_ORDER, sizeof(act_mode_order_param), &act_mode_order_param[0]); + if (stat == NFA_STATUS_OK) + sNfaSetConfigEvent.wait (); + } + + // Set configuration to allow UICC to Power off if there is no traffic. + if (GetNumValue(NAME_UICC_IDLE_TIMEOUT, &num, sizeof(num)) && (num != 0)) + { + // 61 => The least significant bit of this byte enables the power off when Idle mode. + // 00 87 93 03 == > These 4 bytes form a 4 byte value which decides the idle timeout(in us) + // value to trigger the uicc deactivation. + // e.g. in current example its value is 0x3938700 i.e. 60000000 is 60 seconds. + UINT8 swpcfg_param[] = { 0x61, 0x00, 0x82, 0x04, 0x20, 0xA1, 0x07, 0x00, + 0x90, 0xD0, 0x03, 0x00, 0x00, 0x87, 0x93, 0x03 }; + + ALOGD ("%s: Configure UICC idle-timeout to %lu ms", __FUNCTION__, num); + + // Set the timeout from the .conf file value. + num *= 1000; + UINT8 * p = &swpcfg_param[12]; + UINT32_TO_STREAM(p, num) + + SyncEventGuard guard (sNfaSetConfigEvent); + stat = NFA_SetConfig(NCI_PARAM_ID_SWPCFG, sizeof(swpcfg_param), &swpcfg_param[0]); + if (stat == NFA_STATUS_OK) + sNfaSetConfigEvent.wait (); + } + + // Set antenna tuning configuration if configured. +#define PREINIT_DSP_CFG_SIZE 30 + UINT8 preinit_dsp_param[PREINIT_DSP_CFG_SIZE]; + + if (GetStrValue(NAME_PREINIT_DSP_CFG, (char*)&preinit_dsp_param[0], sizeof(preinit_dsp_param))) + { + SyncEventGuard guard (sNfaSetConfigEvent); + stat = NFA_SetConfig(NCI_PARAM_ID_PREINIT_DSP_CFG, sizeof(preinit_dsp_param), &preinit_dsp_param[0]); + if (stat == NFA_STATUS_OK) + sNfaSetConfigEvent.wait (); + } +} + + +/******************************************************************************* +** +** Function: nfcManager_isNfcActive +** +** Description: Used externaly to determine if NFC is active or not. +** +** Returns: 'true' if the NFC stack is running, else 'false'. +** +*******************************************************************************/ +bool nfcManager_isNfcActive() +{ + return sIsNfaEnabled; +} + + +/******************************************************************************* +** +** Function: startStopPolling +** +** Description: Start or stop polling. +** isStartPolling: true to start polling; false to stop polling. +** +** Returns: None. +** +*******************************************************************************/ +void startStopPolling (bool isStartPolling) +{ + ALOGD ("%s: enter; isStart=%u", __FUNCTION__, isStartPolling); + tNFA_STATUS stat = NFA_STATUS_FAILED; + + startRfDiscovery (false); + if (isStartPolling) + { + tNFA_TECHNOLOGY_MASK tech_mask = DEFAULT_TECH_MASK; + unsigned long num = 0; + if (GetNumValue(NAME_POLLING_TECH_MASK, &num, sizeof(num))) + tech_mask = num; + + SyncEventGuard guard (sNfaEnableDisablePollingEvent); + ALOGD ("%s: enable polling", __FUNCTION__); + stat = NFA_EnablePolling (tech_mask); + if (stat == NFA_STATUS_OK) + { + ALOGD ("%s: wait for enable event", __FUNCTION__); + sNfaEnableDisablePollingEvent.wait (); //wait for NFA_POLL_ENABLED_EVT + } + else + ALOGE ("%s: fail enable polling; error=0x%X", __FUNCTION__, stat); + } + else + { + SyncEventGuard guard (sNfaEnableDisablePollingEvent); + ALOGD ("%s: disable polling", __FUNCTION__); + stat = NFA_DisablePolling (); + if (stat == NFA_STATUS_OK) + { + sNfaEnableDisablePollingEvent.wait (); //wait for NFA_POLL_DISABLED_EVT + } + else + ALOGE ("%s: fail disable polling; error=0x%X", __FUNCTION__, stat); + } + startRfDiscovery (true); + ALOGD ("%s: exit", __FUNCTION__); +} + + +} /* namespace android */ diff --git a/nci/jni/NativeNfcTag.cpp b/nci/jni/NativeNfcTag.cpp new file mode 100755 index 0000000..0ba7b21 --- /dev/null +++ b/nci/jni/NativeNfcTag.cpp @@ -0,0 +1,1507 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <semaphore.h> +#include <errno.h> +#include <time.h> +#include <signal.h> +#include "OverrideLog.h" +#include "NfcJniUtil.h" +#include "NfcTag.h" +#include "config.h" +#include "Mutex.h" +#include "IntervalTimer.h" +#include "JavaClassConstants.h" +#include "Pn544Interop.h" + +extern "C" +{ + #include "nfa_api.h" + #include "nfa_rw_api.h" + #include "ndef_utils.h" + #include "rw_api.h" +} +namespace android +{ + extern nfc_jni_native_data* getNative(JNIEnv *e, jobject o); + extern bool nfcManager_isNfcActive(); + extern int gGeneralTransceiveTimeout; +} + + +/***************************************************************************** +** +** public variables and functions +** +*****************************************************************************/ +namespace android +{ + bool gIsTagDeactivating = false; // flag for nfa callback indicating we are deactivating for RF interface switch + bool gIsSelectingRfInterface = false; // flag for nfa callback indicating we are selecting for RF interface switch +} + + +/***************************************************************************** +** +** private variables and functions +** +*****************************************************************************/ +namespace android +{ + + +// Pre-defined tag type values. These must match the values in +// framework Ndef.java for Google public NFC API. +#define NDEF_UNKNOWN_TYPE -1 +#define NDEF_TYPE1_TAG 1 +#define NDEF_TYPE2_TAG 2 +#define NDEF_TYPE3_TAG 3 +#define NDEF_TYPE4_TAG 4 +#define NDEF_MIFARE_CLASSIC_TAG 101 + +#define STATUS_CODE_TARGET_LOST 146 // this error code comes from the service + +static uint32_t sCheckNdefCurrentSize = 0; +static tNFA_STATUS sCheckNdefStatus = 0; //whether tag already contains a NDEF message +static bool sCheckNdefCapable = false; //whether tag has NDEF capability +static tNFA_HANDLE sNdefTypeHandlerHandle = NFA_HANDLE_INVALID; +static tNFA_INTF_TYPE sCurrentRfInterface = NFA_INTERFACE_ISO_DEP; +static uint8_t* sTransceiveData = NULL; +static uint32_t sTransceiveDataLen = 0; +static bool sWaitingForTransceive = false; +static bool sNeedToSwitchRf = false; +static Mutex sRfInterfaceMutex; +static uint32_t sReadDataLen = 0; +static uint8_t* sReadData = NULL; +static bool sIsReadingNdefMessage = false; +static SyncEvent sReadEvent; +static sem_t sWriteSem; +static sem_t sFormatSem; +static SyncEvent sTransceiveEvent; +static SyncEvent sReconnectEvent; +static sem_t sCheckNdefSem; +static sem_t sPresenceCheckSem; +static sem_t sMakeReadonlySem; +static IntervalTimer sSwitchBackTimer; // timer used to tell us to switch back to ISO_DEP frame interface +static jboolean sWriteOk = JNI_FALSE; +static jboolean sWriteWaitingForComplete = JNI_FALSE; +static bool sFormatOk = false; +static jboolean sConnectOk = JNI_FALSE; +static jboolean sConnectWaitingForComplete = JNI_FALSE; +static bool sGotDeactivate = false; +static uint32_t sCheckNdefMaxSize = 0; +static bool sCheckNdefCardReadOnly = false; +static jboolean sCheckNdefWaitingForComplete = JNI_FALSE; +static int sCountTagAway = 0; //count the consecutive number of presence-check failures +static tNFA_STATUS sMakeReadonlyStatus = NFA_STATUS_FAILED; +static jboolean sMakeReadonlyWaitingForComplete = JNI_FALSE; + +static int reSelect (tNFA_INTF_TYPE rfInterface); +static bool switchRfInterface(tNFA_INTF_TYPE rfInterface); + + +/******************************************************************************* +** +** Function: nativeNfcTag_abortWaits +** +** Description: Unblock all thread synchronization objects. +** +** Returns: None +** +*******************************************************************************/ +void nativeNfcTag_abortWaits () +{ + ALOGD ("%s", __FUNCTION__); + { + SyncEventGuard g (sReadEvent); + sReadEvent.notifyOne (); + } + sem_post (&sWriteSem); + sem_post (&sFormatSem); + { + SyncEventGuard g (sTransceiveEvent); + sTransceiveEvent.notifyOne (); + } + { + SyncEventGuard g (sReconnectEvent); + sReconnectEvent.notifyOne (); + } + + sem_post (&sCheckNdefSem); + sem_post (&sPresenceCheckSem); + sem_post (&sMakeReadonlySem); +} + + +/******************************************************************************* +** +** Function: switchBackTimerProc +** +** Description: Callback function for interval timer. +** +** Returns: None +** +*******************************************************************************/ +static void switchBackTimerProc (union sigval) +{ + ALOGD ("%s", __FUNCTION__); + switchRfInterface(NFA_INTERFACE_ISO_DEP); +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doReadCompleted +** +** Description: Receive the completion status of read operation. Called by +** NFA_READ_CPLT_EVT. +** status: Status of operation. +** +** Returns: None +** +*******************************************************************************/ +void nativeNfcTag_doReadCompleted (tNFA_STATUS status) +{ + ALOGD ("%s: status=0x%X; is reading=%u", __FUNCTION__, status, sIsReadingNdefMessage); + + if (sIsReadingNdefMessage == false) + return; //not reading NDEF message right now, so just return + + if (status != NFA_STATUS_OK) + { + sReadDataLen = 0; + if (sReadData) + free (sReadData); + sReadData = NULL; + } + SyncEventGuard g (sReadEvent); + sReadEvent.notifyOne (); +} + + +/******************************************************************************* +** +** Function: ndefHandlerCallback +** +** Description: Receive NDEF-message related events from stack. +** event: Event code. +** p_data: Event data. +** +** Returns: None +** +*******************************************************************************/ +static void ndefHandlerCallback (tNFA_NDEF_EVT event, tNFA_NDEF_EVT_DATA *eventData) +{ + ALOGD ("%s: event=%u, eventData=%p", __FUNCTION__, event, eventData); + + switch (event) + { + case NFA_NDEF_REGISTER_EVT: + { + tNFA_NDEF_REGISTER& ndef_reg = eventData->ndef_reg; + ALOGD ("%s: NFA_NDEF_REGISTER_EVT; status=0x%X; h=0x%X", __FUNCTION__, ndef_reg.status, ndef_reg.ndef_type_handle); + sNdefTypeHandlerHandle = ndef_reg.ndef_type_handle; + } + break; + + case NFA_NDEF_DATA_EVT: + { + ALOGD ("%s: NFA_NDEF_DATA_EVT; data_len = %lu", __FUNCTION__, eventData->ndef_data.len); + sReadDataLen = eventData->ndef_data.len; + sReadData = (uint8_t*) malloc (sReadDataLen); + memcpy (sReadData, eventData->ndef_data.p_data, eventData->ndef_data.len); + } + break; + + default: + ALOGE ("%s: Unknown event %u ????", __FUNCTION__, event); + break; + } +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doRead +** +** Description: Read the NDEF message on the tag. +** e: JVM environment. +** o: Java object. +** +** Returns: NDEF message. +** +*******************************************************************************/ +static jbyteArray nativeNfcTag_doRead (JNIEnv *e, jobject o) +{ + ALOGD ("%s: enter", __FUNCTION__); + tNFA_STATUS status = NFA_STATUS_FAILED; + jbyteArray buf = NULL; + + sReadDataLen = 0; + if (sReadData != NULL) + { + free (sReadData); + sReadData = NULL; + } + + if (sCheckNdefCurrentSize > 0) + { + { + SyncEventGuard g (sReadEvent); + sIsReadingNdefMessage = true; + status = NFA_RwReadNDef (); + sReadEvent.wait (); //wait for NFA_READ_CPLT_EVT + } + sIsReadingNdefMessage = false; + + if (sReadDataLen > 0) //if stack actually read data from the tag + { + ALOGD ("%s: read %u bytes", __FUNCTION__, sReadDataLen); + buf = e->NewByteArray (sReadDataLen); + e->SetByteArrayRegion (buf, 0, sReadDataLen, (jbyte*) sReadData); + } + } + else + { + ALOGD ("%s: create emtpy buffer", __FUNCTION__); + static uint8_t* empty = (uint8_t*) ""; + sReadDataLen = 0; + sReadData = (uint8_t*) malloc (1); + buf = e->NewByteArray (sReadDataLen); + e->SetByteArrayRegion (buf, 0, sReadDataLen, (jbyte*) sReadData); + } + + if (sReadData) + { + free (sReadData); + sReadData = NULL; + } + sReadDataLen = 0; + + ALOGD ("%s: exit", __FUNCTION__); + return buf; +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doWriteStatus +** +** Description: Receive the completion status of write operation. Called +** by NFA_WRITE_CPLT_EVT. +** isWriteOk: Status of operation. +** +** Returns: None +** +*******************************************************************************/ +void nativeNfcTag_doWriteStatus (jboolean isWriteOk) +{ + if (sWriteWaitingForComplete != JNI_FALSE) + { + sWriteWaitingForComplete = JNI_FALSE; + sWriteOk = isWriteOk; + sem_post (&sWriteSem); + } +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_formatStatus +** +** Description: Receive the completion status of format operation. Called +** by NFA_FORMAT_CPLT_EVT. +** isOk: Status of operation. +** +** Returns: None +** +*******************************************************************************/ +void nativeNfcTag_formatStatus (bool isOk) +{ + sFormatOk = isOk; + sem_post (&sFormatSem); +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doWrite +** +** Description: Write a NDEF message to the tag. +** e: JVM environment. +** o: Java object. +** buf: Contains a NDEF message. +** +** Returns: True if ok. +** +*******************************************************************************/ +static jboolean nativeNfcTag_doWrite (JNIEnv *e, jobject o, jbyteArray buf) +{ + jboolean result = JNI_FALSE; + tNFA_STATUS status = 0; + UINT32 len = 0; + UINT8* p_data = NULL; + const int maxBufferSize = 1024; + UINT8 buffer[maxBufferSize] = { 0 }; + UINT32 curDataSize = 0; + + len = (UINT32) e->GetArrayLength (buf); + p_data = (UINT8*) e->GetByteArrayElements (buf, NULL); + + ALOGD ("%s: enter; len = %lu", __FUNCTION__, len); + + /* Create the write semaphore */ + if (sem_init (&sWriteSem, 0, 0) == -1) + { + ALOGE ("%s: semaphore creation failed (errno=0x%08x)", __FUNCTION__, errno); + return JNI_FALSE; + } + + sWriteWaitingForComplete = JNI_TRUE; + if (sCheckNdefStatus == NFA_STATUS_FAILED) + { + //if tag does not contain a NDEF message + //and tag is capable of storing NDEF message + if (sCheckNdefCapable) + { + ALOGD ("%s: try format", __FUNCTION__); + sem_init (&sFormatSem, 0, 0); + sFormatOk = false; + status = NFA_RwFormatTag (); + sem_wait (&sFormatSem); + sem_destroy (&sFormatSem); + if (sFormatOk == false) //if format operation failed + goto TheEnd; + } + ALOGD ("%s: try write", __FUNCTION__); + status = NFA_RwWriteNDef (p_data, len); + } + else if (len == 0) + { + //if (NXP TagWriter wants to erase tag) then create and write an empty ndef message + NDEF_MsgInit (buffer, maxBufferSize, &curDataSize); + status = NDEF_MsgAddRec (buffer, maxBufferSize, &curDataSize, NDEF_TNF_EMPTY, NULL, 0, NULL, 0, NULL, 0); + ALOGD ("%s: create empty ndef msg; status=%u; size=%lu", __FUNCTION__, status, curDataSize); + status = NFA_RwWriteNDef (buffer, curDataSize); + } + else + { + ALOGD ("%s: NFA_RwWriteNDef", __FUNCTION__); + status = NFA_RwWriteNDef (p_data, len); + } + + if (status != NFA_STATUS_OK) + { + ALOGE ("%s: write/format error=%d", __FUNCTION__, status); + goto TheEnd; + } + + /* Wait for write completion status */ + sWriteOk = false; + if (sem_wait (&sWriteSem)) + { + ALOGE ("%s: wait semaphore (errno=0x%08x)", __FUNCTION__, errno); + goto TheEnd; + } + + result = sWriteOk; + +TheEnd: + /* Destroy semaphore */ + if (sem_destroy (&sWriteSem)) + { + ALOGE ("%s: failed destroy semaphore (errno=0x%08x)", __FUNCTION__, errno); + } + sWriteWaitingForComplete = JNI_FALSE; + ALOGD ("%s: exit; result=%d", __FUNCTION__, result); + return result; +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doConnectStatus +** +** Description: Receive the completion status of connect operation. +** isConnectOk: Status of the operation. +** +** Returns: None +** +*******************************************************************************/ +void nativeNfcTag_doConnectStatus (jboolean isConnectOk) +{ + if (sConnectWaitingForComplete != JNI_FALSE) + { + sConnectWaitingForComplete = JNI_FALSE; + sConnectOk = isConnectOk; + SyncEventGuard g (sReconnectEvent); + sReconnectEvent.notifyOne (); + } +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doDeactivateStatus +** +** Description: Receive the completion status of deactivate operation. +** +** Returns: None +** +*******************************************************************************/ +void nativeNfcTag_doDeactivateStatus (int status) +{ + sGotDeactivate = (status == 0); + + SyncEventGuard g (sReconnectEvent); + sReconnectEvent.notifyOne (); +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doConnect +** +** Description: Connect to the tag in RF field. +** e: JVM environment. +** o: Java object. +** targetHandle: Handle of the tag. +** +** Returns: Must return NXP status code, which NFC service expects. +** +*******************************************************************************/ +static jint nativeNfcTag_doConnect (JNIEnv *e, jobject o, jint targetHandle) +{ + ALOGD ("%s: targetHandle = %d", __FUNCTION__, targetHandle); + int i = targetHandle; + struct nfc_jni_native_data *nat = getNative (0, 0); + NfcTag& natTag = NfcTag::getInstance (); + int retCode = NFCSTATUS_SUCCESS; + + sNeedToSwitchRf = false; + if (i >= NfcTag::MAX_NUM_TECHNOLOGY) + { + ALOGE ("%s: Handle not found", __FUNCTION__); + retCode = NFCSTATUS_FAILED; + goto TheEnd; + } + + if (natTag.getActivationState() != NfcTag::Active) + { + ALOGE ("%s: tag already deactivated", __FUNCTION__); + retCode = NFCSTATUS_FAILED; + goto TheEnd; + } + + if (natTag.mTechLibNfcTypes[i] != NFC_PROTOCOL_ISO_DEP) + { + ALOGD ("%s() Nfc type = %d, do nothing for non ISO_DEP", __FUNCTION__, natTag.mTechLibNfcTypes[i]); + retCode = NFCSTATUS_SUCCESS; + goto TheEnd; + } + + if (natTag.mTechList[i] == TARGET_TYPE_ISO14443_3A || natTag.mTechList[i] == TARGET_TYPE_ISO14443_3B) + { + ALOGD ("%s: switching to tech: %d need to switch rf intf to frame", __FUNCTION__, natTag.mTechList[i]); + // connecting to NfcA or NfcB don't actually switch until/unless we get a transceive + sNeedToSwitchRf = true; + } + else + { + // connecting back to IsoDep or NDEF + return (switchRfInterface (NFA_INTERFACE_ISO_DEP) ? NFCSTATUS_SUCCESS : NFCSTATUS_FAILED); + } + +TheEnd: + ALOGD ("%s: exit 0x%X", __FUNCTION__, retCode); + return retCode; +} + +/******************************************************************************* +** +** Function: reSelect +** +** Description: Deactivates the tag and re-selects it with the specified +** rf interface. +** +** Returns: status code, 0 on success, 1 on failure, +** 146 (defined in service) on tag lost +** +*******************************************************************************/ +static int reSelect (tNFA_INTF_TYPE rfInterface) +{ + ALOGD ("%s: enter; rf intf = %d", __FUNCTION__, rfInterface); + NfcTag& natTag = NfcTag::getInstance (); + + tNFA_STATUS status; + int rVal = 1; + + do + { + //if tag has shutdown, abort this method + if (NfcTag::getInstance ().isNdefDetectionTimedOut()) + { + ALOGD ("%s: ndef detection timeout; break", __FUNCTION__); + rVal = STATUS_CODE_TARGET_LOST; + break; + } + + { + SyncEventGuard g (sReconnectEvent); + gIsTagDeactivating = true; + sGotDeactivate = false; + ALOGD ("%s: deactivate to sleep", __FUNCTION__); + if (NFA_STATUS_OK != (status = NFA_Deactivate (TRUE))) //deactivate to sleep state + { + ALOGE ("%s: deactivate failed, status = %d", __FUNCTION__, status); + break; + } + + if (sReconnectEvent.wait (1000) == false) //if timeout occurred + { + ALOGE ("%s: timeout waiting for deactivate", __FUNCTION__); + } + } + + if (NfcTag::getInstance ().getActivationState () != NfcTag::Sleep) + { + ALOGD ("%s: tag is not in sleep", __FUNCTION__); + rVal = STATUS_CODE_TARGET_LOST; + break; + } + + gIsTagDeactivating = false; + + { + SyncEventGuard g2 (sReconnectEvent); + + sConnectWaitingForComplete = JNI_TRUE; + ALOGD ("%s: select interface %u", __FUNCTION__, rfInterface); + gIsSelectingRfInterface = true; + if (NFA_STATUS_OK != (status = NFA_Select (natTag.mTechHandles[0], natTag.mTechLibNfcTypes[0], rfInterface))) + { + ALOGE ("%s: NFA_Select failed, status = %d", __FUNCTION__, status); + break; + } + + sConnectOk = false; + if (sReconnectEvent.wait (1000) == false) //if timeout occured + { + ALOGE ("%s: timeout waiting for select", __FUNCTION__); + break; + } + } + + ALOGD("%s: select completed; sConnectOk=%d", __FUNCTION__, sConnectOk); + if (NfcTag::getInstance ().getActivationState () != NfcTag::Active) + { + ALOGD("%s: tag is not active", __FUNCTION__); + rVal = STATUS_CODE_TARGET_LOST; + break; + } + rVal = (sConnectOk) ? 0 : 1; + } while (0); + + sConnectWaitingForComplete = JNI_FALSE; + gIsTagDeactivating = false; + gIsSelectingRfInterface = false; + ALOGD ("%s: exit; status=%d", __FUNCTION__, rVal); + return rVal; +} + +/******************************************************************************* +** +** Function: switchRfInterface +** +** Description: Switch controller's RF interface to frame, ISO-DEP, or NFC-DEP. +** rfInterface: Type of RF interface. +** +** Returns: True if ok. +** +*******************************************************************************/ +static bool switchRfInterface (tNFA_INTF_TYPE rfInterface) +{ + ALOGD ("%s: rf intf = %d", __FUNCTION__, rfInterface); + NfcTag& natTag = NfcTag::getInstance (); + + if (natTag.mTechLibNfcTypes[0] != NFC_PROTOCOL_ISO_DEP) + { + ALOGD ("%s: protocol: %d not ISO_DEP, do nothing", __FUNCTION__, natTag.mTechLibNfcTypes[0]); + return true; + } + + sRfInterfaceMutex.lock (); + ALOGD ("%s: new rf intf = %d, cur rf intf = %d", __FUNCTION__, rfInterface, sCurrentRfInterface); + + bool rVal = true; + if (rfInterface != sCurrentRfInterface) + { + if (rVal = (0 == reSelect(rfInterface))) + { + sCurrentRfInterface = rfInterface; + } + } + + sRfInterfaceMutex.unlock (); + return rVal; +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doReconnect +** +** Description: Re-connect to the tag in RF field. +** e: JVM environment. +** o: Java object. +** +** Returns: Status code. +** +*******************************************************************************/ +static jint nativeNfcTag_doReconnect (JNIEnv *e, jobject o) +{ + ALOGD ("%s: enter", __FUNCTION__); + int retCode = NFCSTATUS_SUCCESS; + NfcTag& natTag = NfcTag::getInstance (); + + if (natTag.getActivationState() != NfcTag::Active) + { + ALOGE ("%s: tag already deactivated", __FUNCTION__); + retCode = NFCSTATUS_FAILED; + goto TheEnd; + } + + // this is only supported for type 2 or 4 (ISO_DEP) tags + if (natTag.mTechLibNfcTypes[0] == NFA_PROTOCOL_ISO_DEP) + retCode = reSelect(NFA_INTERFACE_ISO_DEP); + else if (natTag.mTechLibNfcTypes[0] == NFA_PROTOCOL_T2T) + retCode = reSelect(NFA_INTERFACE_FRAME); + +TheEnd: + ALOGD ("%s: exit 0x%X", __FUNCTION__, retCode); + return retCode; +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doHandleReconnect +** +** Description: Re-connect to the tag in RF field. +** e: JVM environment. +** o: Java object. +** targetHandle: Handle of the tag. +** +** Returns: Status code. +** +*******************************************************************************/ +static jint nativeNfcTag_doHandleReconnect (JNIEnv *e, jobject o, jint targetHandle) +{ + ALOGD ("%s: targetHandle = %d", __FUNCTION__, targetHandle); + return nativeNfcTag_doConnect (e, o, targetHandle); +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doDisconnect +** +** Description: Deactivate the RF field. +** e: JVM environment. +** o: Java object. +** +** Returns: True if ok. +** +*******************************************************************************/ +static jboolean nativeNfcTag_doDisconnect (JNIEnv *e, jobject o) +{ + ALOGD ("%s: enter", __FUNCTION__); + struct nfc_jni_native_data *nat = getNative (0, 0); + tNFA_STATUS nfaStat = NFA_STATUS_OK; + + gGeneralTransceiveTimeout = DEFAULT_GENERAL_TRANS_TIMEOUT; + + if (NfcTag::getInstance ().getActivationState () != NfcTag::Active) + { + ALOGE ("%s: tag already deactivated", __FUNCTION__); + goto TheEnd; + } + + nfaStat = NFA_Deactivate (FALSE); + if (nfaStat != NFA_STATUS_OK) + ALOGE ("%s: deactivate failed; error=0x%X", __FUNCTION__, nfaStat); + +TheEnd: + ALOGD ("%s: exit", __FUNCTION__); + return (nfaStat == NFA_STATUS_OK) ? JNI_TRUE : JNI_FALSE; +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doTransceiveStatus +** +** Description: Receive the completion status of transceive operation. +** buf: Contains tag's response. +** bufLen: Length of buffer. +** +** Returns: None +** +*******************************************************************************/ +void nativeNfcTag_doTransceiveStatus (uint8_t* buf, uint32_t bufLen) +{ + ALOGD ("%s: data len=%d, waiting for transceive: %d", __FUNCTION__, bufLen, sWaitingForTransceive); + if (!sWaitingForTransceive) + return; + + sTransceiveDataLen = 0; + if (bufLen) + { + if (NULL == (sTransceiveData = (uint8_t *) malloc (bufLen))) + { + ALOGD ("%s: memory allocation error", __FUNCTION__); + } + else + { + memcpy (sTransceiveData, buf, sTransceiveDataLen = bufLen); + } + } + + { + SyncEventGuard g (sTransceiveEvent); + sTransceiveEvent.notifyOne (); + } +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doTransceive +** +** Description: Send raw data to the tag; receive tag's response. +** e: JVM environment. +** o: Java object. +** raw: Not used. +** statusTargetLost: Whether tag responds or times out. +** +** Returns: Response from tag. +** +*******************************************************************************/ +static jbyteArray nativeNfcTag_doTransceive (JNIEnv *e, jobject o, jbyteArray data, jboolean raw, jintArray statusTargetLost) +{ + ALOGD ("%s: enter; raw=%u; timeout = %d", __FUNCTION__, raw, gGeneralTransceiveTimeout); + bool fNeedToSwitchBack = false; + nfc_jni_native_data *nat = getNative (0, 0); + bool waitOk = false; + bool isNack = false; + uint8_t *buf = NULL; + uint32_t bufLen = 0; + jint *targetLost = NULL; + + if (NfcTag::getInstance ().getActivationState () != NfcTag::Active) + { + if (statusTargetLost) + { + targetLost = e->GetIntArrayElements (statusTargetLost, 0); + if (targetLost) + *targetLost = 1; //causes NFC service to throw TagLostException + e->ReleaseIntArrayElements (statusTargetLost, targetLost, 0); + } + ALOGD ("%s: tag not active", __FUNCTION__); + return NULL; + } + + NfcTag& natTag = NfcTag::getInstance (); + + // get input buffer and length from java call + buf = (uint8_t *) e->GetByteArrayElements (data, NULL); + bufLen = (uint32_t) e->GetArrayLength (data); + + if (statusTargetLost) + { + targetLost = e->GetIntArrayElements (statusTargetLost, 0); + if (targetLost) + *targetLost = 0; //success, tag is still present + } + + sSwitchBackTimer.kill (); + jbyteArray result = NULL; + do + { + if (sNeedToSwitchRf) + { + // for ISO_DEP tags connected to NfcA or NfcB we need to be in FRAME interface + if (!switchRfInterface (NFA_INTERFACE_FRAME)) //NFA_INTERFACE_ISO_DEP + { + break; + } + fNeedToSwitchBack = true; + } + + sWaitingForTransceive = true; + sTransceiveDataLen = 0; + { + SyncEventGuard g (sTransceiveEvent); + tNFA_STATUS status = NFA_SendRawFrame (buf, bufLen); + if (status != NFA_STATUS_OK) + { + ALOGE ("%s: fail send; error=%d", __FUNCTION__, status); + break; + } + waitOk = sTransceiveEvent.wait (gGeneralTransceiveTimeout); + } + + if (waitOk == false) //if timeout occurred + { + ALOGE ("%s: wait response timeout", __FUNCTION__); + if (targetLost) + *targetLost = 1; //causes NFC service to throw TagLostException + break; + } + + if (NfcTag::getInstance ().getActivationState () != NfcTag::Active) + { + ALOGE ("%s: already deactivated", __FUNCTION__); + if (targetLost) + *targetLost = 1; //causes NFC service to throw TagLostException + break; + } + + ALOGD ("%s: response %d bytes", __FUNCTION__, sTransceiveDataLen); + + if ((natTag.getProtocol () == NFA_PROTOCOL_T2T) && + natTag.isT2tNackResponse (sTransceiveData, sTransceiveDataLen)) + { + isNack = true; + } + + if (sTransceiveDataLen) + { + if (!isNack) { + // marshall data to java for return + result = e->NewByteArray (sTransceiveDataLen); + if (result != NULL) + { + e->SetByteArrayRegion (result, 0, sTransceiveDataLen, (jbyte *) sTransceiveData); + } + else + ALOGE ("%s: Failed to allocate java byte array", __FUNCTION__); + } // else a nack is treated as a transceive failure to the upper layers + + free (sTransceiveData); + sTransceiveData = NULL; + sTransceiveDataLen = 0; + } + } while (0); + + sWaitingForTransceive = false; + e->ReleaseByteArrayElements (data, (jbyte *) buf, JNI_ABORT); + if (targetLost) + e->ReleaseIntArrayElements (statusTargetLost, targetLost, 0); + + if (fNeedToSwitchBack) + { + // this timer proc will switch us back to ISO_DEP frame interface + sSwitchBackTimer.set (1500, switchBackTimerProc); + } + + ALOGD ("%s: exit", __FUNCTION__); + return result; +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doGetNdefType +** +** Description: Retrieve the type of tag. +** e: JVM environment. +** o: Java object. +** libnfcType: Type of tag represented by JNI. +** javaType: Not used. +** +** Returns: Type of tag represented by NFC Service. +** +*******************************************************************************/ +static jint nativeNfcTag_doGetNdefType (JNIEnv *e, jobject o, jint libnfcType, jint javaType) +{ + ALOGD ("%s: enter; libnfc type=%d; java type=%d", __FUNCTION__, libnfcType, javaType); + jint ndefType = NDEF_UNKNOWN_TYPE; + + // For NFA, libnfcType is mapped to the protocol value received + // in the NFA_ACTIVATED_EVT and NFA_DISC_RESULT_EVT event. + switch (libnfcType) { + case NFA_PROTOCOL_T1T: + ndefType = NDEF_TYPE1_TAG; + break; + case NFA_PROTOCOL_T2T: + ndefType = NDEF_TYPE2_TAG;; + break; + case NFA_PROTOCOL_T3T: + ndefType = NDEF_TYPE3_TAG; + break; + case NFA_PROTOCOL_ISO_DEP: + ndefType = NDEF_TYPE4_TAG; + break; + case NFA_PROTOCOL_ISO15693: + ndefType = NDEF_UNKNOWN_TYPE; + break; + case NFA_PROTOCOL_INVALID: + ndefType = NDEF_UNKNOWN_TYPE; + break; + default: + ndefType = NDEF_UNKNOWN_TYPE; + break; + } + ALOGD ("%s: exit; ndef type=%d", __FUNCTION__, ndefType); + return ndefType; +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doCheckNdefResult +** +** Description: Receive the result of checking whether the tag contains a NDEF +** message. Called by the NFA_NDEF_DETECT_EVT. +** status: Status of the operation. +** maxSize: Maximum size of NDEF message. +** currentSize: Current size of NDEF message. +** flags: Indicate various states. +** +** Returns: None +** +*******************************************************************************/ +void nativeNfcTag_doCheckNdefResult (tNFA_STATUS status, uint32_t maxSize, uint32_t currentSize, uint8_t flags) +{ + //this function's flags parameter is defined using the following macros + //in nfc/include/rw_api.h; + //#define RW_NDEF_FL_READ_ONLY 0x01 /* Tag is read only */ + //#define RW_NDEF_FL_FORMATED 0x02 /* Tag formated for NDEF */ + //#define RW_NDEF_FL_SUPPORTED 0x04 /* NDEF supported by the tag */ + //#define RW_NDEF_FL_UNKNOWN 0x08 /* Unable to find if tag is ndef capable/formated/read only */ + //#define RW_NDEF_FL_FORMATABLE 0x10 /* Tag supports format operation */ + + if (status == NFC_STATUS_BUSY) + { + ALOGE ("%s: stack is busy", __FUNCTION__); + return; + } + + if (!sCheckNdefWaitingForComplete) + { + ALOGE ("%s: not waiting", __FUNCTION__); + return; + } + + if (flags & RW_NDEF_FL_READ_ONLY) + ALOGD ("%s: flag read-only", __FUNCTION__); + if (flags & RW_NDEF_FL_FORMATED) + ALOGD ("%s: flag formatted for ndef", __FUNCTION__); + if (flags & RW_NDEF_FL_SUPPORTED) + ALOGD ("%s: flag ndef supported", __FUNCTION__); + if (flags & RW_NDEF_FL_UNKNOWN) + ALOGD ("%s: flag all unknown", __FUNCTION__); + if (flags & RW_NDEF_FL_FORMATABLE) + ALOGD ("%s: flag formattable", __FUNCTION__); + + sCheckNdefWaitingForComplete = JNI_FALSE; + sCheckNdefStatus = status; + sCheckNdefCapable = false; //assume tag is NOT ndef capable + if (sCheckNdefStatus == NFA_STATUS_OK) + { + //NDEF content is on the tag + sCheckNdefMaxSize = maxSize; + sCheckNdefCurrentSize = currentSize; + sCheckNdefCardReadOnly = flags & RW_NDEF_FL_READ_ONLY; + sCheckNdefCapable = true; + } + else if (sCheckNdefStatus == NFA_STATUS_FAILED) + { + //no NDEF content on the tag + sCheckNdefMaxSize = 0; + sCheckNdefCurrentSize = 0; + sCheckNdefCardReadOnly = flags & RW_NDEF_FL_READ_ONLY; + if ((flags & RW_NDEF_FL_UNKNOWN) == 0) //if stack understands the tag + { + if (flags & RW_NDEF_FL_SUPPORTED) //if tag is ndef capable + sCheckNdefCapable = true; + } + } + else + { + ALOGE ("%s: unknown status=0x%X", __FUNCTION__, status); + sCheckNdefMaxSize = 0; + sCheckNdefCurrentSize = 0; + sCheckNdefCardReadOnly = false; + } + sem_post (&sCheckNdefSem); +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doCheckNdef +** +** Description: Does the tag contain a NDEF message? +** e: JVM environment. +** o: Java object. +** ndefInfo: NDEF info. +** +** Returns: Status code; 0 is success. +** +*******************************************************************************/ +static jint nativeNfcTag_doCheckNdef (JNIEnv *e, jobject o, jintArray ndefInfo) +{ + tNFA_STATUS status = NFA_STATUS_FAILED; + jint* ndef = NULL; + + ALOGD ("%s: enter", __FUNCTION__); + + /* Create the write semaphore */ + if (sem_init (&sCheckNdefSem, 0, 0) == -1) + { + ALOGE ("%s: Check NDEF semaphore creation failed (errno=0x%08x)", __FUNCTION__, errno); + return JNI_FALSE; + } + + if (NfcTag::getInstance ().getActivationState () != NfcTag::Active) + { + ALOGE ("%s: tag already deactivated", __FUNCTION__); + goto TheEnd; + } + + ALOGD ("%s: try NFA_RwDetectNDef", __FUNCTION__); + sCheckNdefWaitingForComplete = JNI_TRUE; + status = NFA_RwDetectNDef (); + + if (status != NFA_STATUS_OK) + { + ALOGE ("%s: NFA_RwDetectNDef failed, status = 0x%X", __FUNCTION__, status); + goto TheEnd; + } + + /* Wait for check NDEF completion status */ + if (sem_wait (&sCheckNdefSem)) + { + ALOGE ("%s: Failed to wait for check NDEF semaphore (errno=0x%08x)", __FUNCTION__, errno); + goto TheEnd; + } + + if (sCheckNdefStatus == NFA_STATUS_OK) + { + //stack found a NDEF message on the tag + ndef = e->GetIntArrayElements (ndefInfo, 0); + if (NfcTag::getInstance ().getProtocol () == NFA_PROTOCOL_T1T) + ndef[0] = NfcTag::getInstance ().getT1tMaxMessageSize (); + else + ndef[0] = sCheckNdefMaxSize; + if (sCheckNdefCardReadOnly) + ndef[1] = NDEF_MODE_READ_ONLY; + else + ndef[1] = NDEF_MODE_READ_WRITE; + e->ReleaseIntArrayElements (ndefInfo, ndef, 0); + status = NFA_STATUS_OK; + } + else if (sCheckNdefStatus == NFA_STATUS_FAILED) + { + //stack did not find a NDEF message on the tag; + ndef = e->GetIntArrayElements (ndefInfo, 0); + if (NfcTag::getInstance ().getProtocol () == NFA_PROTOCOL_T1T) + ndef[0] = NfcTag::getInstance ().getT1tMaxMessageSize (); + else + ndef[0] = sCheckNdefMaxSize; + if (sCheckNdefCardReadOnly) + ndef[1] = NDEF_MODE_READ_ONLY; + else + ndef[1] = NDEF_MODE_READ_WRITE; + e->ReleaseIntArrayElements (ndefInfo, ndef, 0); + status = NFA_STATUS_FAILED; + } + else if (sCheckNdefStatus == NFA_STATUS_TIMEOUT) + { + pn544InteropStopPolling (); + status = sCheckNdefStatus; + } + else + { + ALOGD ("%s: unknown status 0x%X", __FUNCTION__, sCheckNdefStatus); + status = sCheckNdefStatus; + } + +TheEnd: + /* Destroy semaphore */ + if (sem_destroy (&sCheckNdefSem)) + { + ALOGE ("%s: Failed to destroy check NDEF semaphore (errno=0x%08x)", __FUNCTION__, errno); + } + sCheckNdefWaitingForComplete = JNI_FALSE; + ALOGD ("%s: exit; status=0x%X", __FUNCTION__, status); + return status; +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_resetPresenceCheck +** +** Description: Reset variables related to presence-check. +** +** Returns: None +** +*******************************************************************************/ +void nativeNfcTag_resetPresenceCheck () +{ + sCountTagAway = 0; +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doPresenceCheckResult +** +** Description: Receive the result of presence-check. +** status: Result of presence-check. +** +** Returns: None +** +*******************************************************************************/ +void nativeNfcTag_doPresenceCheckResult (tNFA_STATUS status) +{ + if (status == NFA_STATUS_OK) + sCountTagAway = 0; + else + sCountTagAway++; + if (sCountTagAway > 0) + ALOGD ("%s: sCountTagAway=%d", __FUNCTION__, sCountTagAway); + sem_post (&sPresenceCheckSem); +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doPresenceCheck +** +** Description: Check if the tag is in the RF field. +** e: JVM environment. +** o: Java object. +** +** Returns: True if tag is in RF field. +** +*******************************************************************************/ +static jboolean nativeNfcTag_doPresenceCheck (JNIEnv *e, jobject o) +{ + ALOGD ("%s", __FUNCTION__); + tNFA_STATUS status = NFA_STATUS_OK; + jboolean isPresent = JNI_FALSE; + + if (nfcManager_isNfcActive() == false) + { + ALOGD ("%s: NFC is no longer active.", __FUNCTION__); + return JNI_FALSE; + } + + if (NfcTag::getInstance ().getActivationState () != NfcTag::Active) + { + ALOGD ("%s: tag already deactivated", __FUNCTION__); + return JNI_FALSE; + } + + if (sem_init (&sPresenceCheckSem, 0, 0) == -1) + { + ALOGE ("%s: semaphore creation failed (errno=0x%08x)", __FUNCTION__, errno); + return JNI_FALSE; + } + + status = NFA_RwPresenceCheck (); + if (status == NFA_STATUS_OK) + { + if (sem_wait (&sPresenceCheckSem)) + { + ALOGE ("%s: failed to wait (errno=0x%08x)", __FUNCTION__, errno); + } + else + { + isPresent = (sCountTagAway > 3) ? JNI_FALSE : JNI_TRUE; + } + } + + if (sem_destroy (&sPresenceCheckSem)) + { + ALOGE ("Failed to destroy check NDEF semaphore (errno=0x%08x)", errno); + } + + if (isPresent == JNI_FALSE) + ALOGD ("%s: tag absent ????", __FUNCTION__); + return isPresent; +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doIsNdefFormatable +** +** Description: Can tag be formatted to store NDEF message? +** e: JVM environment. +** o: Java object. +** libNfcType: Type of tag. +** uidBytes: Tag's unique ID. +** pollBytes: Data from activation. +** actBytes: Data from activation. +** +** Returns: True if formattable. +** +*******************************************************************************/ +static jboolean nativeNfcTag_doIsNdefFormatable (JNIEnv *e, + jobject o, jint libNfcType, jbyteArray uidBytes, jbyteArray pollBytes, + jbyteArray actBytes) +{ + jboolean isFormattable = JNI_FALSE; + + switch (NfcTag::getInstance().getProtocol()) + { + case NFA_PROTOCOL_T1T: + case NFA_PROTOCOL_ISO15693: + isFormattable = JNI_TRUE; + break; + + case NFA_PROTOCOL_T2T: + isFormattable = NfcTag::getInstance().isMifareUltralight() ? JNI_TRUE : JNI_FALSE; + } + ALOGD("%s: is formattable=%u", __FUNCTION__, isFormattable); + return isFormattable; +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doIsIsoDepNdefFormatable +** +** Description: Is ISO-DEP tag formattable? +** e: JVM environment. +** o: Java object. +** pollBytes: Data from activation. +** actBytes: Data from activation. +** +** Returns: True if formattable. +** +*******************************************************************************/ +static jboolean nativeNfcTag_doIsIsoDepNdefFormatable (JNIEnv *e, jobject o, jbyteArray pollBytes, jbyteArray actBytes) +{ + uint8_t uidFake[] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; + ALOGD ("%s", __FUNCTION__); + jbyteArray uidArray = e->NewByteArray (8); + e->SetByteArrayRegion (uidArray, 0, 8, (jbyte*) uidFake); + return nativeNfcTag_doIsNdefFormatable (e, o, 0, uidArray, pollBytes, actBytes); +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doNdefFormat +** +** Description: Format a tag so it can store NDEF message. +** e: JVM environment. +** o: Java object. +** key: Not used. +** +** Returns: True if ok. +** +*******************************************************************************/ +static jboolean nativeNfcTag_doNdefFormat (JNIEnv *e, jobject o, jbyteArray key) +{ + ALOGD ("%s: enter", __FUNCTION__); + tNFA_STATUS status = NFA_STATUS_OK; + + sem_init (&sFormatSem, 0, 0); + sFormatOk = false; + status = NFA_RwFormatTag (); + if (status == NFA_STATUS_OK) + { + ALOGD ("%s: wait for completion", __FUNCTION__); + sem_wait (&sFormatSem); + status = sFormatOk ? NFA_STATUS_OK : NFA_STATUS_FAILED; + } + else + ALOGE ("%s: error status=%u", __FUNCTION__, status); + sem_destroy (&sFormatSem); + + ALOGD ("%s: exit", __FUNCTION__); + return (status == NFA_STATUS_OK) ? JNI_TRUE : JNI_FALSE; +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doMakeReadonlyResult +** +** Description: Receive the result of making a tag read-only. Called by the +** NFA_SET_TAG_RO_EVT. +** status: Status of the operation. +** +** Returns: None +** +*******************************************************************************/ +void nativeNfcTag_doMakeReadonlyResult (tNFA_STATUS status) +{ + if (sMakeReadonlyWaitingForComplete != JNI_FALSE) + { + sMakeReadonlyWaitingForComplete = JNI_FALSE; + sMakeReadonlyStatus = status; + + sem_post (&sMakeReadonlySem); + } +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_doMakeReadonly +** +** Description: Make the tag read-only. +** e: JVM environment. +** o: Java object. +** key: Key to access the tag. +** +** Returns: True if ok. +** +*******************************************************************************/ +static jboolean nativeNfcTag_doMakeReadonly (JNIEnv *e, jobject o, jbyteArray key) +{ + jboolean result = JNI_FALSE; + tNFA_STATUS status; + + ALOGD ("%s", __FUNCTION__); + + /* Create the make_readonly semaphore */ + if (sem_init (&sMakeReadonlySem, 0, 0) == -1) + { + ALOGE ("%s: Make readonly semaphore creation failed (errno=0x%08x)", __FUNCTION__, errno); + return JNI_FALSE; + } + + sMakeReadonlyWaitingForComplete = JNI_TRUE; + + // Hard-lock the tag (cannot be reverted) + status = NFA_RwSetTagReadOnly(TRUE); + + if (status != NFA_STATUS_OK) + { + ALOGE ("%s: NFA_RwSetTagReadOnly failed, status = %d", __FUNCTION__, status); + goto TheEnd; + } + + /* Wait for check NDEF completion status */ + if (sem_wait (&sMakeReadonlySem)) + { + ALOGE ("%s: Failed to wait for make_readonly semaphore (errno=0x%08x)", __FUNCTION__, errno); + goto TheEnd; + } + + if (sMakeReadonlyStatus == NFA_STATUS_OK) + { + result = JNI_TRUE; + } + +TheEnd: + /* Destroy semaphore */ + if (sem_destroy (&sMakeReadonlySem)) + { + ALOGE ("%s: Failed to destroy read_only semaphore (errno=0x%08x)", __FUNCTION__, errno); + } + sMakeReadonlyWaitingForComplete = JNI_FALSE; + return result; +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_registerNdefTypeHandler +** +** Description: Register a callback to receive NDEF message from the tag +** from the NFA_NDEF_DATA_EVT. +** +** Returns: None +** +*******************************************************************************/ +//register a callback to receive NDEF message from the tag +//from the NFA_NDEF_DATA_EVT; +void nativeNfcTag_registerNdefTypeHandler () +{ + ALOGD ("%s", __FUNCTION__); + sNdefTypeHandlerHandle = NFA_HANDLE_INVALID; + NFA_RegisterNDefTypeHandler (TRUE, NFA_TNF_DEFAULT, (UINT8 *) "", 0, ndefHandlerCallback); +} + + +/******************************************************************************* +** +** Function: nativeNfcTag_deregisterNdefTypeHandler +** +** Description: No longer need to receive NDEF message from the tag. +** +** Returns: None +** +*******************************************************************************/ +void nativeNfcTag_deregisterNdefTypeHandler () +{ + ALOGD ("%s", __FUNCTION__); + NFA_DeregisterNDefTypeHandler (sNdefTypeHandlerHandle); + sNdefTypeHandlerHandle = NFA_HANDLE_INVALID; +} + + +/***************************************************************************** +** +** JNI functions for Android 4.0.3 +** +*****************************************************************************/ +static JNINativeMethod gMethods[] = +{ + {"doConnect", "(I)I", (void *)nativeNfcTag_doConnect}, + {"doDisconnect", "()Z", (void *)nativeNfcTag_doDisconnect}, + {"doReconnect", "()I", (void *)nativeNfcTag_doReconnect}, + {"doHandleReconnect", "(I)I", (void *)nativeNfcTag_doHandleReconnect}, + {"doTransceive", "([BZ[I)[B", (void *)nativeNfcTag_doTransceive}, + {"doGetNdefType", "(II)I", (void *)nativeNfcTag_doGetNdefType}, + {"doCheckNdef", "([I)I", (void *)nativeNfcTag_doCheckNdef}, + {"doRead", "()[B", (void *)nativeNfcTag_doRead}, + {"doWrite", "([B)Z", (void *)nativeNfcTag_doWrite}, + {"doPresenceCheck", "()Z", (void *)nativeNfcTag_doPresenceCheck}, + {"doIsIsoDepNdefFormatable", "([B[B)Z", (void *)nativeNfcTag_doIsIsoDepNdefFormatable}, + {"doNdefFormat", "([B)Z", (void *)nativeNfcTag_doNdefFormat}, + {"doMakeReadonly", "([B)Z", (void *)nativeNfcTag_doMakeReadonly}, +}; + + +/******************************************************************************* +** +** Function: register_com_android_nfc_NativeNfcTag +** +** Description: Regisgter JNI functions with Java Virtual Machine. +** e: Environment of JVM. +** +** Returns: Status of registration. +** +*******************************************************************************/ +int register_com_android_nfc_NativeNfcTag (JNIEnv *e) +{ + ALOGD ("%s", __FUNCTION__); + return jniRegisterNativeMethods (e, gNativeNfcTagClassName, gMethods, NELEM (gMethods)); +} + + +} /* namespace android */ diff --git a/nci/jni/NativeP2pDevice.cpp b/nci/jni/NativeP2pDevice.cpp new file mode 100644 index 0000000..57f9dad --- /dev/null +++ b/nci/jni/NativeP2pDevice.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "OverrideLog.h" +#include "NfcJniUtil.h" +#include "JavaClassConstants.h" + + +namespace android +{ + + +static jboolean nativeP2pDeviceDoConnect (JNIEnv* e, jobject o) +{ + ALOGD ("%s", __FUNCTION__); + return JNI_TRUE; +} + + +static jboolean nativeP2pDeviceDoDisconnect (JNIEnv* e, jobject o) +{ + ALOGD ("%s", __FUNCTION__); + return JNI_TRUE; +} + + +static jbyteArray nativeP2pDeviceDoTransceive (JNIEnv* e, jobject o, jbyteArray data) +{ + ALOGD ("%s", __FUNCTION__); + return NULL; +} + + +static jbyteArray nativeP2pDeviceDoReceive (JNIEnv* e, jobject o) +{ + ALOGD ("%s", __FUNCTION__); + return NULL; +} + + +static jboolean nativeP2pDeviceDoSend (JNIEnv* e, jobject o, jbyteArray buf) +{ + ALOGD ("%s", __FUNCTION__); + return JNI_TRUE; +} + + +/***************************************************************************** +** +** Description: JNI functions +** +*****************************************************************************/ +static JNINativeMethod gMethods[] = +{ + {"doConnect", "()Z", (void *) nativeP2pDeviceDoConnect}, + {"doDisconnect", "()Z", (void *) nativeP2pDeviceDoDisconnect}, + {"doTransceive", "([B)[B", (void *) nativeP2pDeviceDoTransceive}, + {"doReceive", "()[B", (void *) nativeP2pDeviceDoReceive}, + {"doSend", "([B)Z", (void *) nativeP2pDeviceDoSend}, +}; + + +/******************************************************************************* +** +** Function: register_com_android_nfc_NativeP2pDevice +** +** Description: Regisgter JNI functions with Java Virtual Machine. +** e: Environment of JVM. +** +** Returns: Status of registration. +** +*******************************************************************************/ +int register_com_android_nfc_NativeP2pDevice (JNIEnv* e) +{ + return jniRegisterNativeMethods (e, gNativeP2pDeviceClassName, + gMethods, NELEM(gMethods)); +} + + +} // namepspace android diff --git a/nci/jni/NativeSecureElement.cpp b/nci/jni/NativeSecureElement.cpp new file mode 100755 index 0000000..1a2a73a --- /dev/null +++ b/nci/jni/NativeSecureElement.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "OverrideLog.h" +#include "SecureElement.h" +#include "JavaClassConstants.h" +#include "PowerSwitch.h" + + +namespace android +{ + + +extern void com_android_nfc_NfcManager_disableDiscovery (JNIEnv* e, jobject o); +extern void com_android_nfc_NfcManager_enableDiscovery (JNIEnv* e, jobject o, jint mode); +extern int gGeneralTransceiveTimeout; + + +/******************************************************************************* +** +** Function: nativeNfcSecureElement_doOpenSecureElementConnection +** +** Description: Connect to the secure element. +** e: JVM environment. +** o: Java object. +** +** Returns: Handle of secure element. 0 is failure. +** +*******************************************************************************/ +static jint nativeNfcSecureElement_doOpenSecureElementConnection (JNIEnv* e, jobject o) +{ + ALOGD("%s: enter", __FUNCTION__); + bool stat = true; + jint secElemHandle = 0; + SecureElement &se = SecureElement::getInstance(); + + if (se.isActivatedInListenMode()) { + ALOGD("Denying SE open due to SE listen mode active"); + goto TheEnd; + } + + if (se.isRfFieldOn()) { + ALOGD("Denying SE open due to SE in active RF field"); + goto TheEnd; + } + //tell the controller to power up to get ready for sec elem operations + PowerSwitch::getInstance ().setLevel (PowerSwitch::FULL_POWER); + PowerSwitch::getInstance ().setModeOn (PowerSwitch::SE_CONNECTED); + + //if controller is not routing AND there is no pipe connected, + //then turn on the sec elem + if (! se.isBusy()) + stat = se.activate(0); + + if (stat) + { + //establish a pipe to sec elem + stat = se.connectEE(); + if (stat) + secElemHandle = se.mActiveEeHandle; + else + se.deactivate (0); + } + + //if code fails to connect to the secure element, and nothing is active, then + //tell the controller to power down + if ((!stat) && (! PowerSwitch::getInstance ().setModeOff (PowerSwitch::SE_CONNECTED))) + { + PowerSwitch::getInstance ().setLevel (PowerSwitch::LOW_POWER); + } + +TheEnd: + ALOGD("%s: exit; return handle=0x%X", __FUNCTION__, secElemHandle); + return secElemHandle; +} + + +/******************************************************************************* +** +** Function: nativeNfcSecureElement_doDisconnectSecureElementConnection +** +** Description: Disconnect from the secure element. +** e: JVM environment. +** o: Java object. +** handle: Handle of secure element. +** +** Returns: True if ok. +** +*******************************************************************************/ +static jboolean nativeNfcSecureElement_doDisconnectSecureElementConnection (JNIEnv* e, jobject o, jint handle) +{ + ALOGD("%s: enter; handle=0x%04x", __FUNCTION__, handle); + bool stat = false; + + stat = SecureElement::getInstance().disconnectEE (handle); + + //if controller is not routing AND there is no pipe connected, + //then turn off the sec elem + if (! SecureElement::getInstance().isBusy()) + SecureElement::getInstance().deactivate (handle); + + //if nothing is active after this, then tell the controller to power down + if (! PowerSwitch::getInstance ().setModeOff (PowerSwitch::SE_CONNECTED)) + PowerSwitch::getInstance ().setLevel (PowerSwitch::LOW_POWER); + + ALOGD("%s: exit", __FUNCTION__); + return stat ? JNI_TRUE : JNI_FALSE; +} + + +/******************************************************************************* +** +** Function: nativeNfcSecureElement_doTransceive +** +** Description: Send data to the secure element; retrieve response. +** e: JVM environment. +** o: Java object. +** handle: Secure element's handle. +** data: Data to send. +** +** Returns: Buffer of received data. +** +*******************************************************************************/ +static jbyteArray nativeNfcSecureElement_doTransceive (JNIEnv* e, jobject o, jint handle, jbyteArray data) +{ + UINT8* buf = NULL; + INT32 buflen = 0; + const INT32 recvBufferMaxSize = 1024; + UINT8 recvBuffer [recvBufferMaxSize]; + INT32 recvBufferActualSize = 0; + jbyteArray result = NULL; + + buf = (UINT8*) e->GetByteArrayElements (data, NULL); + buflen = e->GetArrayLength (data); + + ALOGD("%s: enter; handle=0x%X; buf len=%ld", __FUNCTION__, handle, buflen); + SecureElement::getInstance().transceive (buf, buflen, recvBuffer, recvBufferMaxSize, recvBufferActualSize, gGeneralTransceiveTimeout); + + //copy results back to java + result = e->NewByteArray (recvBufferActualSize); + if (result != NULL) + { + e->SetByteArrayRegion (result, 0, recvBufferActualSize, (jbyte *) recvBuffer); + } + + e->ReleaseByteArrayElements (data, (jbyte *) buf, JNI_ABORT); + ALOGD("%s: exit: recv len=%ld", __FUNCTION__, recvBufferActualSize); + return result; +} + + +/******************************************************************************* +** +** Function: nativeNfcSecureElement_doGetUid +** +** Description: Get the secure element's unique ID. +** e: JVM environment. +** o: Java object. +** handle: Handle of secure element. +** +** Returns: Secure element's unique ID. +** +*******************************************************************************/ +static jbyteArray nativeNfcSecureElement_doGetUid (JNIEnv* e, jobject o, jint handle) +{ + ALOGD("%s: enter; handle=0x%X", __FUNCTION__, handle); + jbyteArray secureElementUid = NULL; + + SecureElement::getInstance ().getUiccId (handle, secureElementUid); + + ALOGD("%s: exit", __FUNCTION__); + return secureElementUid; +} + + +/******************************************************************************* +** +** Function: nativeNfcSecureElement_doGetTechList +** +** Description: Get a list of technologies that the secure element supports. +** e: JVM environment. +** o: Java object. +** handle: Handle of secure element. +** +** Returns: Array of technologies. +** +*******************************************************************************/ +static jintArray nativeNfcSecureElement_doGetTechList (JNIEnv* e, jobject o, jint handle) +{ + ALOGD("%s: enter; handle=0x%X", __FUNCTION__, handle); + jintArray techList = NULL; + + SecureElement::getInstance().getTechnologyList (handle, techList); + + ALOGD("%s: exit", __FUNCTION__); + return techList; +} + + +/***************************************************************************** +** +** Description: JNI functions +** +*****************************************************************************/ +static JNINativeMethod gMethods[] = +{ + {"doNativeOpenSecureElementConnection", "()I", (void *) nativeNfcSecureElement_doOpenSecureElementConnection}, + {"doNativeDisconnectSecureElementConnection", "(I)Z", (void *) nativeNfcSecureElement_doDisconnectSecureElementConnection}, + {"doTransceive", "(I[B)[B", (void *) nativeNfcSecureElement_doTransceive}, + {"doGetUid", "(I)[B", (void *) nativeNfcSecureElement_doGetUid}, + {"doGetTechList", "(I)[I", (void *) nativeNfcSecureElement_doGetTechList}, +}; + + +/******************************************************************************* +** +** Function: register_com_android_nfc_NativeNfcSecureElement +** +** Description: Regisgter JNI functions with Java Virtual Machine. +** e: Environment of JVM. +** +** Returns: Status of registration. +** +*******************************************************************************/ +int register_com_android_nfc_NativeNfcSecureElement(JNIEnv *e) +{ + return jniRegisterNativeMethods(e, gNativeNfcSecureElementClassName, + gMethods, NELEM(gMethods)); +} + + +} // namespace android + diff --git a/nci/jni/NfcJniUtil.cpp b/nci/jni/NfcJniUtil.cpp new file mode 100755 index 0000000..9921cae --- /dev/null +++ b/nci/jni/NfcJniUtil.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "NfcJniUtil.h" +#include <errno.h> + + +/******************************************************************************* +** +** Function: JNI_OnLoad +** +** Description: Register all JNI functions with Java Virtual Machine. +** jvm: Java Virtual Machine. +** reserved: Not used. +** +** Returns: JNI version. +** +*******************************************************************************/ +jint JNI_OnLoad (JavaVM *jvm, void *reserved) +{ + ALOGD ("%s: enter", __FUNCTION__); + JNIEnv *e = NULL; + + // Check JNI version + if (jvm->GetEnv ((void **) &e, JNI_VERSION_1_6)) + return JNI_ERR; + + if (android::register_com_android_nfc_NativeNfcManager (e) == -1) + return JNI_ERR; + if (android::register_com_android_nfc_NativeLlcpServiceSocket (e) == -1) + return JNI_ERR; + if (android::register_com_android_nfc_NativeLlcpSocket (e) == -1) + return JNI_ERR; + if (android::register_com_android_nfc_NativeNfcTag (e) == -1) + return JNI_ERR; + if (android::register_com_android_nfc_NativeLlcpConnectionlessSocket (e) == -1) + return JNI_ERR; + if (android::register_com_android_nfc_NativeP2pDevice (e) == -1) + return JNI_ERR; + if (android::register_com_android_nfc_NativeNfcSecureElement (e) == -1) + return JNI_ERR; + ALOGD ("%s: exit", __FUNCTION__); + return JNI_VERSION_1_6; +} + + +namespace android +{ + + +/******************************************************************************* +** +** Function: nfc_jni_cache_object +** +** Description: +** +** Returns: Status code. +** +*******************************************************************************/ +int nfc_jni_cache_object (JNIEnv *e, const char *className, jobject *cachedObj) +{ + jclass cls = NULL; + jobject obj = NULL; + jmethodID ctor = 0; + + cls = e->FindClass (className); + if(cls == NULL) + { + ALOGE ("%s: find class error", __FUNCTION__); + return -1; + } + + ctor = e->GetMethodID (cls, "<init>", "()V"); + obj = e->NewObject (cls, ctor); + if (obj == NULL) + { + ALOGE ("%s: create object error", __FUNCTION__); + return -1; + } + + *cachedObj = e->NewGlobalRef (obj); + if (*cachedObj == NULL) + { + e->DeleteLocalRef (obj); + ALOGE ("%s: global ref error", __FUNCTION__); + return -1; + } + e->DeleteLocalRef (obj); + return 0; +} + + +/******************************************************************************* +** +** Function: nfc_jni_get_nfc_socket_handle +** +** Description: Get the value of "mHandle" member variable. +** e: JVM environment. +** o: Java object. +** +** Returns: Value of mHandle. +** +*******************************************************************************/ +int nfc_jni_get_nfc_socket_handle (JNIEnv *e, jobject o) +{ + jclass c = NULL; + jfieldID f = 0; + + c = e->GetObjectClass (o); + f = e->GetFieldID (c, "mHandle", "I"); + return e->GetIntField (o, f); +} + + +/******************************************************************************* +** +** Function: nfc_jni_get_nat +** +** Description: Get the value of "mNative" member variable. +** e: JVM environment. +** o: Java object. +** +** Returns: Pointer to the value of mNative. +** +*******************************************************************************/ +struct nfc_jni_native_data* nfc_jni_get_nat(JNIEnv *e, jobject o) +{ + jclass c = NULL; + jfieldID f = 0; + + /* Retrieve native structure address */ + c = e->GetObjectClass(o); + f = e->GetFieldID(c, "mNative", "I"); + return (struct nfc_jni_native_data*)e->GetIntField(o, f); +} + + +} // namespace android diff --git a/nci/jni/NfcJniUtil.h b/nci/jni/NfcJniUtil.h new file mode 100755 index 0000000..8caa0b8 --- /dev/null +++ b/nci/jni/NfcJniUtil.h @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#define LOG_TAG "BrcmNfcJni" +#include <JNIHelp.h> +#include <jni.h> +#include <pthread.h> +#include <sys/queue.h> +#include <semaphore.h> + + +/* Discovery modes -- keep in sync with NFCManager.DISCOVERY_MODE_* */ +#define DISCOVERY_MODE_TAG_READER 0 +#define DISCOVERY_MODE_NFCIP1 1 +#define DISCOVERY_MODE_CARD_EMULATION 2 +#define DISCOVERY_MODE_TABLE_SIZE 3 + +#define DISCOVERY_MODE_DISABLED 0 +#define DISCOVERY_MODE_ENABLED 1 + +#define MODE_P2P_TARGET 0 +#define MODE_P2P_INITIATOR 1 + + +/* Properties values */ +#define PROPERTY_LLCP_LTO 0 +#define PROPERTY_LLCP_MIU 1 +#define PROPERTY_LLCP_WKS 2 +#define PROPERTY_LLCP_OPT 3 +#define PROPERTY_NFC_DISCOVERY_A 4 +#define PROPERTY_NFC_DISCOVERY_B 5 +#define PROPERTY_NFC_DISCOVERY_F 6 +#define PROPERTY_NFC_DISCOVERY_15693 7 +#define PROPERTY_NFC_DISCOVERY_NCFIP 8 + + +/* Error codes */ +#define ERROR_BUFFER_TOO_SMALL -12 +#define ERROR_INSUFFICIENT_RESOURCES -9 + + +/* Pre-defined tag type values. These must match the values in + * Ndef.java in the framework. + */ +#define NDEF_UNKNOWN_TYPE -1 +#define NDEF_TYPE1_TAG 1 +#define NDEF_TYPE2_TAG 2 +#define NDEF_TYPE3_TAG 3 +#define NDEF_TYPE4_TAG 4 +#define NDEF_MIFARE_CLASSIC_TAG 101 + + +/* Pre-defined card read/write state values. These must match the values in + * Ndef.java in the framework. + */ +#define NDEF_MODE_READ_ONLY 1 +#define NDEF_MODE_READ_WRITE 2 +#define NDEF_MODE_UNKNOWN 3 + + +/* Name strings for target types. These *must* match the values in TagTechnology.java */ +#define TARGET_TYPE_UNKNOWN -1 +#define TARGET_TYPE_ISO14443_3A 1 +#define TARGET_TYPE_ISO14443_3B 2 +#define TARGET_TYPE_ISO14443_4 3 +#define TARGET_TYPE_FELICA 4 +#define TARGET_TYPE_ISO15693 5 +#define TARGET_TYPE_NDEF 6 +#define TARGET_TYPE_NDEF_FORMATABLE 7 +#define TARGET_TYPE_MIFARE_CLASSIC 8 +#define TARGET_TYPE_MIFARE_UL 9 + + +//define a few NXP error codes that NFC service expects; +//see external/libnfc-nxp/src/phLibNfcStatus.h; +//see external/libnfc-nxp/inc/phNfcStatus.h +#define NFCSTATUS_SUCCESS (0x0000) +#define NFCSTATUS_FAILED (0x00FF) + +//default general trasceive timeout in millisecond +#define DEFAULT_GENERAL_TRANS_TIMEOUT 1000 + +struct nfc_jni_native_data +{ + /* Thread handle */ + pthread_t thread; + int running; + + /* Our VM */ + JavaVM *vm; + int env_version; + + /* Reference to the NFCManager instance */ + jobject manager; + + /* Cached objects */ + jobject cached_NfcTag; + jobject cached_P2pDevice; + + /* Secure Element selected */ + int seId; + + /* LLCP params */ + int lto; + int miu; + int wks; + int opt; + + int tech_mask; + + /* Tag detected */ + jobject tag; + + int tHandle; + int tProtocols[16]; + int handles[16]; +}; + + +extern "C" +{ + jint JNI_OnLoad(JavaVM *jvm, void *reserved); +} + + +namespace android +{ + int nfc_jni_cache_object (JNIEnv *e, const char *clsname, jobject *cached_obj); + int nfc_jni_get_nfc_socket_handle (JNIEnv *e, jobject o); + struct nfc_jni_native_data* nfc_jni_get_nat (JNIEnv *e, jobject o); + int register_com_android_nfc_NativeNfcManager (JNIEnv *e); + int register_com_android_nfc_NativeNfcTag (JNIEnv *e); + int register_com_android_nfc_NativeP2pDevice (JNIEnv *e); + int register_com_android_nfc_NativeLlcpConnectionlessSocket (JNIEnv *e); + int register_com_android_nfc_NativeLlcpServiceSocket (JNIEnv *e); + int register_com_android_nfc_NativeLlcpSocket (JNIEnv *e); + int register_com_android_nfc_NativeNfcSecureElement (JNIEnv *e); +} // namespace android diff --git a/nci/jni/NfcTag.cpp b/nci/jni/NfcTag.cpp new file mode 100755 index 0000000..b94355f --- /dev/null +++ b/nci/jni/NfcTag.cpp @@ -0,0 +1,1325 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Tag-reading, tag-writing operations. + */ +#include "OverrideLog.h" +#include "NfcTag.h" +#include "JavaClassConstants.h" +extern "C" +{ + #include "rw_int.h" +} + + +/******************************************************************************* +** +** Function: NfcTag +** +** Description: Initialize member variables. +** +** Returns: None +** +*******************************************************************************/ +NfcTag::NfcTag () +: mNativeData (NULL), + mActivationState (Idle), + mProtocol(NFC_PROTOCOL_UNKNOWN), + mNumTechList (0), + mtT1tMaxMessageSize (0), + mReadCompletedStatus (NFA_STATUS_OK), + mLastKovioUidLen (0), + mNdefDetectionTimedOut (false) +{ + memset (mTechList, 0, sizeof(mTechList)); + memset (mTechHandles, 0, sizeof(mTechHandles)); + memset (mTechLibNfcTypes, 0, sizeof(mTechLibNfcTypes)); + memset (mTechParams, 0, sizeof(mTechParams)); + memset(mLastKovioUid, 0, NFC_KOVIO_MAX_LEN); +} + + +/******************************************************************************* +** +** Function: getInstance +** +** Description: Get a reference to the singleton NfcTag object. +** +** Returns: Reference to NfcTag object. +** +*******************************************************************************/ +NfcTag& NfcTag::getInstance () +{ + static NfcTag tag; + return tag; +} + + +/******************************************************************************* +** +** Function: initialize +** +** Description: Reset member variables. +** native: Native data. +** +** Returns: None +** +*******************************************************************************/ +void NfcTag::initialize (nfc_jni_native_data* native) +{ + mNativeData = native; + mActivationState = Idle; + mProtocol = NFC_PROTOCOL_UNKNOWN; + mNumTechList = 0; + mtT1tMaxMessageSize = 0; + mReadCompletedStatus = NFA_STATUS_OK; + resetTechnologies (); +} + + +/******************************************************************************* +** +** Function: abort +** +** Description: Unblock all operations. +** +** Returns: None +** +*******************************************************************************/ +void NfcTag::abort () +{ + SyncEventGuard g (mReadCompleteEvent); + mReadCompleteEvent.notifyOne (); +} + + +/******************************************************************************* +** +** Function: getActivationState +** +** Description: What is the current state: Idle, Sleep, or Activated. +** +** Returns: Idle, Sleep, or Activated. +** +*******************************************************************************/ +NfcTag::ActivationState NfcTag::getActivationState () +{ + return mActivationState; +} + + +/******************************************************************************* +** +** Function: setDeactivationState +** +** Description: Set the current state: Idle or Sleep. +** deactivated: state of deactivation. +** +** Returns: None. +** +*******************************************************************************/ +void NfcTag::setDeactivationState (tNFA_DEACTIVATED& deactivated) +{ + static const char fn [] = "NfcTag::setDeactivationState"; + mActivationState = Idle; + mNdefDetectionTimedOut = false; + if (deactivated.type == NFA_DEACTIVATE_TYPE_SLEEP) + mActivationState = Sleep; + ALOGD ("%s: state=%u", fn, mActivationState); +} + + +/******************************************************************************* +** +** Function: setActivationState +** +** Description: Set the current state to Active. +** +** Returns: None. +** +*******************************************************************************/ +void NfcTag::setActivationState () +{ + static const char fn [] = "NfcTag::setActivationState"; + mNdefDetectionTimedOut = false; + mActivationState = Active; + ALOGD ("%s: state=%u", fn, mActivationState); +} + + +/******************************************************************************* +** +** Function: getProtocol +** +** Description: Get the protocol of the current tag. +** +** Returns: Protocol number. +** +*******************************************************************************/ +tNFC_PROTOCOL NfcTag::getProtocol() +{ + return mProtocol; +} + +/******************************************************************************* +** +** Function TimeDiff +** +** Description Computes time difference in milliseconds. +** +** Returns Time difference in milliseconds +** +*******************************************************************************/ +UINT32 TimeDiff(timespec start, timespec end) +{ + timespec temp; + if ((end.tv_nsec-start.tv_nsec)<0) + { + temp.tv_sec = end.tv_sec-start.tv_sec-1; + temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; + } + else + { + temp.tv_sec = end.tv_sec-start.tv_sec; + temp.tv_nsec = end.tv_nsec-start.tv_nsec; + } + + return (temp.tv_sec * 1000) + (temp.tv_nsec / 1000000); +} + +/******************************************************************************* +** +** Function: IsSameKovio +** +** Description: Checks if tag activate is the same (UID) Kovio tag previously +** activated. This is needed due to a problem with some Kovio +** tags re-activating multiple times. +** activationData: data from activation. +** +** Returns: true if the activation is from the same tag previously +** activated, false otherwise +** +*******************************************************************************/ +bool NfcTag::IsSameKovio(tNFA_ACTIVATED& activationData) +{ + static const char fn [] = "NfcTag::IsSameKovio"; + ALOGD ("%s: enter", fn); + tNFC_ACTIVATE_DEVT& rfDetail = activationData.activate_ntf; + + if (rfDetail.protocol != NFC_PROTOCOL_KOVIO) + return false; + + memcpy (&(mTechParams[0]), &(rfDetail.rf_tech_param), sizeof(rfDetail.rf_tech_param)); + if (mTechParams [0].mode != NFC_DISCOVERY_TYPE_POLL_KOVIO) + return false; + + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + + bool rVal = false; + if (mTechParams[0].param.pk.uid_len == mLastKovioUidLen) + { + if (memcmp(mLastKovioUid, &mTechParams [0].param.pk.uid, mTechParams[0].param.pk.uid_len) == 0) + { + //same tag + if (TimeDiff(mLastKovioTime, now) < 500) + { + // same tag within 500 ms, ignore activation + rVal = true; + } + } + } + + // save Kovio tag info + if (!rVal) + { + if ((mLastKovioUidLen = mTechParams[0].param.pk.uid_len) > NFC_KOVIO_MAX_LEN) + mLastKovioUidLen = NFC_KOVIO_MAX_LEN; + memcpy(mLastKovioUid, mTechParams[0].param.pk.uid, mLastKovioUidLen); + } + mLastKovioTime = now; + ALOGD ("%s: exit, is same Kovio=%d", fn, rVal); + return rVal; +} + +/******************************************************************************* +** +** Function: discoverTechnologies +** +** Description: Discover the technologies that NFC service needs by interpreting +** the data strucutures from the stack. +** activationData: data from activation. +** +** Returns: None +** +*******************************************************************************/ +void NfcTag::discoverTechnologies (tNFA_ACTIVATED& activationData) +{ + static const char fn [] = "NfcTag::discoverTechnologies (activation)"; + ALOGD ("%s: enter", fn); + tNFC_ACTIVATE_DEVT& rfDetail = activationData.activate_ntf; + + mNumTechList = 0; + mTechHandles [mNumTechList] = rfDetail.rf_disc_id; + mTechLibNfcTypes [mNumTechList] = rfDetail.protocol; + + //save the stack's data structure for interpretation later + memcpy (&(mTechParams[mNumTechList]), &(rfDetail.rf_tech_param), sizeof(rfDetail.rf_tech_param)); + + switch (rfDetail.protocol) + { + case NFC_PROTOCOL_T1T: + mTechList [mNumTechList] = TARGET_TYPE_ISO14443_3A; //is TagTechnology.NFC_A by Java API + break; + + case NFC_PROTOCOL_T2T: + mTechList [mNumTechList] = TARGET_TYPE_ISO14443_3A; //is TagTechnology.NFC_A by Java API + // could be MifFare UL or Classic or Kovio + { + // need to look at first byte of uid to find manuf. + tNFC_RF_TECH_PARAMS tech_params; + memcpy (&tech_params, &(rfDetail.rf_tech_param), sizeof(rfDetail.rf_tech_param)); + + if ((tech_params.param.pa.nfcid1[0] == 0x04 && rfDetail.rf_tech_param.param.pa.sel_rsp == 0) || + rfDetail.rf_tech_param.param.pa.sel_rsp == 0x18 || + rfDetail.rf_tech_param.param.pa.sel_rsp == 0x08) + { + if (rfDetail.rf_tech_param.param.pa.sel_rsp == 0) + { + mNumTechList++; + mTechHandles [mNumTechList] = rfDetail.rf_disc_id; + mTechLibNfcTypes [mNumTechList] = rfDetail.protocol; + //save the stack's data structure for interpretation later + memcpy (&(mTechParams[mNumTechList]), &(rfDetail.rf_tech_param), sizeof(rfDetail.rf_tech_param)); + mTechList [mNumTechList] = TARGET_TYPE_MIFARE_UL; //is TagTechnology.MIFARE_ULTRALIGHT by Java API + } + } + } + break; + + case NFC_PROTOCOL_T3T: + mTechList [mNumTechList] = TARGET_TYPE_FELICA; + break; + + case NFC_PROTOCOL_ISO_DEP: //type-4 tag uses technology ISO-DEP and technology A or B + mTechList [mNumTechList] = TARGET_TYPE_ISO14443_4; //is TagTechnology.ISO_DEP by Java API + if ( (rfDetail.rf_tech_param.mode == NFC_DISCOVERY_TYPE_POLL_A) || + (rfDetail.rf_tech_param.mode == NFC_DISCOVERY_TYPE_POLL_A_ACTIVE) || + (rfDetail.rf_tech_param.mode == NFC_DISCOVERY_TYPE_LISTEN_A) || + (rfDetail.rf_tech_param.mode == NFC_DISCOVERY_TYPE_LISTEN_A_ACTIVE) ) + { + mNumTechList++; + mTechHandles [mNumTechList] = rfDetail.rf_disc_id; + mTechLibNfcTypes [mNumTechList] = rfDetail.protocol; + mTechList [mNumTechList] = TARGET_TYPE_ISO14443_3A; //is TagTechnology.NFC_A by Java API + //save the stack's data structure for interpretation later + memcpy (&(mTechParams[mNumTechList]), &(rfDetail.rf_tech_param), sizeof(rfDetail.rf_tech_param)); + } + else if ( (rfDetail.rf_tech_param.mode == NFC_DISCOVERY_TYPE_POLL_B) || + (rfDetail.rf_tech_param.mode == NFC_DISCOVERY_TYPE_POLL_B_PRIME) || + (rfDetail.rf_tech_param.mode == NFC_DISCOVERY_TYPE_LISTEN_B) || + (rfDetail.rf_tech_param.mode == NFC_DISCOVERY_TYPE_LISTEN_B_PRIME) ) + { + mNumTechList++; + mTechHandles [mNumTechList] = rfDetail.rf_disc_id; + mTechLibNfcTypes [mNumTechList] = rfDetail.protocol; + mTechList [mNumTechList] = TARGET_TYPE_ISO14443_3B; //is TagTechnology.NFC_B by Java API + //save the stack's data structure for interpretation later + memcpy (&(mTechParams[mNumTechList]), &(rfDetail.rf_tech_param), sizeof(rfDetail.rf_tech_param)); + } + break; + + case NFC_PROTOCOL_15693: //is TagTechnology.NFC_V by Java API + mTechList [mNumTechList] = TARGET_TYPE_ISO15693; + break; + + case NFC_PROTOCOL_KOVIO: + ALOGE ("%s: Kovio", fn); + mNumTechList--; // no tech classes for Kovio + break; + + default: + ALOGE ("%s: unknown protocol ????", fn); + mTechList [mNumTechList] = TARGET_TYPE_UNKNOWN; + break; + } + + mNumTechList++; + for (int i=0; i < mNumTechList; i++) + { + ALOGD ("%s: index=%d; tech=%d; handle=%d; nfc type=%d", fn, + i, mTechList[i], mTechHandles[i], mTechLibNfcTypes[i]); + } + ALOGD ("%s: exit", fn); +} + + +/******************************************************************************* +** +** Function: discoverTechnologies +** +** Description: Discover the technologies that NFC service needs by interpreting +** the data strucutures from the stack. +** discoveryData: data from discovery events(s). +** +** Returns: None +** +*******************************************************************************/ +void NfcTag::discoverTechnologies (tNFA_DISC_RESULT& discoveryData) +{ + static const char fn [] = "NfcTag::discoverTechnologies (discovery)"; + tNFC_RESULT_DEVT& discovery_ntf = discoveryData.discovery_ntf; + + ALOGD ("%s: enter: rf disc. id=%u; protocol=%u, mNumTechList=%u", fn, discovery_ntf.rf_disc_id, discovery_ntf.protocol, mNumTechList); + if (mNumTechList >= MAX_NUM_TECHNOLOGY) + { + ALOGE ("%s: exceed max=%d", fn, MAX_NUM_TECHNOLOGY); + goto TheEnd; + } + mTechHandles [mNumTechList] = discovery_ntf.rf_disc_id; + mTechLibNfcTypes [mNumTechList] = discovery_ntf.protocol; + + //save the stack's data structure for interpretation later + memcpy (&(mTechParams[mNumTechList]), &(discovery_ntf.rf_tech_param), sizeof(discovery_ntf.rf_tech_param)); + + switch (discovery_ntf.protocol) + { + case NFC_PROTOCOL_T1T: + mTechList [mNumTechList] = TARGET_TYPE_ISO14443_3A; //is TagTechnology.NFC_A by Java API + break; + + case NFC_PROTOCOL_T2T: + mTechList [mNumTechList] = TARGET_TYPE_ISO14443_3A; //is TagTechnology.NFC_A by Java API + //type-2 tags are identitical to Mifare Ultralight, so Ultralight is also discovered + if (discovery_ntf.rf_tech_param.param.pa.sel_rsp == 0) + { + // mifare Ultralight + mNumTechList++; + mTechHandles [mNumTechList] = discovery_ntf.rf_disc_id; + mTechLibNfcTypes [mNumTechList] = discovery_ntf.protocol; + mTechList [mNumTechList] = TARGET_TYPE_MIFARE_UL; //is TagTechnology.MIFARE_ULTRALIGHT by Java API + } + + //save the stack's data structure for interpretation later + memcpy (&(mTechParams[mNumTechList]), &(discovery_ntf.rf_tech_param), sizeof(discovery_ntf.rf_tech_param)); + break; + + case NFC_PROTOCOL_T3T: + mTechList [mNumTechList] = TARGET_TYPE_FELICA; + break; + + case NFC_PROTOCOL_ISO_DEP: //type-4 tag uses technology ISO-DEP and technology A or B + mTechList [mNumTechList] = TARGET_TYPE_ISO14443_4; //is TagTechnology.ISO_DEP by Java API + if ( (discovery_ntf.rf_tech_param.mode == NFC_DISCOVERY_TYPE_POLL_A) || + (discovery_ntf.rf_tech_param.mode == NFC_DISCOVERY_TYPE_POLL_A_ACTIVE) || + (discovery_ntf.rf_tech_param.mode == NFC_DISCOVERY_TYPE_LISTEN_A) || + (discovery_ntf.rf_tech_param.mode == NFC_DISCOVERY_TYPE_LISTEN_A_ACTIVE) ) + { + mNumTechList++; + mTechHandles [mNumTechList] = discovery_ntf.rf_disc_id; + mTechLibNfcTypes [mNumTechList] = discovery_ntf.protocol; + mTechList [mNumTechList] = TARGET_TYPE_ISO14443_3A; //is TagTechnology.NFC_A by Java API + //save the stack's data structure for interpretation later + memcpy (&(mTechParams[mNumTechList]), &(discovery_ntf.rf_tech_param), sizeof(discovery_ntf.rf_tech_param)); + } + else if ( (discovery_ntf.rf_tech_param.mode == NFC_DISCOVERY_TYPE_POLL_B) || + (discovery_ntf.rf_tech_param.mode == NFC_DISCOVERY_TYPE_POLL_B_PRIME) || + (discovery_ntf.rf_tech_param.mode == NFC_DISCOVERY_TYPE_LISTEN_B) || + (discovery_ntf.rf_tech_param.mode == NFC_DISCOVERY_TYPE_LISTEN_B_PRIME) ) + { + mNumTechList++; + mTechHandles [mNumTechList] = discovery_ntf.rf_disc_id; + mTechLibNfcTypes [mNumTechList] = discovery_ntf.protocol; + mTechList [mNumTechList] = TARGET_TYPE_ISO14443_3B; //is TagTechnology.NFC_B by Java API + //save the stack's data structure for interpretation later + memcpy (&(mTechParams[mNumTechList]), &(discovery_ntf.rf_tech_param), sizeof(discovery_ntf.rf_tech_param)); + } + break; + + case NFC_PROTOCOL_15693: //is TagTechnology.NFC_V by Java API + mTechList [mNumTechList] = TARGET_TYPE_ISO15693; + break; + + default: + ALOGE ("%s: unknown protocol ????", fn); + mTechList [mNumTechList] = TARGET_TYPE_UNKNOWN; + break; + } + + mNumTechList++; + if (discovery_ntf.more == FALSE) + { + for (int i=0; i < mNumTechList; i++) + { + ALOGD ("%s: index=%d; tech=%d; handle=%d; nfc type=%d", fn, + i, mTechList[i], mTechHandles[i], mTechLibNfcTypes[i]); + } + } + +TheEnd: + ALOGD ("%s: exit", fn); +} + + +/******************************************************************************* +** +** Function: createNativeNfcTag +** +** Description: Create a brand new Java NativeNfcTag object; +** fill the objects's member variables with data; +** notify NFC service; +** activationData: data from activation. +** +** Returns: None +** +*******************************************************************************/ +void NfcTag::createNativeNfcTag (tNFA_ACTIVATED& activationData) +{ + static const char fn [] = "NfcTag::createNativeNfcTag"; + ALOGD ("%s: enter", fn); + JNIEnv* e = NULL; + jclass tag_cls = NULL; + jmethodID ctor = NULL; + jobject tag = NULL; + + //acquire a pointer to the Java virtual machine + mNativeData->vm->AttachCurrentThread (&e, NULL); + if (e == NULL) + { + ALOGE("%s: jni env is null", fn); + goto TheEnd; + } + + tag_cls = e->GetObjectClass (mNativeData->cached_NfcTag); + if (e->ExceptionCheck()) + { + e->ExceptionClear(); + ALOGE("%s: failed to get class", fn); + goto TheEnd; + } + + //create a new Java NativeNfcTag object + ctor = e->GetMethodID (tag_cls, "<init>", "()V"); + tag = e->NewObject (tag_cls, ctor); + + //fill NativeNfcTag's mProtocols, mTechList, mTechHandles, mTechLibNfcTypes + fillNativeNfcTagMembers1 (e, tag_cls, tag); + + //fill NativeNfcTag's members: mHandle, mConnectedTechnology + fillNativeNfcTagMembers2 (e, tag_cls, tag, activationData); + + //fill NativeNfcTag's members: mTechPollBytes + fillNativeNfcTagMembers3 (e, tag_cls, tag, activationData); + + //fill NativeNfcTag's members: mTechActBytes + fillNativeNfcTagMembers4 (e, tag_cls, tag, activationData); + + //fill NativeNfcTag's members: mUid + fillNativeNfcTagMembers5 (e, tag_cls, tag, activationData); + + if (mNativeData->tag != NULL) { + e->DeleteGlobalRef (mNativeData->tag); + } + mNativeData->tag = e->NewGlobalRef (tag); + + //notify NFC service about this new tag + ALOGD ("%s: try notify nfc service", fn); + e->CallVoidMethod (mNativeData->manager, android::gCachedNfcManagerNotifyNdefMessageListeners, tag); + if (e->ExceptionCheck()) + { + e->ExceptionClear(); + ALOGE ("%s: fail notify nfc service", fn); + } + e->DeleteLocalRef (tag); + +TheEnd: + mNativeData->vm->DetachCurrentThread (); + ALOGD ("%s: exit", fn); +} + + +/******************************************************************************* +** +** Function: fillNativeNfcTagMembers1 +** +** Description: Fill NativeNfcTag's members: mProtocols, mTechList, mTechHandles, mTechLibNfcTypes. +** e: JVM environment. +** tag_cls: Java NativeNfcTag class. +** tag: Java NativeNfcTag object. +** +** Returns: None +** +*******************************************************************************/ +void NfcTag::fillNativeNfcTagMembers1 (JNIEnv* e, jclass tag_cls, jobject tag) +{ + static const char fn [] = "NfcTag::fillNativeNfcTagMembers1"; + ALOGD ("%s", fn); + jfieldID f = NULL; + + //create objects that represent NativeNfcTag's member variables + jintArray techList = e->NewIntArray (mNumTechList); + jintArray handleList = e->NewIntArray (mNumTechList); + jintArray typeList = e->NewIntArray (mNumTechList); + + jint* technologies = e->GetIntArrayElements (techList, NULL); + jint* handles = e->GetIntArrayElements (handleList, NULL); + jint* types = e->GetIntArrayElements (typeList, NULL); + for (int i = 0; i < mNumTechList; i++) + { + mNativeData->tProtocols [i] = mTechLibNfcTypes [i]; + mNativeData->handles [i] = mTechHandles [i]; + technologies [i] = mTechList [i]; + handles [i] = mTechHandles [i]; + types [i] = mTechLibNfcTypes [i]; + } + e->ReleaseIntArrayElements (techList, technologies, 0); + e->ReleaseIntArrayElements (handleList, handles, 0); + e->ReleaseIntArrayElements (typeList, types, 0); + + 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); +} + + +/******************************************************************************* +** +** Function: fillNativeNfcTagMembers2 +** +** Description: Fill NativeNfcTag's members: mConnectedTechIndex or mConnectedTechnology. +** The original Google's implementation is in set_target_pollBytes( +** in com_android_nfc_NativeNfcTag.cpp; +** e: JVM environment. +** tag_cls: Java NativeNfcTag class. +** tag: Java NativeNfcTag object. +** activationData: data from activation. +** +** Returns: None +** +*******************************************************************************/ +//fill NativeNfcTag's members: mHandle, mConnectedTechnology +void NfcTag::fillNativeNfcTagMembers2 (JNIEnv* e, jclass tag_cls, jobject tag, tNFA_ACTIVATED& activationData) +{ + static const char fn [] = "NfcTag::fillNativeNfcTagMembers2"; + ALOGD ("%s", fn); + jfieldID f = NULL; + + f = e->GetFieldID (tag_cls, "mConnectedTechIndex", "I"); + e->SetIntField (tag, f, (jint) 0); +} + + +/******************************************************************************* +** +** Function: fillNativeNfcTagMembers3 +** +** Description: Fill NativeNfcTag's members: mTechPollBytes. +** The original Google's implementation is in set_target_pollBytes( +** in com_android_nfc_NativeNfcTag.cpp; +** e: JVM environment. +** tag_cls: Java NativeNfcTag class. +** tag: Java NativeNfcTag object. +** activationData: data from activation. +** +** Returns: None +** +*******************************************************************************/ +void NfcTag::fillNativeNfcTagMembers3 (JNIEnv* e, jclass tag_cls, jobject tag, tNFA_ACTIVATED& activationData) +{ + static const char fn [] = "NfcTag::fillNativeNfcTagMembers3"; + jfieldID f = NULL; + jbyteArray pollBytes = e->NewByteArray (0); + jobjectArray techPollBytes = e->NewObjectArray (mNumTechList, e->GetObjectClass(pollBytes), 0); + int len = 0; + + for (int i = 0; i < mNumTechList; i++) + { + ALOGD ("%s: index=%d; rf tech params mode=%u", fn, i, mTechParams [i].mode); + switch (mTechParams [i].mode) + { + case NFC_DISCOVERY_TYPE_POLL_A: + case NFC_DISCOVERY_TYPE_POLL_A_ACTIVE: + case NFC_DISCOVERY_TYPE_LISTEN_A: + case NFC_DISCOVERY_TYPE_LISTEN_A_ACTIVE: + ALOGD ("%s: tech A", fn); + pollBytes = e->NewByteArray (2); + e->SetByteArrayRegion (pollBytes, 0, 2, + (jbyte*) mTechParams [i].param.pa.sens_res); + break; + + case NFC_DISCOVERY_TYPE_POLL_B: + case NFC_DISCOVERY_TYPE_POLL_B_PRIME: + case NFC_DISCOVERY_TYPE_LISTEN_B: + case NFC_DISCOVERY_TYPE_LISTEN_B_PRIME: + if (mTechList [i] == TARGET_TYPE_ISO14443_3B) //is TagTechnology.NFC_B by Java API + { + /***************** + see NFC Forum Digital Protocol specification; section 5.6.2; + in SENSB_RES response, byte 6 through 9 is Application Data, byte 10-12 or 13 is Protocol Info; + used by public API: NfcB.getApplicationData(), NfcB.getProtocolInfo(); + *****************/ + ALOGD ("%s: tech B; TARGET_TYPE_ISO14443_3B", fn); + len = mTechParams [i].param.pb.sensb_res_len; + len = len - 4; //subtract 4 bytes for NFCID0 at byte 2 through 5 + pollBytes = e->NewByteArray (len); + e->SetByteArrayRegion (pollBytes, 0, len, (jbyte*) (mTechParams [i].param.pb.sensb_res+4)); + } + else + pollBytes = e->NewByteArray (0); + break; + + case NFC_DISCOVERY_TYPE_POLL_F: + case NFC_DISCOVERY_TYPE_POLL_F_ACTIVE: + case NFC_DISCOVERY_TYPE_LISTEN_F: + case NFC_DISCOVERY_TYPE_LISTEN_F_ACTIVE: + { + /**************** + see NFC Forum Type 3 Tag Operation Specification; sections 2.3.2, 2.3.1.2; + see NFC Forum Digital Protocol Specification; sections 6.6.2; + PMm: manufacture parameter; 8 bytes; + System Code: 2 bytes; + ****************/ + ALOGD ("%s: tech F", fn); + UINT8 result [10]; //return result to NFC service + memset (result, 0, sizeof(result)); + len = 10; + + /**** + for (int ii = 0; ii < mTechParams [i].param.pf.sensf_res_len; ii++) + { + ALOGD ("%s: tech F, sendf_res[%d]=%d (0x%x)", + fn, ii, mTechParams [i].param.pf.sensf_res[ii],mTechParams [i].param.pf.sensf_res[ii]); + } + ***/ + memcpy (result, mTechParams [i].param.pf.sensf_res + 8, 8); //copy PMm + if (activationData.params.t3t.num_system_codes > 0) //copy the first System Code + { + UINT16 systemCode = *(activationData.params.t3t.p_system_codes); + result [8] = (UINT8) (systemCode >> 8); + result [9] = (UINT8) systemCode; + ALOGD ("%s: tech F; sys code=0x%X 0x%X", fn, result [8], result [9]); + } + pollBytes = e->NewByteArray (len); + e->SetByteArrayRegion (pollBytes, 0, len, (jbyte*) result); + } + break; + + case NFC_DISCOVERY_TYPE_POLL_ISO15693: + case NFC_DISCOVERY_TYPE_LISTEN_ISO15693: + { + ALOGD ("%s: tech iso 15693", fn); + //iso 15693 response flags: 1 octet + //iso 15693 Data Structure Format Identifier (DSF ID): 1 octet + //used by public API: NfcV.getDsfId(), NfcV.getResponseFlags(); + uint8_t data [2]= {activationData.params.i93.afi, activationData.params.i93.dsfid}; + pollBytes = e->NewByteArray (2); + e->SetByteArrayRegion (pollBytes, 0, 2, (jbyte *) data); + } + break; + + default: + ALOGE ("%s: tech unknown ????", fn); + pollBytes = e->NewByteArray(0); + break; + } //switch: every type of technology + e->SetObjectArrayElement (techPollBytes, i, pollBytes); + } //for: every technology in the array + f = e->GetFieldID (tag_cls, "mTechPollBytes", "[[B"); + e->SetObjectField (tag, f, techPollBytes); +} + + +/******************************************************************************* +** +** Function: fillNativeNfcTagMembers4 +** +** Description: Fill NativeNfcTag's members: mTechActBytes. +** The original Google's implementation is in set_target_activationBytes() +** in com_android_nfc_NativeNfcTag.cpp; +** e: JVM environment. +** tag_cls: Java NativeNfcTag class. +** tag: Java NativeNfcTag object. +** activationData: data from activation. +** +** Returns: None +** +*******************************************************************************/ +void NfcTag::fillNativeNfcTagMembers4 (JNIEnv* e, jclass tag_cls, jobject tag, tNFA_ACTIVATED& activationData) +{ + static const char fn [] = "NfcTag::fillNativeNfcTagMembers4"; + jfieldID f = NULL; + jbyteArray actBytes = e->NewByteArray (0); + jobjectArray techActBytes = e->NewObjectArray (mNumTechList, e->GetObjectClass(actBytes), 0); + jbyteArray uid = NULL; + int len = 0; + + for (int i = 0; i < mNumTechList; i++) + { + ALOGD ("%s: index=%d", fn, i); + switch (mTechLibNfcTypes[i]) + { + case NFC_PROTOCOL_T1T: + { + ALOGD ("%s: T1T; tech A", fn); + actBytes = e->NewByteArray (1); + e->SetByteArrayRegion (actBytes, 0, 1, + (jbyte*) &mTechParams [i].param.pa.sel_rsp); + } + break; + + case NFC_PROTOCOL_T2T: + { + ALOGD ("%s: T2T; tech A", fn); + actBytes = e->NewByteArray (1); + e->SetByteArrayRegion (actBytes, 0, 1, + (jbyte*) &mTechParams [i].param.pa.sel_rsp); + } + break; + + case NFC_PROTOCOL_T3T: //felica + { + ALOGD ("%s: T3T; felica; tech F", fn); + //really, there is no data + actBytes = e->NewByteArray (0); + } + break; + + case NFC_PROTOCOL_ISO_DEP: //t4t + { + if (mTechList [i] == TARGET_TYPE_ISO14443_4) //is TagTechnology.ISO_DEP by Java API + { + if ( (mTechParams[i].mode == NFC_DISCOVERY_TYPE_POLL_A) || + (mTechParams[i].mode == NFC_DISCOVERY_TYPE_POLL_A_ACTIVE) || + (mTechParams[i].mode == NFC_DISCOVERY_TYPE_LISTEN_A) || + (mTechParams[i].mode == NFC_DISCOVERY_TYPE_LISTEN_A_ACTIVE) ) + { + //see NFC Forum Digital Protocol specification, section 11.6.2, "RATS Response"; search for "historical bytes"; + //copy historical bytes into Java object; + //the public API, IsoDep.getHistoricalBytes(), returns this data; + if (activationData.activate_ntf.intf_param.type == NFC_INTERFACE_ISO_DEP) + { + tNFC_INTF_PA_ISO_DEP& pa_iso = activationData.activate_ntf.intf_param.intf_param.pa_iso; + ALOGD ("%s: T4T; ISO_DEP for tech A; copy historical bytes; len=%u", fn, pa_iso.his_byte_len); + actBytes = e->NewByteArray (pa_iso.his_byte_len); + if (pa_iso.his_byte_len > 0) + e->SetByteArrayRegion (actBytes, 0, pa_iso.his_byte_len, (jbyte*) (pa_iso.his_byte)); + } + else + { + ALOGE ("%s: T4T; ISO_DEP for tech A; wrong interface=%u", fn, activationData.activate_ntf.intf_param.type); + actBytes = e->NewByteArray (0); + } + } + else if ( (mTechParams[i].mode == NFC_DISCOVERY_TYPE_POLL_B) || + (mTechParams[i].mode == NFC_DISCOVERY_TYPE_POLL_B_PRIME) || + (mTechParams[i].mode == NFC_DISCOVERY_TYPE_LISTEN_B) || + (mTechParams[i].mode == NFC_DISCOVERY_TYPE_LISTEN_B_PRIME) ) + { + //see NFC Forum Digital Protocol specification, section 12.6.2, "ATTRIB Response"; + //copy higher-layer response bytes into Java object; + //the public API, IsoDep.getHiLayerResponse(), returns this data; + if (activationData.activate_ntf.intf_param.type == NFC_INTERFACE_ISO_DEP) + { + tNFC_INTF_PB_ISO_DEP& pb_iso = activationData.activate_ntf.intf_param.intf_param.pb_iso; + ALOGD ("%s: T4T; ISO_DEP for tech B; copy response bytes; len=%u", fn, pb_iso.hi_info_len); + actBytes = e->NewByteArray (pb_iso.hi_info_len); + if (pb_iso.hi_info_len > 0) + e->SetByteArrayRegion (actBytes, 0, pb_iso.hi_info_len, (jbyte*) (pb_iso.hi_info)); + } + else + { + ALOGE ("%s: T4T; ISO_DEP for tech B; wrong interface=%u", fn, activationData.activate_ntf.intf_param.type); + actBytes = e->NewByteArray (0); + } + } + } + else if (mTechList [i] == TARGET_TYPE_ISO14443_3A) //is TagTechnology.NFC_A by Java API + { + ALOGD ("%s: T4T; tech A", fn); + actBytes = e->NewByteArray (1); + e->SetByteArrayRegion (actBytes, 0, 1, (jbyte*) &mTechParams [i].param.pa.sel_rsp); + } + else + { + actBytes = e->NewByteArray (0); + } + } //case NFC_PROTOCOL_ISO_DEP: //t4t + break; + + case NFC_PROTOCOL_15693: + { + ALOGD ("%s: tech iso 15693", fn); + //iso 15693 response flags: 1 octet + //iso 15693 Data Structure Format Identifier (DSF ID): 1 octet + //used by public API: NfcV.getDsfId(), NfcV.getResponseFlags(); + uint8_t data [2]= {activationData.params.i93.afi, activationData.params.i93.dsfid}; + actBytes = e->NewByteArray (2); + e->SetByteArrayRegion (actBytes, 0, 2, (jbyte *) data); + } + break; + + default: + ALOGD ("%s: tech unknown ????", fn); + actBytes = e->NewByteArray (0); + break; + }//switch + e->SetObjectArrayElement (techActBytes, i, actBytes); + } //for: every technology in the array + f = e->GetFieldID (tag_cls, "mTechActBytes", "[[B"); + e->SetObjectField (tag, f, techActBytes); +} + + +/******************************************************************************* +** +** Function: fillNativeNfcTagMembers5 +** +** Description: Fill NativeNfcTag's members: mUid. +** The original Google's implementation is in nfc_jni_Discovery_notification_callback() +** in com_android_nfc_NativeNfcManager.cpp; +** e: JVM environment. +** tag_cls: Java NativeNfcTag class. +** tag: Java NativeNfcTag object. +** activationData: data from activation. +** +** Returns: None +** +*******************************************************************************/ +void NfcTag::fillNativeNfcTagMembers5 (JNIEnv* e, jclass tag_cls, jobject tag, tNFA_ACTIVATED& activationData) +{ + static const char fn [] = "NfcTag::fillNativeNfcTagMembers5"; + jfieldID f = NULL; + int len = 0; + jbyteArray uid = NULL; + + switch (mTechParams [0].mode) + { + case NFC_DISCOVERY_TYPE_POLL_KOVIO: + ALOGD ("%s: Kovio", fn); + len = mTechParams [0].param.pk.uid_len; + uid = e->NewByteArray (len); + e->SetByteArrayRegion (uid, 0, len, + (jbyte*) &mTechParams [0].param.pk.uid); + break; + + case NFC_DISCOVERY_TYPE_POLL_A: + case NFC_DISCOVERY_TYPE_POLL_A_ACTIVE: + case NFC_DISCOVERY_TYPE_LISTEN_A: + case NFC_DISCOVERY_TYPE_LISTEN_A_ACTIVE: + ALOGD ("%s: tech A", fn); + len = mTechParams [0].param.pa.nfcid1_len; + uid = e->NewByteArray (len); + e->SetByteArrayRegion (uid, 0, len, + (jbyte*) &mTechParams [0].param.pa.nfcid1); + break; + + case NFC_DISCOVERY_TYPE_POLL_B: + case NFC_DISCOVERY_TYPE_POLL_B_PRIME: + case NFC_DISCOVERY_TYPE_LISTEN_B: + case NFC_DISCOVERY_TYPE_LISTEN_B_PRIME: + ALOGD ("%s: tech B", fn); + uid = e->NewByteArray (NFC_NFCID0_MAX_LEN); + e->SetByteArrayRegion (uid, 0, NFC_NFCID0_MAX_LEN, + (jbyte*) &mTechParams [0].param.pb.nfcid0); + break; + + case NFC_DISCOVERY_TYPE_POLL_F: + case NFC_DISCOVERY_TYPE_POLL_F_ACTIVE: + case NFC_DISCOVERY_TYPE_LISTEN_F: + case NFC_DISCOVERY_TYPE_LISTEN_F_ACTIVE: + ALOGD ("%s: tech F", fn); + uid = e->NewByteArray (NFC_NFCID2_LEN); + e->SetByteArrayRegion (uid, 0, NFC_NFCID2_LEN, + (jbyte*) &mTechParams [0].param.pf.nfcid2); + break; + + case NFC_DISCOVERY_TYPE_POLL_ISO15693: + case NFC_DISCOVERY_TYPE_LISTEN_ISO15693: + { + ALOGD ("%s: tech iso 15693", fn); + jbyte data [I93_UID_BYTE_LEN]; //8 bytes + for (int i=0; i<I93_UID_BYTE_LEN; ++i) //reverse the ID + data[i] = activationData.params.i93.uid [I93_UID_BYTE_LEN - i - 1]; + uid = e->NewByteArray (I93_UID_BYTE_LEN); + e->SetByteArrayRegion (uid, 0, I93_UID_BYTE_LEN, data); + } + break; + + default: + ALOGE ("%s: tech unknown ????", fn); + uid = e->NewByteArray (0); + break; + } //if + f = e->GetFieldID(tag_cls, "mUid", "[B"); + e->SetObjectField(tag, f, uid); +} + + +/******************************************************************************* +** +** Function: isP2pDiscovered +** +** Description: Does the peer support P2P? +** +** Returns: True if the peer supports P2P. +** +*******************************************************************************/ +bool NfcTag::isP2pDiscovered () +{ + static const char fn [] = "NfcTag::isP2pDiscovered"; + bool retval = false; + + for (int i = 0; i < mNumTechList; i++) + { + if (mTechLibNfcTypes[i] == NFA_PROTOCOL_NFC_DEP) + { + //if remote device supports P2P + ALOGD ("%s: discovered P2P", fn); + retval = true; + break; + } + } + ALOGD ("%s: return=%u", fn, retval); + return retval; +} + + +/******************************************************************************* +** +** Function: selectP2p +** +** Description: Select the preferred P2P technology if there is a choice. +** +** Returns: None +** +*******************************************************************************/ +void NfcTag::selectP2p() +{ + static const char fn [] = "NfcTag::selectP2p"; + UINT8 rfDiscoveryId = 0; + + for (int i = 0; i < mNumTechList; i++) + { + //if remote device does not support P2P, just skip it + if (mTechLibNfcTypes[i] != NFA_PROTOCOL_NFC_DEP) + continue; + + //if remote device supports tech F; + //tech F is preferred because it is faster than tech A + if ( (mTechParams[i].mode == NFC_DISCOVERY_TYPE_POLL_F) || + (mTechParams[i].mode == NFC_DISCOVERY_TYPE_POLL_F_ACTIVE) ) + { + rfDiscoveryId = mTechHandles[i]; + break; //no need to search further + } + else if ( (mTechParams[i].mode == NFC_DISCOVERY_TYPE_POLL_A) || + (mTechParams[i].mode == NFC_DISCOVERY_TYPE_POLL_A_ACTIVE) ) + { + //only choose tech A if tech F is unavailable + if (rfDiscoveryId == 0) + rfDiscoveryId = mTechHandles[i]; + } + } + + if (rfDiscoveryId > 0) + { + ALOGD ("%s: select P2P; target rf discov id=0x%X", fn, rfDiscoveryId); + tNFA_STATUS stat = NFA_Select (rfDiscoveryId, NFA_PROTOCOL_NFC_DEP, NFA_INTERFACE_NFC_DEP); + if (stat != NFA_STATUS_OK) + ALOGE ("%s: fail select P2P; error=0x%X", fn, stat); + } + else + ALOGE ("%s: cannot find P2P", fn); + resetTechnologies (); +} + + +/******************************************************************************* +** +** Function: resetTechnologies +** +** Description: Clear all data related to the technology, protocol of the tag. +** +** Returns: None +** +*******************************************************************************/ +void NfcTag::resetTechnologies () +{ + static const char fn [] = "NfcTag::resetTechnologies"; + ALOGD ("%s", fn); + mNumTechList = 0; + memset (mTechList, 0, sizeof(mTechList)); + memset (mTechHandles, 0, sizeof(mTechHandles)); + memset (mTechLibNfcTypes, 0, sizeof(mTechLibNfcTypes)); + memset (mTechParams, 0, sizeof(mTechParams)); +} + + +/******************************************************************************* +** +** Function: selectFirstTag +** +** Description: When multiple tags are discovered, just select the first one to activate. +** +** Returns: None +** +*******************************************************************************/ +void NfcTag::selectFirstTag () +{ + static const char fn [] = "NfcTag::selectFirstTag"; + ALOGD ("%s: nfa target h=0x%X; protocol=0x%X", + fn, mTechHandles [0], mTechLibNfcTypes [0]); + tNFA_INTF_TYPE rf_intf = NFA_INTERFACE_FRAME; + + if (mTechLibNfcTypes [0] == NFA_PROTOCOL_ISO_DEP) + { + rf_intf = NFA_INTERFACE_ISO_DEP; + } + else if (mTechLibNfcTypes [0] == NFA_PROTOCOL_NFC_DEP) + rf_intf = NFA_INTERFACE_NFC_DEP; + else + rf_intf = NFA_INTERFACE_FRAME; + + tNFA_STATUS stat = NFA_Select (mTechHandles [0], mTechLibNfcTypes [0], rf_intf); + if (stat != NFA_STATUS_OK) + ALOGE ("%s: fail select; error=0x%X", fn, stat); +} + + +/******************************************************************************* +** +** Function: getT1tMaxMessageSize +** +** Description: Get the maximum size (octet) that a T1T can store. +** +** Returns: Maximum size in octets. +** +*******************************************************************************/ +int NfcTag::getT1tMaxMessageSize () +{ + static const char fn [] = "NfcTag::getT1tMaxMessageSize"; + + if (mProtocol != NFC_PROTOCOL_T1T) + { + ALOGE ("%s: wrong protocol %u", fn, mProtocol); + return 0; + } + return mtT1tMaxMessageSize; +} + + +/******************************************************************************* +** +** Function: calculateT1tMaxMessageSize +** +** Description: Calculate type-1 tag's max message size based on header ROM bytes. +** activate: reference to activation data. +** +** Returns: None +** +*******************************************************************************/ +void NfcTag::calculateT1tMaxMessageSize (tNFA_ACTIVATED& activate) +{ + static const char fn [] = "NfcTag::calculateT1tMaxMessageSize"; + + //make sure the tag is type-1 + if (activate.activate_ntf.protocol != NFC_PROTOCOL_T1T) + { + mtT1tMaxMessageSize = 0; + return; + } + + //examine the first byte of header ROM bytes + switch (activate.params.t1t.hr[0]) + { + case RW_T1T_IS_TOPAZ96: + mtT1tMaxMessageSize = 90; + break; + case RW_T1T_IS_TOPAZ512: + mtT1tMaxMessageSize = 462; + break; + default: + ALOGE ("%s: unknown T1T HR0=%u", fn, activate.params.t1t.hr[0]); + mtT1tMaxMessageSize = 0; + break; + } +} + + +/******************************************************************************* +** +** Function: isMifareUltralight +** +** Description: Whether the currently activated tag is Mifare Ultralight. +** +** Returns: True if tag is Mifare Ultralight. +** +*******************************************************************************/ +bool NfcTag::isMifareUltralight () +{ + static const char fn [] = "NfcTag::isMifareUltralight"; + bool retval = false; + + for (int i =0; i < mNumTechList; i++) + { + if ( (mTechParams[i].mode == NFC_DISCOVERY_TYPE_POLL_A) || + (mTechParams[i].mode == NFC_DISCOVERY_TYPE_LISTEN_A) || + (mTechParams[i].mode == NFC_DISCOVERY_TYPE_LISTEN_A_ACTIVE) ) + { + //see NFC Digital Protocol, section 4.6.3 (SENS_RES); section 4.8.2 (SEL_RES). + //see Mifare Type Identification Procedure, section 5.1 (ATQA), 5.2 (SAK). + if ( (mTechParams[i].param.pa.sens_res[0] == 0x44) && + (mTechParams[i].param.pa.sens_res[1] == 0) ) + { + // SyncEventGuard g (mReadCompleteEvent); + // mReadCompletedStatus = NFA_STATUS_BUSY; + // ALOGD ("%s: read block 0x10", fn); + // tNFA_STATUS stat = NFA_RwT2tRead (0x10); + // if (stat == NFA_STATUS_OK) + // mReadCompleteEvent.wait (); + // + // //if read-completion status is failure, then the tag is + // //definitely Mifare Ultralight; + // //if read-completion status is OK, then the tag is + // //definitely Mifare Ultralight C; + // retval = (mReadCompletedStatus == NFA_STATUS_FAILED); + retval = true; + } + break; + } + } + ALOGD ("%s: return=%u", fn, retval); + return retval; +} + + +/******************************************************************************* +** +** Function: isT2tNackResponse +** +** Description: Whether the response is a T2T NACK response. +** See NFC Digital Protocol Technical Specification (2010-11-17). +** Chapter 9 (Type 2 Tag Platform), section 9.6 (READ). +** response: buffer contains T2T response. +** responseLen: length of the response. +** +** Returns: True if the response is NACK +** +*******************************************************************************/ +bool NfcTag::isT2tNackResponse (const UINT8* response, UINT32 responseLen) +{ + static const char fn [] = "NfcTag::isT2tNackResponse"; + bool isNack = false; + + if (responseLen == 1) + { + if (response[0] == 0xA) + isNack = false; //an ACK response, so definitely not a NACK + else + isNack = true; //assume every value is a NACK + } + ALOGD ("%s: return %u", fn, isNack); + return isNack; +} + + +/******************************************************************************* +** +** Function: isNdefDetectionTimedOut +** +** Description: Whether NDEF-detection algorithm timed out. +** +** Returns: True if NDEF-detection algorithm timed out. +** +*******************************************************************************/ +bool NfcTag::isNdefDetectionTimedOut () +{ + return mNdefDetectionTimedOut; +} + + +/******************************************************************************* +** +** Function: connectionEventHandler +** +** Description: Handle connection-related events. +** event: event code. +** data: pointer to event data. +** +** Returns: None +** +*******************************************************************************/ +void NfcTag::connectionEventHandler (UINT8 event, tNFA_CONN_EVT_DATA* data) +{ + static const char fn [] = "NfcTag::connectionEventHandler"; + + switch (event) + { + case NFA_DISC_RESULT_EVT: + { + tNFA_DISC_RESULT& disc_result = data->disc_result; + if (disc_result.status == NFA_STATUS_OK) + { + discoverTechnologies (disc_result); + } + } + break; + + case NFA_ACTIVATED_EVT: + // Only do tag detection if we are polling and it is not 'EE Direct RF' activation + // (which may happen when we are activated as a tag). + if (data->activated.activate_ntf.rf_tech_param.mode < NCI_DISCOVERY_TYPE_LISTEN_A + && data->activated.activate_ntf.intf_param.type != NFC_INTERFACE_EE_DIRECT_RF) + { + tNFA_ACTIVATED& activated = data->activated; + if (IsSameKovio(activated)) + break; + mProtocol = activated.activate_ntf.protocol; + calculateT1tMaxMessageSize (activated); + discoverTechnologies (activated); + createNativeNfcTag (activated); + } + break; + + case NFA_DEACTIVATED_EVT: + mProtocol = NFC_PROTOCOL_UNKNOWN; + resetTechnologies (); + break; + + case NFA_READ_CPLT_EVT: + { + SyncEventGuard g (mReadCompleteEvent); + mReadCompletedStatus = data->status; + mReadCompleteEvent.notifyOne (); + } + break; + + case NFA_NDEF_DETECT_EVT: + { + tNFA_NDEF_DETECT& ndef_detect = data->ndef_detect; + mNdefDetectionTimedOut = ndef_detect.status == NFA_STATUS_TIMEOUT; + if (mNdefDetectionTimedOut) + ALOGE ("%s: NDEF detection timed out", fn); + } + } +} + diff --git a/nci/jni/NfcTag.h b/nci/jni/NfcTag.h new file mode 100755 index 0000000..7fa56ee --- /dev/null +++ b/nci/jni/NfcTag.h @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Tag-reading, tag-writing operations. + */ + +#pragma once +#include "SyncEvent.h" +#include "NfcJniUtil.h" +extern "C" +{ + #include "nfa_rw_api.h" +} + + +class NfcTag +{ +public: + enum ActivationState {Idle, Sleep, Active}; + static const int MAX_NUM_TECHNOLOGY = 10; //max number of technologies supported by one or more tags + int mTechList [MAX_NUM_TECHNOLOGY]; //array of NFC technologies according to NFC service + int mTechHandles [MAX_NUM_TECHNOLOGY]; //array of tag handles according to NFC service + int mTechLibNfcTypes [MAX_NUM_TECHNOLOGY]; //array of detailed tag types according to NFC service + int mNumTechList; //current number of NFC technologies in the list + + /******************************************************************************* + ** + ** Function: NfcTag + ** + ** Description: Initialize member variables. + ** + ** Returns: None + ** + *******************************************************************************/ + NfcTag (); + + + /******************************************************************************* + ** + ** Function: getInstance + ** + ** Description: Get a reference to the singleton NfcTag object. + ** + ** Returns: Reference to NfcTag object. + ** + *******************************************************************************/ + static NfcTag& getInstance (); + + + /******************************************************************************* + ** + ** Function: initialize + ** + ** Description: Reset member variables. + ** native: Native data. + ** Returns: None + ** + *******************************************************************************/ + void initialize (nfc_jni_native_data* native); + + + /******************************************************************************* + ** + ** Function: abort + ** + ** Description: Unblock all operations. + ** + ** Returns: None + ** + *******************************************************************************/ + void abort (); + + + /******************************************************************************* + ** + ** Function: connectionEventHandler + ** + ** Description: Handle connection-related events. + ** event: event code. + ** data: pointer to event data. + ** + ** Returns: None + ** + *******************************************************************************/ + void connectionEventHandler (UINT8 event, tNFA_CONN_EVT_DATA* data); + + + /******************************************************************************* + ** + ** Function: getActivationState + ** + ** Description: What is the current state: Idle, Sleep, or Activated. + ** + ** Returns: Idle, Sleep, or Activated. + ** + *******************************************************************************/ + ActivationState getActivationState (); + + + /******************************************************************************* + ** + ** Function: setDeactivationState + ** + ** Description: Set the current state: Idle or Sleep. + ** deactivated: state of deactivation. + ** + ** Returns: None. + ** + *******************************************************************************/ + void setDeactivationState (tNFA_DEACTIVATED& deactivated); + + + /******************************************************************************* + ** + ** Function: setActivationState + ** + ** Description: Set the current state to Active. + ** + ** Returns: None. + ** + *******************************************************************************/ + void setActivationState (); + + /******************************************************************************* + ** + ** Function: getProtocol + ** + ** Description: Get the protocol of the current tag. + ** + ** Returns: Protocol number. + ** + *******************************************************************************/ + tNFC_PROTOCOL getProtocol (); + + + /******************************************************************************* + ** + ** Function: isP2pDiscovered + ** + ** Description: Does the peer support P2P? + ** + ** Returns: True if the peer supports P2P. + ** + *******************************************************************************/ + bool isP2pDiscovered (); + + + /******************************************************************************* + ** + ** Function: selectP2p + ** + ** Description: Select the preferred P2P technology if there is a choice. + ** + ** Returns: None + ** + *******************************************************************************/ + void selectP2p (); + + + /******************************************************************************* + ** + ** Function: selectFirstTag + ** + ** Description: When multiple tags are discovered, just select the first one to activate. + ** + ** Returns: None + ** + *******************************************************************************/ + void selectFirstTag (); + + + /******************************************************************************* + ** + ** Function: getT1tMaxMessageSize + ** + ** Description: Get the maximum size (octet) that a T1T can store. + ** + ** Returns: Maximum size in octets. + ** + *******************************************************************************/ + int getT1tMaxMessageSize (); + + + /******************************************************************************* + ** + ** Function: isMifareUltralight + ** + ** Description: Whether the currently activated tag is Mifare Ultralight. + ** + ** Returns: True if tag is Mifare Ultralight. + ** + *******************************************************************************/ + bool isMifareUltralight (); + + + /******************************************************************************* + ** + ** Function: isT2tNackResponse + ** + ** Description: Whether the response is a T2T NACK response. + ** See NFC Digital Protocol Technical Specification (2010-11-17). + ** Chapter 9 (Type 2 Tag Platform), section 9.6 (READ). + ** response: buffer contains T2T response. + ** responseLen: length of the response. + ** + ** Returns: True if the response is NACK + ** + *******************************************************************************/ + bool isT2tNackResponse (const UINT8* response, UINT32 responseLen); + + /******************************************************************************* + ** + ** Function: isNdefDetectionTimedOut + ** + ** Description: Whether NDEF-detection algorithm has timed out. + ** + ** Returns: True if NDEF-detection algorithm timed out. + ** + *******************************************************************************/ + bool isNdefDetectionTimedOut (); + +private: + nfc_jni_native_data* mNativeData; + ActivationState mActivationState; + tNFC_PROTOCOL mProtocol; + int mtT1tMaxMessageSize; //T1T max NDEF message size + tNFA_STATUS mReadCompletedStatus; + int mLastKovioUidLen; // len of uid of last Kovio tag activated + bool mNdefDetectionTimedOut; // whether NDEF detection algorithm timed out + tNFC_RF_TECH_PARAMS mTechParams [MAX_NUM_TECHNOLOGY]; //array of technology parameters + SyncEvent mReadCompleteEvent; + struct timespec mLastKovioTime; // time of last Kovio tag activation + UINT8 mLastKovioUid[NFC_KOVIO_MAX_LEN]; // uid of last Kovio tag activated + + + /******************************************************************************* + ** + ** Function: IsSameKovio + ** + ** Description: Checks if tag activate is the same (UID) Kovio tag previously + ** activated. This is needed due to a problem with some Kovio + ** tags re-activating multiple times. + ** activationData: data from activation. + ** + ** Returns: true if the activation is from the same tag previously + ** activated, false otherwise + ** + *******************************************************************************/ + bool IsSameKovio(tNFA_ACTIVATED& activationData); + + /******************************************************************************* + ** + ** Function: discoverTechnologies + ** + ** Description: Discover the technologies that NFC service needs by interpreting + ** the data strucutures from the stack. + ** activationData: data from activation. + ** + ** Returns: None + ** + *******************************************************************************/ + void discoverTechnologies (tNFA_ACTIVATED& activationData); + + + /******************************************************************************* + ** + ** Function: discoverTechnologies + ** + ** Description: Discover the technologies that NFC service needs by interpreting + ** the data strucutures from the stack. + ** discoveryData: data from discovery events(s). + ** + ** Returns: None + ** + *******************************************************************************/ + void discoverTechnologies (tNFA_DISC_RESULT& discoveryData); + + + /******************************************************************************* + ** + ** Function: createNativeNfcTag + ** + ** Description: Create a brand new Java NativeNfcTag object; + ** fill the objects's member variables with data; + ** notify NFC service; + ** activationData: data from activation. + ** + ** Returns: None + ** + *******************************************************************************/ + void createNativeNfcTag (tNFA_ACTIVATED& activationData); + + + /******************************************************************************* + ** + ** Function: fillNativeNfcTagMembers1 + ** + ** Description: Fill NativeNfcTag's members: mProtocols, mTechList, mTechHandles, mTechLibNfcTypes. + ** e: JVM environment. + ** tag_cls: Java NativeNfcTag class. + ** tag: Java NativeNfcTag object. + ** + ** Returns: None + ** + *******************************************************************************/ + void fillNativeNfcTagMembers1 (JNIEnv* e, jclass tag_cls, jobject tag); + + + /******************************************************************************* + ** + ** Function: fillNativeNfcTagMembers2 + ** + ** Description: Fill NativeNfcTag's members: mConnectedTechIndex or mConnectedTechnology. + ** The original Google's implementation is in set_target_pollBytes( + ** in com_android_nfc_NativeNfcTag.cpp; + ** e: JVM environment. + ** tag_cls: Java NativeNfcTag class. + ** tag: Java NativeNfcTag object. + ** activationData: data from activation. + ** + ** Returns: None + ** + *******************************************************************************/ + void fillNativeNfcTagMembers2 (JNIEnv* e, jclass tag_cls, jobject tag, tNFA_ACTIVATED& activationData); + + + /******************************************************************************* + ** + ** Function: fillNativeNfcTagMembers3 + ** + ** Description: Fill NativeNfcTag's members: mTechPollBytes. + ** The original Google's implementation is in set_target_pollBytes( + ** in com_android_nfc_NativeNfcTag.cpp; + ** e: JVM environment. + ** tag_cls: Java NativeNfcTag class. + ** tag: Java NativeNfcTag object. + ** activationData: data from activation. + ** + ** Returns: None + ** + *******************************************************************************/ + void fillNativeNfcTagMembers3 (JNIEnv* e, jclass tag_cls, jobject tag, tNFA_ACTIVATED& activationData); + + + /******************************************************************************* + ** + ** Function: fillNativeNfcTagMembers4 + ** + ** Description: Fill NativeNfcTag's members: mTechActBytes. + ** The original Google's implementation is in set_target_activationBytes() + ** in com_android_nfc_NativeNfcTag.cpp; + ** e: JVM environment. + ** tag_cls: Java NativeNfcTag class. + ** tag: Java NativeNfcTag object. + ** activationData: data from activation. + ** + ** Returns: None + ** + *******************************************************************************/ + void fillNativeNfcTagMembers4 (JNIEnv* e, jclass tag_cls, jobject tag, tNFA_ACTIVATED& activationData); + + + /******************************************************************************* + ** + ** Function: fillNativeNfcTagMembers5 + ** + ** Description: Fill NativeNfcTag's members: mUid. + ** The original Google's implementation is in nfc_jni_Discovery_notification_callback() + ** in com_android_nfc_NativeNfcManager.cpp; + ** e: JVM environment. + ** tag_cls: Java NativeNfcTag class. + ** tag: Java NativeNfcTag object. + ** activationData: data from activation. + ** + ** Returns: None + ** + *******************************************************************************/ + void fillNativeNfcTagMembers5 (JNIEnv* e, jclass tag_cls, jobject tag, tNFA_ACTIVATED& activationData); + + + /******************************************************************************* + ** + ** Function: resetTechnologies + ** + ** Description: Clear all data related to the technology, protocol of the tag. + ** + ** Returns: None + ** + *******************************************************************************/ + void resetTechnologies (); + + + /******************************************************************************* + ** + ** Function: calculateT1tMaxMessageSize + ** + ** Description: Calculate type-1 tag's max message size based on header ROM bytes. + ** activate: reference to activation data. + ** + ** Returns: None + ** + *******************************************************************************/ + void calculateT1tMaxMessageSize (tNFA_ACTIVATED& activate); +}; + diff --git a/nci/jni/PeerToPeer.cpp b/nci/jni/PeerToPeer.cpp new file mode 100644 index 0000000..7994e2a --- /dev/null +++ b/nci/jni/PeerToPeer.cpp @@ -0,0 +1,1840 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Communicate with a peer using NFC-DEP, LLCP, SNEP. + */ +#include "OverrideLog.h" +#include "PeerToPeer.h" +#include "NfcJniUtil.h" +#include "llcp_defs.h" +#include "config.h" +#include "JavaClassConstants.h" + +using namespace android; + +namespace android +{ + extern void nativeNfcTag_registerNdefTypeHandler (); + extern void nativeNfcTag_deregisterNdefTypeHandler (); +} + + +PeerToPeer PeerToPeer::sP2p; +const std::string P2pServer::sSnepServiceName ("urn:nfc:sn:snep"); + + +/******************************************************************************* +** +** Function: PeerToPeer +** +** Description: Initialize member variables. +** +** Returns: None +** +*******************************************************************************/ +PeerToPeer::PeerToPeer () +: mRemoteWKS (0), + mIsP2pListening (false), + mP2pListenTechMask (NFA_TECHNOLOGY_MASK_A + | NFA_TECHNOLOGY_MASK_F + | NFA_TECHNOLOGY_MASK_A_ACTIVE + | NFA_TECHNOLOGY_MASK_F_ACTIVE), + mNextJniHandle (1) +{ + unsigned long num = 0; + memset (mServers, 0, sizeof(mServers)); + memset (mClients, 0, sizeof(mClients)); +} + + +/******************************************************************************* +** +** Function: ~PeerToPeer +** +** Description: Free all resources. +** +** Returns: None +** +*******************************************************************************/ +PeerToPeer::~PeerToPeer () +{ +} + + +/******************************************************************************* +** +** Function: getInstance +** +** Description: Get the singleton PeerToPeer object. +** +** Returns: Singleton PeerToPeer object. +** +*******************************************************************************/ +PeerToPeer& PeerToPeer::getInstance () +{ + return sP2p; +} + + +/******************************************************************************* +** +** Function: initialize +** +** Description: Initialize member variables. +** +** Returns: None +** +*******************************************************************************/ +void PeerToPeer::initialize () +{ + ALOGD ("PeerToPeer::initialize"); + unsigned long num = 0; + + if (GetNumValue ("P2P_LISTEN_TECH_MASK", &num, sizeof (num))) + mP2pListenTechMask = num; +} + + +/******************************************************************************* +** +** Function: findServerLocked +** +** Description: Find a PeerToPeer object by connection handle. +** Assumes mMutex is already held +** nfaP2pServerHandle: Connectin handle. +** +** Returns: PeerToPeer object. +** +*******************************************************************************/ +sp<P2pServer> PeerToPeer::findServerLocked (tNFA_HANDLE nfaP2pServerHandle) +{ + for (int i = 0; i < sMax; i++) + { + if ( (mServers[i] != NULL) + && (mServers[i]->mNfaP2pServerHandle == nfaP2pServerHandle) ) + { + return (mServers [i]); + } + } + + // If here, not found + return NULL; +} + + +/******************************************************************************* +** +** Function: findServerLocked +** +** Description: Find a PeerToPeer object by connection handle. +** Assumes mMutex is already held +** serviceName: service name. +** +** Returns: PeerToPeer object. +** +*******************************************************************************/ +sp<P2pServer> PeerToPeer::findServerLocked (tJNI_HANDLE jniHandle) +{ + for (int i = 0; i < sMax; i++) + { + if ( (mServers[i] != NULL) + && (mServers[i]->mJniHandle == jniHandle) ) + { + return (mServers [i]); + } + } + + // If here, not found + return NULL; +} + + +/******************************************************************************* +** +** Function: findServerLocked +** +** Description: Find a PeerToPeer object by service name +** Assumes mMutex is already heldf +** serviceName: service name. +** +** Returns: PeerToPeer object. +** +*******************************************************************************/ +sp<P2pServer> PeerToPeer::findServerLocked (const char *serviceName) +{ + for (int i = 0; i < sMax; i++) + { + if ( (mServers[i] != NULL) && (mServers[i]->mServiceName.compare(serviceName) == 0) ) + return (mServers [i]); + } + + // If here, not found + return NULL; +} + + +/******************************************************************************* +** +** Function: registerServer +** +** Description: Let a server start listening for peer's connection request. +** jniHandle: Connection handle. +** serviceName: Server's service name. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool PeerToPeer::registerServer (tJNI_HANDLE jniHandle, const char *serviceName) +{ + static const char fn [] = "PeerToPeer::registerServer"; + ALOGD ("%s: enter; service name: %s JNI handle: %u", fn, serviceName, jniHandle); + tNFA_STATUS stat = NFA_STATUS_OK; + sp<P2pServer> pSrv = NULL; + UINT8 serverSap = NFA_P2P_ANY_SAP; + + mMutex.lock(); + // Check if already registered + if ((pSrv = findServerLocked(serviceName)) != NULL) + { + ALOGD ("%s: service name=%s already registered, handle: 0x%04x", fn, serviceName, pSrv->mNfaP2pServerHandle); + + // Update JNI handle + pSrv->mJniHandle = jniHandle; + mMutex.unlock(); + return (true); + } + + for (int ii = 0; ii < sMax; ii++) + { + if (mServers[ii] == NULL) + { + pSrv = mServers[ii] = new P2pServer(jniHandle, serviceName); + + ALOGD ("%s: added new p2p server index: %d handle: %u name: %s", fn, ii, jniHandle, serviceName); + break; + } + } + mMutex.unlock(); + + if (pSrv == NULL) + { + ALOGE ("%s: service name=%s no free entry", fn, serviceName); + return (false); + } + + if (pSrv->registerWithStack()) { + ALOGD ("%s: got new p2p server h=0x%X", fn, pSrv->mNfaP2pServerHandle); + return (true); + } else { + ALOGE ("%s: invalid server handle", fn); + removeServer (jniHandle); + return (false); + } +} + + +/******************************************************************************* +** +** Function: removeServer +** +** Description: Free resources related to a server. +** jniHandle: Connection handle. +** +** Returns: None +** +*******************************************************************************/ +void PeerToPeer::removeServer (tJNI_HANDLE jniHandle) +{ + static const char fn [] = "PeerToPeer::removeServer"; + + AutoMutex mutex(mMutex); + + for (int i = 0; i < sMax; i++) + { + if ( (mServers[i] != NULL) && (mServers[i]->mJniHandle == jniHandle) ) + { + ALOGD ("%s: server jni_handle: %u; nfa_handle: 0x%04x; name: %s; index=%d", + fn, jniHandle, mServers[i]->mNfaP2pServerHandle, mServers[i]->mServiceName.c_str(), i); + + mServers [i] = NULL; + return; + } + } + ALOGE ("%s: unknown server jni handle: %u", fn, jniHandle); +} + + +/******************************************************************************* +** +** Function: llcpActivatedHandler +** +** Description: Receive LLLCP-activated event from stack. +** nat: JVM-related data. +** activated: Event data. +** +** Returns: None +** +*******************************************************************************/ +void PeerToPeer::llcpActivatedHandler (nfc_jni_native_data* nat, tNFA_LLCP_ACTIVATED& activated) +{ + static const char fn [] = "PeerToPeer::llcpActivatedHandler"; + ALOGD ("%s: enter", fn); + JNIEnv* e = NULL; + jclass tag_cls = NULL; + jobject tag = NULL; + jmethodID ctor = 0; + jfieldID f = 0; + + //no longer need to receive NDEF message from a tag + android::nativeNfcTag_deregisterNdefTypeHandler (); + + mRemoteWKS = activated.remote_wks; + + nat->vm->AttachCurrentThread (&e, NULL); + if (e == NULL) + { + ALOGE ("%s: jni env is null", fn); + return; + } + + ALOGD ("%s: get object class", fn); + tag_cls = e->GetObjectClass (nat->cached_P2pDevice); + if (e->ExceptionCheck()) + { + e->ExceptionClear(); + ALOGE ("%s: fail get p2p device", fn); + goto TheEnd; + } + + ALOGD ("%s: instantiate", fn); + /* 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 (activated.is_initiator == TRUE) + { + ALOGD ("%s: p2p initiator", fn); + e->SetIntField (tag, f, (jint) MODE_P2P_INITIATOR); + } + else + { + ALOGD ("%s: p2p target", fn); + e->SetIntField (tag, f, (jint) MODE_P2P_TARGET); + } + + /* Set tag handle */ + f = e->GetFieldID (tag_cls, "mHandle", "I"); + e->SetIntField (tag, f, (jint) 0x1234); // ?? This handle is not used for anything + + if (nat->tag != NULL) + { + e->DeleteGlobalRef (nat->tag); + } + nat->tag = e->NewGlobalRef (tag); + + ALOGD ("%s: notify nfc service", fn); + + /* Notify manager that new a P2P device was found */ + e->CallVoidMethod (nat->manager, android::gCachedNfcManagerNotifyLlcpLinkActivation, tag); + if (e->ExceptionCheck()) + { + e->ExceptionClear(); + ALOGE ("%s: fail notify", fn); + } + + e->DeleteLocalRef (tag); + +TheEnd: + nat->vm->DetachCurrentThread (); + ALOGD ("%s: exit", fn); +} + + +/******************************************************************************* +** +** Function: llcpDeactivatedHandler +** +** Description: Receive LLLCP-deactivated event from stack. +** nat: JVM-related data. +** deactivated: Event data. +** +** Returns: None +** +*******************************************************************************/ +void PeerToPeer::llcpDeactivatedHandler (nfc_jni_native_data* nat, tNFA_LLCP_DEACTIVATED& deactivated) +{ + static const char fn [] = "PeerToPeer::llcpDeactivatedHandler"; + ALOGD ("%s: enter", fn); + JNIEnv* e = NULL; + + nat->vm->AttachCurrentThread (&e, NULL); + if (e == NULL) + { + ALOGE ("%s: jni env is null", fn); + return; + } + + ALOGD ("%s: notify nfc service", fn); + /* Notify manager that the LLCP is lost or deactivated */ + e->CallVoidMethod (nat->manager, android::gCachedNfcManagerNotifyLlcpLinkDeactivated, nat->tag); + if (e->ExceptionCheck()) + { + e->ExceptionClear(); + ALOGE ("%s: fail notify", fn); + } + + nat->vm->DetachCurrentThread (); + + //let the tag-reading code handle NDEF data event + android::nativeNfcTag_registerNdefTypeHandler (); + ALOGD ("%s: exit", fn); +} + + +/******************************************************************************* +** +** Function: accept +** +** Description: Accept a peer's request to connect. +** serverJniHandle: Server's handle. +** connJniHandle: Connection handle. +** maxInfoUnit: Maximum information unit. +** recvWindow: Receive window size. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool PeerToPeer::accept (tJNI_HANDLE serverJniHandle, tJNI_HANDLE connJniHandle, int maxInfoUnit, int recvWindow) +{ + static const char fn [] = "PeerToPeer::accept"; + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + sp<NfaConn> *pConn = NULL; + bool stat = false; + int ii = 0; + sp<P2pServer> pSrv = NULL; + + ALOGD ("%s: enter; server jni handle: %u; conn jni handle: %u; maxInfoUnit: %d; recvWindow: %d", fn, + serverJniHandle, connJniHandle, maxInfoUnit, recvWindow); + + mMutex.lock(); + if ((pSrv = findServerLocked (serverJniHandle)) == NULL) + { + ALOGE ("%s: unknown server jni handle: %u", fn, serverJniHandle); + mMutex.unlock(); + return (false); + } + mMutex.unlock(); + + return pSrv->accept(serverJniHandle, connJniHandle, maxInfoUnit, recvWindow); +} + + +/******************************************************************************* +** +** Function: deregisterServer +** +** Description: Stop a P2pServer from listening for peer. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool PeerToPeer::deregisterServer (tJNI_HANDLE jniHandle) +{ + static const char fn [] = "PeerToPeer::deregisterServer"; + ALOGD ("%s: enter; JNI handle: %u", fn, jniHandle); + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + sp<P2pServer> pSrv = NULL; + + mMutex.lock(); + if ((pSrv = findServerLocked (jniHandle)) == NULL) + { + ALOGE ("%s: unknown service handle: %u", fn, jniHandle); + mMutex.unlock(); + return (false); + } + mMutex.unlock(); + + { + // Server does not call NFA_P2pDisconnect(), so unblock the accept() + SyncEventGuard guard (pSrv->mConnRequestEvent); + pSrv->mConnRequestEvent.notifyOne(); + } + + nfaStat = NFA_P2pDeregister (pSrv->mNfaP2pServerHandle); + if (nfaStat != NFA_STATUS_OK) + { + ALOGE ("%s: deregister error=0x%X", fn, nfaStat); + } + + removeServer (jniHandle); + + ALOGD ("%s: exit", fn); + return true; +} + + +/******************************************************************************* +** +** Function: createClient +** +** Description: Create a P2pClient object for a new out-bound connection. +** jniHandle: Connection handle. +** miu: Maximum information unit. +** rw: Receive window size. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool PeerToPeer::createClient (tJNI_HANDLE jniHandle, UINT16 miu, UINT8 rw) +{ + static const char fn [] = "PeerToPeer::createClient"; + int i = 0; + ALOGD ("%s: enter: jni h: %u miu: %u rw: %u", fn, jniHandle, miu, rw); + + mMutex.lock(); + sp<P2pClient> client = NULL; + for (i = 0; i < sMax; i++) + { + if (mClients[i] == NULL) + { + mClients [i] = client = new P2pClient(); + + mClients [i]->mClientConn->mJniHandle = jniHandle; + mClients [i]->mClientConn->mMaxInfoUnit = miu; + mClients [i]->mClientConn->mRecvWindow = rw; + break; + } + } + mMutex.unlock(); + + if (client == NULL) + { + ALOGE ("%s: fail", fn); + return (false); + } + + ALOGD ("%s: pClient: 0x%p assigned for client jniHandle: %u", fn, client.get(), jniHandle); + + { + SyncEventGuard guard (mClients[i]->mRegisteringEvent); + NFA_P2pRegisterClient (NFA_P2P_DLINK_TYPE, nfaClientCallback); + mClients[i]->mRegisteringEvent.wait(); //wait for NFA_P2P_REG_CLIENT_EVT + } + + if (mClients[i]->mNfaP2pClientHandle != NFA_HANDLE_INVALID) + { + ALOGD ("%s: exit; new client jniHandle: %u NFA Handle: 0x%04x", fn, jniHandle, client->mClientConn->mNfaConnHandle); + return (true); + } + else + { + ALOGE ("%s: FAILED; new client jniHandle: %u NFA Handle: 0x%04x", fn, jniHandle, client->mClientConn->mNfaConnHandle); + removeConn (jniHandle); + return (false); + } +} + + +/******************************************************************************* +** +** Function: removeConn +** +** Description: Free resources related to a connection. +** jniHandle: Connection handle. +** +** Returns: None +** +*******************************************************************************/ +void PeerToPeer::removeConn(tJNI_HANDLE jniHandle) +{ + static const char fn[] = "PeerToPeer::removeConn"; + int ii = 0, jj = 0; + + AutoMutex mutex(mMutex); + // If the connection is a for a client, delete the client itself + for (ii = 0; ii < sMax; ii++) + { + if ((mClients[ii] != NULL) && (mClients[ii]->mClientConn->mJniHandle == jniHandle)) + { + if (mClients[ii]->mNfaP2pClientHandle != NFA_HANDLE_INVALID) + NFA_P2pDeregister (mClients[ii]->mNfaP2pClientHandle); + + mClients[ii] = NULL; + ALOGD ("%s: deleted client handle: %u index: %u", fn, jniHandle, ii); + return; + } + } + + // If the connection is for a server, just delete the connection + for (ii = 0; ii < sMax; ii++) + { + if (mServers[ii] != NULL) + { + if (mServers[ii]->removeServerConnection(jniHandle)) { + return; + } + } + } + + ALOGE ("%s: could not find handle: %u", fn, jniHandle); +} + + +/******************************************************************************* +** +** Function: connectConnOriented +** +** Description: Estabish a connection-oriented connection to a peer. +** jniHandle: Connection handle. +** serviceName: Peer's service name. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool PeerToPeer::connectConnOriented (tJNI_HANDLE jniHandle, const char* serviceName) +{ + static const char fn [] = "PeerToPeer::connectConnOriented"; + ALOGD ("%s: enter; h: %u service name=%s", fn, jniHandle, serviceName); + bool stat = createDataLinkConn (jniHandle, serviceName, 0); + ALOGD ("%s: exit; h: %u stat: %u", fn, jniHandle, stat); + return stat; +} + + +/******************************************************************************* +** +** Function: connectConnOriented +** +** Description: Estabish a connection-oriented connection to a peer. +** jniHandle: Connection handle. +** destinationSap: Peer's service access point. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool PeerToPeer::connectConnOriented (tJNI_HANDLE jniHandle, UINT8 destinationSap) +{ + static const char fn [] = "PeerToPeer::connectConnOriented"; + ALOGD ("%s: enter; h: %u dest sap: 0x%X", fn, jniHandle, destinationSap); + bool stat = createDataLinkConn (jniHandle, NULL, destinationSap); + ALOGD ("%s: exit; h: %u stat: %u", fn, jniHandle, stat); + return stat; +} + + +/******************************************************************************* +** +** Function: createDataLinkConn +** +** Description: Estabish a connection-oriented connection to a peer. +** jniHandle: Connection handle. +** serviceName: Peer's service name. +** destinationSap: Peer's service access point. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool PeerToPeer::createDataLinkConn (tJNI_HANDLE jniHandle, const char* serviceName, UINT8 destinationSap) +{ + static const char fn [] = "PeerToPeer::createDataLinkConn"; + ALOGD ("%s: enter", fn); + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + sp<P2pClient> pClient = NULL; + + if ((pClient = findClient (jniHandle)) == NULL) + { + ALOGE ("%s: can't find client, JNI handle: %u", fn, jniHandle); + return (false); + } + + { + SyncEventGuard guard (pClient->mConnectingEvent); + pClient->mIsConnecting = true; + + if (serviceName) + nfaStat = NFA_P2pConnectByName (pClient->mNfaP2pClientHandle, + const_cast<char*>(serviceName), pClient->mClientConn->mMaxInfoUnit, + pClient->mClientConn->mRecvWindow); + else if (destinationSap) + nfaStat = NFA_P2pConnectBySap (pClient->mNfaP2pClientHandle, destinationSap, + pClient->mClientConn->mMaxInfoUnit, pClient->mClientConn->mRecvWindow); + if (nfaStat == NFA_STATUS_OK) + { + ALOGD ("%s: wait for connected event mConnectingEvent: 0x%p", fn, pClient.get()); + pClient->mConnectingEvent.wait(); + } + } + + if (nfaStat == NFA_STATUS_OK) + { + if (pClient->mClientConn->mNfaConnHandle == NFA_HANDLE_INVALID) + { + removeConn (jniHandle); + nfaStat = NFA_STATUS_FAILED; + } + else + pClient->mIsConnecting = false; + } + else + { + removeConn (jniHandle); + ALOGE ("%s: fail; error=0x%X", fn, nfaStat); + } + + ALOGD ("%s: exit", fn); + return nfaStat == NFA_STATUS_OK; +} + + +/******************************************************************************* +** +** Function: findClient +** +** Description: Find a PeerToPeer object with a client connection handle. +** nfaConnHandle: Connection handle. +** +** Returns: PeerToPeer object. +** +*******************************************************************************/ +sp<P2pClient> PeerToPeer::findClient (tNFA_HANDLE nfaConnHandle) +{ + AutoMutex mutex(mMutex); + for (int i = 0; i < sMax; i++) + { + if ((mClients[i] != NULL) && (mClients[i]->mNfaP2pClientHandle == nfaConnHandle)) + return (mClients[i]); + } + return (NULL); +} + + +/******************************************************************************* +** +** Function: findClient +** +** Description: Find a PeerToPeer object with a client connection handle. +** jniHandle: Connection handle. +** +** Returns: PeerToPeer object. +** +*******************************************************************************/ +sp<P2pClient> PeerToPeer::findClient (tJNI_HANDLE jniHandle) +{ + AutoMutex mutex(mMutex); + for (int i = 0; i < sMax; i++) + { + if ((mClients[i] != NULL) && (mClients[i]->mClientConn->mJniHandle == jniHandle)) + return (mClients[i]); + } + return (NULL); +} + + +/******************************************************************************* +** +** Function: findClientCon +** +** Description: Find a PeerToPeer object with a client connection handle. +** nfaConnHandle: Connection handle. +** +** Returns: PeerToPeer object. +** +*******************************************************************************/ +sp<P2pClient> PeerToPeer::findClientCon (tNFA_HANDLE nfaConnHandle) +{ + AutoMutex mutex(mMutex); + for (int i = 0; i < sMax; i++) + { + if ((mClients[i] != NULL) && (mClients[i]->mClientConn->mNfaConnHandle == nfaConnHandle)) + return (mClients[i]); + } + return (NULL); +} + + +/******************************************************************************* +** +** Function: findConnection +** +** Description: Find a PeerToPeer object with a connection handle. +** nfaConnHandle: Connection handle. +** +** Returns: PeerToPeer object. +** +*******************************************************************************/ +sp<NfaConn> PeerToPeer::findConnection (tNFA_HANDLE nfaConnHandle) +{ + int ii = 0, jj = 0; + + AutoMutex mutex(mMutex); + // First, look through all the client control blocks + for (ii = 0; ii < sMax; ii++) + { + if ( (mClients[ii] != NULL) + && (mClients[ii]->mClientConn->mNfaConnHandle == nfaConnHandle) ) { + return mClients[ii]->mClientConn; + } + } + + // Not found yet. Look through all the server control blocks + for (ii = 0; ii < sMax; ii++) + { + if (mServers[ii] != NULL) + { + sp<NfaConn> conn = mServers[ii]->findServerConnection(nfaConnHandle); + if (conn != NULL) { + return conn; + } + } + } + + // Not found... + return NULL; +} + + +/******************************************************************************* +** +** Function: findConnection +** +** Description: Find a PeerToPeer object with a connection handle. +** jniHandle: Connection handle. +** +** Returns: PeerToPeer object. +** +*******************************************************************************/ +sp<NfaConn> PeerToPeer::findConnection (tJNI_HANDLE jniHandle) +{ + int ii = 0, jj = 0; + + AutoMutex mutex(mMutex); + // First, look through all the client control blocks + for (ii = 0; ii < sMax; ii++) + { + if ( (mClients[ii] != NULL) + && (mClients[ii]->mClientConn->mJniHandle == jniHandle) ) { + return mClients[ii]->mClientConn; + } + } + + // Not found yet. Look through all the server control blocks + for (ii = 0; ii < sMax; ii++) + { + if (mServers[ii] != NULL) + { + sp<NfaConn> conn = mServers[ii]->findServerConnection(jniHandle); + if (conn != NULL) { + return conn; + } + } + } + + // Not found... + return NULL; +} + + +/******************************************************************************* +** +** Function: send +** +** Description: Send data to peer. +** jniHandle: Handle of connection. +** buffer: Buffer of data. +** bufferLen: Length of data. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool PeerToPeer::send (tJNI_HANDLE jniHandle, UINT8 *buffer, UINT16 bufferLen) +{ + static const char fn [] = "PeerToPeer::send"; + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + sp<NfaConn> pConn = NULL; + + if ((pConn = findConnection (jniHandle)) == NULL) + { + ALOGE ("%s: can't find connection handle: %u", fn, jniHandle); + return (false); + } + + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: send data; jniHandle: %u nfaHandle: 0x%04X", + fn, pConn->mJniHandle, pConn->mNfaConnHandle); + + while (true) + { + SyncEventGuard guard (pConn->mCongEvent); + nfaStat = NFA_P2pSendData (pConn->mNfaConnHandle, bufferLen, buffer); + if (nfaStat == NFA_STATUS_CONGESTED) + pConn->mCongEvent.wait (); //wait for NFA_P2P_CONGEST_EVT + else + break; + + if (pConn->mNfaConnHandle == NFA_HANDLE_INVALID) //peer already disconnected + { + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: peer disconnected", fn); + return (false); + } + } + + if (nfaStat == NFA_STATUS_OK) + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: exit OK; JNI handle: %u NFA Handle: 0x%04x", fn, jniHandle, pConn->mNfaConnHandle); + else + ALOGE ("%s: Data not sent; JNI handle: %u NFA Handle: 0x%04x error: 0x%04x", + fn, jniHandle, pConn->mNfaConnHandle, nfaStat); + + return nfaStat == NFA_STATUS_OK; +} + + +/******************************************************************************* +** +** Function: receive +** +** Description: Receive data from peer. +** jniHandle: Handle of connection. +** buffer: Buffer to store data. +** bufferLen: Max length of buffer. +** actualLen: Actual length received. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool PeerToPeer::receive (tJNI_HANDLE jniHandle, UINT8* buffer, UINT16 bufferLen, UINT16& actualLen) +{ + static const char fn [] = "PeerToPeer::receive"; + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: enter; jniHandle: %u bufferLen: %u", fn, jniHandle, bufferLen); + sp<NfaConn> pConn = NULL; + tNFA_STATUS stat = NFA_STATUS_FAILED; + UINT32 actualDataLen2 = 0; + BOOLEAN isMoreData = TRUE; + bool retVal = false; + + if ((pConn = findConnection (jniHandle)) == NULL) + { + ALOGE ("%s: can't find connection handle: %u", fn, jniHandle); + return (false); + } + + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: jniHandle: %u nfaHandle: 0x%04X buf len=%u", fn, pConn->mJniHandle, pConn->mNfaConnHandle, bufferLen); + + while (pConn->mNfaConnHandle != NFA_HANDLE_INVALID) + { + //NFA_P2pReadData() is synchronous + stat = NFA_P2pReadData (pConn->mNfaConnHandle, bufferLen, &actualDataLen2, buffer, &isMoreData); + if ((stat == NFA_STATUS_OK) && (actualDataLen2 > 0)) //received some data + { + actualLen = (UINT16) actualDataLen2; + retVal = true; + break; + } + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: waiting for data...", fn); + { + SyncEventGuard guard (pConn->mReadEvent); + pConn->mReadEvent.wait(); + } + } //while + + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: exit; nfa h: 0x%X ok: %u actual len: %u", fn, pConn->mNfaConnHandle, retVal, actualLen); + return retVal; +} + + +/******************************************************************************* +** +** Function: disconnectConnOriented +** +** Description: Disconnect a connection-oriented connection with peer. +** jniHandle: Handle of connection. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool PeerToPeer::disconnectConnOriented (tJNI_HANDLE jniHandle) +{ + static const char fn [] = "PeerToPeer::disconnectConnOriented"; + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + sp<P2pClient> pClient = NULL; + sp<NfaConn> pConn = NULL; + + ALOGD ("%s: enter; jni handle: %u", fn, jniHandle); + + if ((pConn = findConnection(jniHandle)) == NULL) + { + ALOGE ("%s: can't find connection handle: %u", fn, jniHandle); + return (false); + } + + // If this is a client, he may not be connected yet, so unblock him just in case + if ( ((pClient = findClient(jniHandle)) != NULL) && (pClient->mIsConnecting) ) + { + SyncEventGuard guard (pClient->mConnectingEvent); + pClient->mConnectingEvent.notifyOne(); + return (true); + } + + { + SyncEventGuard guard1 (pConn->mCongEvent); + pConn->mCongEvent.notifyOne (); //unblock send() if congested + } + { + SyncEventGuard guard2 (pConn->mReadEvent); + pConn->mReadEvent.notifyOne (); //unblock receive() + } + + if (pConn->mNfaConnHandle != NFA_HANDLE_INVALID) + { + ALOGD ("%s: try disconn nfa h=0x%04X", fn, pConn->mNfaConnHandle); + SyncEventGuard guard (pConn->mDisconnectingEvent); + nfaStat = NFA_P2pDisconnect (pConn->mNfaConnHandle, FALSE); + + if (nfaStat != NFA_STATUS_OK) + ALOGE ("%s: fail p2p disconnect", fn); + else + pConn->mDisconnectingEvent.wait(); + } + + mDisconnectMutex.lock (); + removeConn (jniHandle); + mDisconnectMutex.unlock (); + + ALOGD ("%s: exit; jni handle: %u", fn, jniHandle); + return nfaStat == NFA_STATUS_OK; +} + + +/******************************************************************************* +** +** Function: getRemoteMaxInfoUnit +** +** Description: Get peer's max information unit. +** jniHandle: Handle of the connection. +** +** Returns: Peer's max information unit. +** +*******************************************************************************/ +UINT16 PeerToPeer::getRemoteMaxInfoUnit (tJNI_HANDLE jniHandle) +{ + static const char fn [] = "PeerToPeer::getRemoteMaxInfoUnit"; + sp<NfaConn> pConn = NULL; + + if ((pConn = findConnection(jniHandle)) == NULL) + { + ALOGE ("%s: can't find client jniHandle: %u", fn, jniHandle); + return 0; + } + ALOGD ("%s: jniHandle: %u MIU: %u", fn, jniHandle, pConn->mRemoteMaxInfoUnit); + return (pConn->mRemoteMaxInfoUnit); +} + + +/******************************************************************************* +** +** Function: getRemoteRecvWindow +** +** Description: Get peer's receive window size. +** jniHandle: Handle of the connection. +** +** Returns: Peer's receive window size. +** +*******************************************************************************/ +UINT8 PeerToPeer::getRemoteRecvWindow (tJNI_HANDLE jniHandle) +{ + static const char fn [] = "PeerToPeer::getRemoteRecvWindow"; + ALOGD ("%s: client jni handle: %u", fn, jniHandle); + sp<NfaConn> pConn = NULL; + + if ((pConn = findConnection(jniHandle)) == NULL) + { + ALOGE ("%s: can't find client", fn); + return 0; + } + return pConn->mRemoteRecvWindow; +} + +/******************************************************************************* +** +** Function: setP2pListenMask +** +** Description: Sets the p2p listen technology mask. +** p2pListenMask: the p2p listen mask to be set? +** +** Returns: None +** +*******************************************************************************/ +void PeerToPeer::setP2pListenMask (tNFA_TECHNOLOGY_MASK p2pListenMask) { + mP2pListenTechMask = p2pListenMask; +} + +/******************************************************************************* +** +** Function: enableP2pListening +** +** Description: Start/stop polling/listening to peer that supports P2P. +** isEnable: Is enable polling/listening? +** +** Returns: None +** +*******************************************************************************/ +void PeerToPeer::enableP2pListening (bool isEnable) +{ + static const char fn [] = "PeerToPeer::enableP2pListening"; + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + + ALOGD ("%s: enter isEnable: %u mIsP2pListening: %u", fn, isEnable, mIsP2pListening); + + // If request to enable P2P listening, and we were not already listening + if ( (isEnable == true) && (mIsP2pListening == false) && (mP2pListenTechMask != 0) ) + { + SyncEventGuard guard (mSetTechEvent); + if ((nfaStat = NFA_SetP2pListenTech (mP2pListenTechMask)) == NFA_STATUS_OK) + { + mSetTechEvent.wait (); + mIsP2pListening = true; + } + else + ALOGE ("%s: fail enable listen; error=0x%X", fn, nfaStat); + } + else if ( (isEnable == false) && (mIsP2pListening == true) ) + { + SyncEventGuard guard (mSetTechEvent); + // Request to disable P2P listening, check if it was enabled + if ((nfaStat = NFA_SetP2pListenTech(0)) == NFA_STATUS_OK) + { + mSetTechEvent.wait (); + mIsP2pListening = false; + } + else + ALOGE ("%s: fail disable listen; error=0x%X", fn, nfaStat); + } + ALOGD ("%s: exit; mIsP2pListening: %u", fn, mIsP2pListening); +} + + +/******************************************************************************* +** +** Function: handleNfcOnOff +** +** Description: Handle events related to turning NFC on/off by the user. +** isOn: Is NFC turning on? +** +** Returns: None +** +*******************************************************************************/ +void PeerToPeer::handleNfcOnOff (bool isOn) +{ + static const char fn [] = "PeerToPeer::handleNfcOnOff"; + ALOGD ("%s: enter; is on=%u", fn, isOn); + tNFA_STATUS stat = NFA_STATUS_FAILED; + + mIsP2pListening = false; // In both cases, P2P will not be listening + + AutoMutex mutex(mMutex); + if (isOn) + { + // Start with no clients or servers + memset (mServers, 0, sizeof(mServers)); + memset (mClients, 0, sizeof(mClients)); + } + else + { + int ii = 0, jj = 0; + + // Disconnect through all the clients + for (ii = 0; ii < sMax; ii++) + { + if (mClients[ii] != NULL) + { + if (mClients[ii]->mClientConn->mNfaConnHandle == NFA_HANDLE_INVALID) + { + SyncEventGuard guard (mClients[ii]->mConnectingEvent); + mClients[ii]->mConnectingEvent.notifyOne(); + } + else + { + mClients[ii]->mClientConn->mNfaConnHandle = NFA_HANDLE_INVALID; + { + SyncEventGuard guard1 (mClients[ii]->mClientConn->mCongEvent); + mClients[ii]->mClientConn->mCongEvent.notifyOne (); //unblock send() + } + { + SyncEventGuard guard2 (mClients[ii]->mClientConn->mReadEvent); + mClients[ii]->mClientConn->mReadEvent.notifyOne (); //unblock receive() + } + } + } + } //loop + + // Now look through all the server control blocks + for (ii = 0; ii < sMax; ii++) + { + if (mServers[ii] != NULL) + { + mServers[ii]->unblockAll(); + } + } //loop + + } + ALOGD ("%s: exit", fn); +} + + +/******************************************************************************* +** +** Function: nfaServerCallback +** +** Description: Receive LLCP-related events from the stack. +** p2pEvent: Event code. +** eventData: Event data. +** +** Returns: None +** +*******************************************************************************/ +void PeerToPeer::nfaServerCallback (tNFA_P2P_EVT p2pEvent, tNFA_P2P_EVT_DATA* eventData) +{ + static const char fn [] = "PeerToPeer::nfaServerCallback"; + sp<P2pServer> pSrv = NULL; + sp<NfaConn> pConn = NULL; + + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: enter; event=0x%X", fn, p2pEvent); + + switch (p2pEvent) + { + case NFA_P2P_REG_SERVER_EVT: // NFA_P2pRegisterServer() has started to listen + ALOGD ("%s: NFA_P2P_REG_SERVER_EVT; handle: 0x%04x; service sap=0x%02x name: %s", fn, + eventData->reg_server.server_handle, eventData->reg_server.server_sap, eventData->reg_server.service_name); + + sP2p.mMutex.lock(); + pSrv = sP2p.findServerLocked(eventData->reg_server.service_name); + sP2p.mMutex.unlock(); + if (pSrv == NULL) + { + ALOGE ("%s: NFA_P2P_REG_SERVER_EVT for unknown service: %s", fn, eventData->reg_server.service_name); + } + else + { + SyncEventGuard guard (pSrv->mRegServerEvent); + pSrv->mNfaP2pServerHandle = eventData->reg_server.server_handle; + pSrv->mRegServerEvent.notifyOne(); //unblock registerServer() + } + break; + + case NFA_P2P_ACTIVATED_EVT: //remote device has activated + ALOGD ("%s: NFA_P2P_ACTIVATED_EVT; handle: 0x%04x", fn, eventData->activated.handle); + break; + + case NFA_P2P_DEACTIVATED_EVT: + ALOGD ("%s: NFA_P2P_DEACTIVATED_EVT; handle: 0x%04x", fn, eventData->activated.handle); + break; + + case NFA_P2P_CONN_REQ_EVT: + ALOGD ("%s: NFA_P2P_CONN_REQ_EVT; nfa server h=0x%04x; nfa conn h=0x%04x; remote sap=0x%02x", fn, + eventData->conn_req.server_handle, eventData->conn_req.conn_handle, eventData->conn_req.remote_sap); + + sP2p.mMutex.lock(); + pSrv = sP2p.findServerLocked(eventData->conn_req.server_handle); + sP2p.mMutex.unlock(); + if (pSrv == NULL) + { + ALOGE ("%s: NFA_P2P_CONN_REQ_EVT; unknown server h", fn); + return; + } + ALOGD ("%s: NFA_P2P_CONN_REQ_EVT; server jni h=%u", fn, pSrv->mJniHandle); + + // Look for a connection block that is waiting (handle invalid) + if ((pConn = pSrv->findServerConnection((tNFA_HANDLE) NFA_HANDLE_INVALID)) == NULL) + { + ALOGE ("%s: NFA_P2P_CONN_REQ_EVT; server not listening", fn); + } + else + { + SyncEventGuard guard (pSrv->mConnRequestEvent); + pConn->mNfaConnHandle = eventData->conn_req.conn_handle; + pConn->mRemoteMaxInfoUnit = eventData->conn_req.remote_miu; + pConn->mRemoteRecvWindow = eventData->conn_req.remote_rw; + ALOGD ("%s: NFA_P2P_CONN_REQ_EVT; server jni h=%u; conn jni h=%u; notify conn req", fn, pSrv->mJniHandle, pConn->mJniHandle); + pSrv->mConnRequestEvent.notifyOne(); //unblock accept() + } + break; + + case NFA_P2P_CONNECTED_EVT: + ALOGD ("%s: NFA_P2P_CONNECTED_EVT; h=0x%x remote sap=0x%X", fn, + eventData->connected.client_handle, eventData->connected.remote_sap); + break; + + case NFA_P2P_DISC_EVT: + ALOGD ("%s: NFA_P2P_DISC_EVT; h=0x%04x; reason=0x%X", fn, eventData->disc.handle, eventData->disc.reason); + // Look for the connection block + if ((pConn = sP2p.findConnection(eventData->disc.handle)) == NULL) + { + ALOGE ("%s: NFA_P2P_DISC_EVT: can't find conn for NFA handle: 0x%04x", fn, eventData->disc.handle); + } + else + { + sP2p.mDisconnectMutex.lock (); + pConn->mNfaConnHandle = NFA_HANDLE_INVALID; + { + ALOGD ("%s: NFA_P2P_DISC_EVT; try guard disconn event", fn); + SyncEventGuard guard3 (pConn->mDisconnectingEvent); + pConn->mDisconnectingEvent.notifyOne (); + ALOGD ("%s: NFA_P2P_DISC_EVT; notified disconn event", fn); + } + { + ALOGD ("%s: NFA_P2P_DISC_EVT; try guard congest event", fn); + SyncEventGuard guard1 (pConn->mCongEvent); + pConn->mCongEvent.notifyOne (); //unblock write (if congested) + ALOGD ("%s: NFA_P2P_DISC_EVT; notified congest event", fn); + } + { + ALOGD ("%s: NFA_P2P_DISC_EVT; try guard read event", fn); + SyncEventGuard guard2 (pConn->mReadEvent); + pConn->mReadEvent.notifyOne (); //unblock receive() + ALOGD ("%s: NFA_P2P_DISC_EVT; notified read event", fn); + } + sP2p.mDisconnectMutex.unlock (); + } + break; + + case NFA_P2P_DATA_EVT: + // Look for the connection block + if ((pConn = sP2p.findConnection(eventData->data.handle)) == NULL) + { + ALOGE ("%s: NFA_P2P_DATA_EVT: can't find conn for NFA handle: 0x%04x", fn, eventData->data.handle); + } + else + { + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: NFA_P2P_DATA_EVT; h=0x%X; remote sap=0x%X", fn, + eventData->data.handle, eventData->data.remote_sap); + SyncEventGuard guard (pConn->mReadEvent); + pConn->mReadEvent.notifyOne(); + } + break; + + case NFA_P2P_CONGEST_EVT: + // Look for the connection block + if ((pConn = sP2p.findConnection(eventData->congest.handle)) == NULL) + { + ALOGE ("%s: NFA_P2P_CONGEST_EVT: can't find conn for NFA handle: 0x%04x", fn, eventData->congest.handle); + } + else + { + ALOGD ("%s: NFA_P2P_CONGEST_EVT; nfa handle: 0x%04x congested: %u", fn, + eventData->congest.handle, eventData->congest.is_congested); + if (eventData->congest.is_congested == FALSE) + { + SyncEventGuard guard (pConn->mCongEvent); + pConn->mCongEvent.notifyOne(); + } + } + break; + + default: + ALOGE ("%s: unknown event 0x%X ????", fn, p2pEvent); + break; + } + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: exit", fn); +} + + +/******************************************************************************* +** +** Function: nfaClientCallback +** +** Description: Receive LLCP-related events from the stack. +** p2pEvent: Event code. +** eventData: Event data. +** +** Returns: None +** +*******************************************************************************/ +void PeerToPeer::nfaClientCallback (tNFA_P2P_EVT p2pEvent, tNFA_P2P_EVT_DATA* eventData) +{ + static const char fn [] = "PeerToPeer::nfaClientCallback"; + sp<NfaConn> pConn = NULL; + sp<P2pClient> pClient = NULL; + + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: enter; event=%u", fn, p2pEvent); + + switch (p2pEvent) + { + case NFA_P2P_REG_CLIENT_EVT: + // Look for a client that is trying to register + if ((pClient = sP2p.findClient ((tNFA_HANDLE)NFA_HANDLE_INVALID)) == NULL) + { + ALOGE ("%s: NFA_P2P_REG_CLIENT_EVT: can't find waiting client", fn); + } + else + { + ALOGD ("%s: NFA_P2P_REG_CLIENT_EVT; Conn Handle: 0x%04x, pClient: 0x%p", fn, eventData->reg_client.client_handle, pClient.get()); + + SyncEventGuard guard (pClient->mRegisteringEvent); + pClient->mNfaP2pClientHandle = eventData->reg_client.client_handle; + pClient->mRegisteringEvent.notifyOne(); + } + break; + + case NFA_P2P_ACTIVATED_EVT: + // Look for a client that is trying to register + if ((pClient = sP2p.findClient (eventData->activated.handle)) == NULL) + { + ALOGE ("%s: NFA_P2P_ACTIVATED_EVT: can't find client", fn); + } + else + { + ALOGD ("%s: NFA_P2P_ACTIVATED_EVT; Conn Handle: 0x%04x, pClient: 0x%p", fn, eventData->activated.handle, pClient.get()); + } + break; + + case NFA_P2P_DEACTIVATED_EVT: + ALOGD ("%s: NFA_P2P_DEACTIVATED_EVT: conn handle: 0x%X", fn, eventData->deactivated.handle); + break; + + case NFA_P2P_CONNECTED_EVT: + // Look for the client that is trying to connect + if ((pClient = sP2p.findClient (eventData->connected.client_handle)) == NULL) + { + ALOGE ("%s: NFA_P2P_CONNECTED_EVT: can't find client: 0x%04x", fn, eventData->connected.client_handle); + } + else + { + ALOGD ("%s: NFA_P2P_CONNECTED_EVT; client_handle=0x%04x conn_handle: 0x%04x remote sap=0x%X pClient: 0x%p", fn, + eventData->connected.client_handle, eventData->connected.conn_handle, eventData->connected.remote_sap, pClient.get()); + + SyncEventGuard guard (pClient->mConnectingEvent); + pClient->mClientConn->mNfaConnHandle = eventData->connected.conn_handle; + pClient->mClientConn->mRemoteMaxInfoUnit = eventData->connected.remote_miu; + pClient->mClientConn->mRemoteRecvWindow = eventData->connected.remote_rw; + pClient->mConnectingEvent.notifyOne(); //unblock createDataLinkConn() + } + break; + + case NFA_P2P_DISC_EVT: + ALOGD ("%s: NFA_P2P_DISC_EVT; h=0x%04x; reason=0x%X", fn, eventData->disc.handle, eventData->disc.reason); + // Look for the connection block + if ((pConn = sP2p.findConnection(eventData->disc.handle)) == NULL) + { + // If no connection, may be a client that is trying to connect + if ((pClient = sP2p.findClientCon ((tNFA_HANDLE)NFA_HANDLE_INVALID)) == NULL) + { + ALOGE ("%s: NFA_P2P_DISC_EVT: can't find conn for NFA handle: 0x%04x", fn, eventData->disc.handle); + return; + } + // Unblock createDataLinkConn() + SyncEventGuard guard (pClient->mConnectingEvent); + pClient->mConnectingEvent.notifyOne(); + } + else + { + sP2p.mDisconnectMutex.lock (); + pConn->mNfaConnHandle = NFA_HANDLE_INVALID; + { + ALOGD ("%s: NFA_P2P_DISC_EVT; try guard disconn event", fn); + SyncEventGuard guard3 (pConn->mDisconnectingEvent); + pConn->mDisconnectingEvent.notifyOne (); + ALOGD ("%s: NFA_P2P_DISC_EVT; notified disconn event", fn); + } + { + ALOGD ("%s: NFA_P2P_DISC_EVT; try guard congest event", fn); + SyncEventGuard guard1 (pConn->mCongEvent); + pConn->mCongEvent.notifyOne(); //unblock write (if congested) + ALOGD ("%s: NFA_P2P_DISC_EVT; notified congest event", fn); + } + { + ALOGD ("%s: NFA_P2P_DISC_EVT; try guard read event", fn); + SyncEventGuard guard2 (pConn->mReadEvent); + pConn->mReadEvent.notifyOne(); //unblock receive() + ALOGD ("%s: NFA_P2P_DISC_EVT; notified read event", fn); + } + sP2p.mDisconnectMutex.unlock (); + } + break; + + case NFA_P2P_DATA_EVT: + // Look for the connection block + if ((pConn = sP2p.findConnection(eventData->data.handle)) == NULL) + { + ALOGE ("%s: NFA_P2P_DATA_EVT: can't find conn for NFA handle: 0x%04x", fn, eventData->data.handle); + } + else + { + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: NFA_P2P_DATA_EVT; h=0x%X; remote sap=0x%X", fn, + eventData->data.handle, eventData->data.remote_sap); + SyncEventGuard guard (pConn->mReadEvent); + pConn->mReadEvent.notifyOne(); + } + break; + + case NFA_P2P_CONGEST_EVT: + // Look for the connection block + if ((pConn = sP2p.findConnection(eventData->congest.handle)) == NULL) + { + ALOGE ("%s: NFA_P2P_CONGEST_EVT: can't find conn for NFA handle: 0x%04x", fn, eventData->congest.handle); + } + else + { + ALOGD_IF ((appl_trace_level>=BT_TRACE_LEVEL_DEBUG), "%s: NFA_P2P_CONGEST_EVT; nfa handle: 0x%04x congested: %u", fn, + eventData->congest.handle, eventData->congest.is_congested); + + SyncEventGuard guard (pConn->mCongEvent); + pConn->mCongEvent.notifyOne(); + } + break; + + default: + ALOGE ("%s: unknown event 0x%X ????", fn, p2pEvent); + break; + } +} + + +/******************************************************************************* +** +** Function: connectionEventHandler +** +** Description: Receive events from the stack. +** event: Event code. +** eventData: Event data. +** +** Returns: None +** +*******************************************************************************/ +void PeerToPeer::connectionEventHandler (UINT8 event, tNFA_CONN_EVT_DATA* eventData) +{ + switch (event) + { + case NFA_SET_P2P_LISTEN_TECH_EVT: + { + SyncEventGuard guard (mSetTechEvent); + mSetTechEvent.notifyOne(); //unblock NFA_SetP2pListenTech() + break; + } + } +} + + +/******************************************************************************* +** +** Function: getNextJniHandle +** +** Description: Get a new JNI handle. +** +** Returns: A new JNI handle. +** +*******************************************************************************/ +PeerToPeer::tJNI_HANDLE PeerToPeer::getNewJniHandle () +{ + tJNI_HANDLE newHandle = 0; + + mNewJniHandleMutex.lock (); + newHandle = mNextJniHandle++; + mNewJniHandleMutex.unlock (); + return newHandle; +} + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + + +/******************************************************************************* +** +** Function: P2pServer +** +** Description: Initialize member variables. +** +** Returns: None +** +*******************************************************************************/ +P2pServer::P2pServer(PeerToPeer::tJNI_HANDLE jniHandle, const char* serviceName) +: mNfaP2pServerHandle (NFA_HANDLE_INVALID), + mJniHandle (jniHandle) +{ + mServiceName.assign (serviceName); + + memset (mServerConn, 0, sizeof(mServerConn)); +} + +bool P2pServer::registerWithStack() +{ + static const char fn [] = "P2pServer::registerWithStack"; + ALOGD ("%s: enter; service name: %s JNI handle: %u", fn, mServiceName.c_str(), mJniHandle); + tNFA_STATUS stat = NFA_STATUS_OK; + UINT8 serverSap = NFA_P2P_ANY_SAP; + + /********************** + default values for all LLCP parameters: + - Local Link MIU (LLCP_MIU) + - Option parameter (LLCP_OPT_VALUE) + - Response Waiting Time Index (LLCP_WAITING_TIME) + - Local Link Timeout (LLCP_LTO_VALUE) + - Inactivity Timeout as initiator role (LLCP_INIT_INACTIVITY_TIMEOUT) + - Inactivity Timeout as target role (LLCP_TARGET_INACTIVITY_TIMEOUT) + - Delay SYMM response (LLCP_DELAY_RESP_TIME) + - Data link connection timeout (LLCP_DATA_LINK_CONNECTION_TOUT) + - Delay timeout to send first PDU as initiator (LLCP_DELAY_TIME_TO_SEND_FIRST_PDU) + ************************/ + stat = NFA_P2pSetLLCPConfig (LLCP_MAX_MIU, + LLCP_OPT_VALUE, + LLCP_WAITING_TIME, + LLCP_LTO_VALUE, + 0, //use 0 for infinite timeout for symmetry procedure when acting as initiator + 0, //use 0 for infinite timeout for symmetry procedure when acting as target + LLCP_DELAY_RESP_TIME, + LLCP_DATA_LINK_CONNECTION_TOUT, + LLCP_DELAY_TIME_TO_SEND_FIRST_PDU); + if (stat != NFA_STATUS_OK) + ALOGE ("%s: fail set LLCP config; error=0x%X", fn, stat); + + if (sSnepServiceName.compare(mServiceName) == 0) + serverSap = LLCP_SAP_SNEP; //LLCP_SAP_SNEP == 4 + + { + SyncEventGuard guard (mRegServerEvent); + stat = NFA_P2pRegisterServer (serverSap, NFA_P2P_DLINK_TYPE, const_cast<char*>(mServiceName.c_str()), + PeerToPeer::nfaServerCallback); + if (stat != NFA_STATUS_OK) + { + ALOGE ("%s: fail register p2p server; error=0x%X", fn, stat); + return (false); + } + ALOGD ("%s: wait for listen-completion event", fn); + // Wait for NFA_P2P_REG_SERVER_EVT + mRegServerEvent.wait (); + } + + return (mNfaP2pServerHandle != NFA_HANDLE_INVALID); +} + +bool P2pServer::accept(PeerToPeer::tJNI_HANDLE serverJniHandle, PeerToPeer::tJNI_HANDLE connJniHandle, + int maxInfoUnit, int recvWindow) +{ + static const char fn [] = "P2pServer::accept"; + tNFA_STATUS nfaStat = NFA_STATUS_OK; + + sp<NfaConn> connection = allocateConnection(connJniHandle); + if (connection == NULL) { + ALOGE ("%s: failed to allocate new server connection", fn); + return false; + } + + { + // Wait for NFA_P2P_CONN_REQ_EVT or NFA_NDEF_DATA_EVT when remote device requests connection + SyncEventGuard guard (mConnRequestEvent); + ALOGD ("%s: serverJniHandle: %u; connJniHandle: %u; wait for incoming connection", fn, + serverJniHandle, connJniHandle); + mConnRequestEvent.wait(); + ALOGD ("%s: serverJniHandle: %u; connJniHandle: %u; nfa conn h: 0x%X; got incoming connection", fn, + serverJniHandle, connJniHandle, connection->mNfaConnHandle); + } + + if (connection->mNfaConnHandle == NFA_HANDLE_INVALID) + { + removeServerConnection(connJniHandle); + ALOGD ("%s: no handle assigned", fn); + return (false); + } + + ALOGD ("%s: serverJniHandle: %u; connJniHandle: %u; nfa conn h: 0x%X; try accept", fn, + serverJniHandle, connJniHandle, connection->mNfaConnHandle); + nfaStat = NFA_P2pAcceptConn (connection->mNfaConnHandle, maxInfoUnit, recvWindow); + + if (nfaStat != NFA_STATUS_OK) + { + ALOGE ("%s: fail to accept remote; error=0x%X", fn, nfaStat); + return (false); + } + + ALOGD ("%s: exit; serverJniHandle: %u; connJniHandle: %u; nfa conn h: 0x%X", fn, + serverJniHandle, connJniHandle, connection->mNfaConnHandle); + return (true); +} + +void P2pServer::unblockAll() +{ + AutoMutex mutex(mMutex); + for (int jj = 0; jj < MAX_NFA_CONNS_PER_SERVER; jj++) + { + if (mServerConn[jj] != NULL) + { + mServerConn[jj]->mNfaConnHandle = NFA_HANDLE_INVALID; + { + SyncEventGuard guard1 (mServerConn[jj]->mCongEvent); + mServerConn[jj]->mCongEvent.notifyOne (); //unblock write (if congested) + } + { + SyncEventGuard guard2 (mServerConn[jj]->mReadEvent); + mServerConn[jj]->mReadEvent.notifyOne (); //unblock receive() + } + } + } +} + +sp<NfaConn> P2pServer::allocateConnection (PeerToPeer::tJNI_HANDLE jniHandle) +{ + AutoMutex mutex(mMutex); + // First, find a free connection block to handle the connection + for (int ii = 0; ii < MAX_NFA_CONNS_PER_SERVER; ii++) + { + if (mServerConn[ii] == NULL) + { + mServerConn[ii] = new NfaConn; + mServerConn[ii]->mJniHandle = jniHandle; + return mServerConn[ii]; + } + } + + return NULL; +} + + +/******************************************************************************* +** +** Function: findServerConnection +** +** Description: Find a P2pServer that has the handle. +** nfaConnHandle: NFA connection handle. +** +** Returns: P2pServer object. +** +*******************************************************************************/ +sp<NfaConn> P2pServer::findServerConnection (tNFA_HANDLE nfaConnHandle) +{ + int jj = 0; + + AutoMutex mutex(mMutex); + for (jj = 0; jj < MAX_NFA_CONNS_PER_SERVER; jj++) + { + if ( (mServerConn[jj] != NULL) && (mServerConn[jj]->mNfaConnHandle == nfaConnHandle) ) + return (mServerConn[jj]); + } + + // If here, not found + return (NULL); +} + +/******************************************************************************* +** +** Function: findServerConnection +** +** Description: Find a P2pServer that has the handle. +** nfaConnHandle: NFA connection handle. +** +** Returns: P2pServer object. +** +*******************************************************************************/ +sp<NfaConn> P2pServer::findServerConnection (PeerToPeer::tJNI_HANDLE jniHandle) +{ + int jj = 0; + + AutoMutex mutex(mMutex); + for (jj = 0; jj < MAX_NFA_CONNS_PER_SERVER; jj++) + { + if ( (mServerConn[jj] != NULL) && (mServerConn[jj]->mJniHandle == jniHandle) ) + return (mServerConn[jj]); + } + + // If here, not found + return (NULL); +} + +/******************************************************************************* +** +** Function: removeServerConnection +** +** Description: Find a P2pServer that has the handle. +** nfaConnHandle: NFA connection handle. +** +** Returns: P2pServer object. +** +*******************************************************************************/ +bool P2pServer::removeServerConnection (PeerToPeer::tJNI_HANDLE jniHandle) +{ + int jj = 0; + + AutoMutex mutex(mMutex); + for (jj = 0; jj < MAX_NFA_CONNS_PER_SERVER; jj++) + { + if ( (mServerConn[jj] != NULL) && (mServerConn[jj]->mJniHandle == jniHandle) ) { + mServerConn[jj] = NULL; + return true; + } + } + + // If here, not found + return false; +} +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + + +/******************************************************************************* +** +** Function: P2pClient +** +** Description: Initialize member variables. +** +** Returns: None +** +*******************************************************************************/ +P2pClient::P2pClient () +: mNfaP2pClientHandle (NFA_HANDLE_INVALID), + mIsConnecting (false) +{ + mClientConn = new NfaConn(); +} + + +/******************************************************************************* +** +** Function: ~P2pClient +** +** Description: Free all resources. +** +** Returns: None +** +*******************************************************************************/ +P2pClient::~P2pClient () +{ +} + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + + +/******************************************************************************* +** +** Function: NfaConn +** +** Description: Initialize member variables. +** +** Returns: None +** +*******************************************************************************/ +NfaConn::NfaConn() +: mNfaConnHandle (NFA_HANDLE_INVALID), + mJniHandle (0), + mMaxInfoUnit (0), + mRecvWindow (0), + mRemoteMaxInfoUnit (0), + mRemoteRecvWindow (0) +{ +} + diff --git a/nci/jni/PeerToPeer.h b/nci/jni/PeerToPeer.h new file mode 100644 index 0000000..3e8ffec --- /dev/null +++ b/nci/jni/PeerToPeer.h @@ -0,0 +1,779 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Communicate with a peer using NFC-DEP, LLCP, SNEP. + */ +#pragma once +#include <utils/RefBase.h> +#include <utils/StrongPointer.h> +#include "SyncEvent.h" +#include "NfcJniUtil.h" +#include <string> +extern "C" +{ + #include "nfa_p2p_api.h" + #include "nfa_snep_api.h" +} + +class P2pServer; +class P2pClient; +class NfaConn; +#define MAX_NFA_CONNS_PER_SERVER 5 + +/***************************************************************************** +** +** Name: PeerToPeer +** +** Description: Communicate with a peer using NFC-DEP, LLCP, SNEP. +** +*****************************************************************************/ +class PeerToPeer +{ +public: + typedef unsigned int tJNI_HANDLE; + + /******************************************************************************* + ** + ** Function: PeerToPeer + ** + ** Description: Initialize member variables. + ** + ** Returns: None + ** + *******************************************************************************/ + PeerToPeer (); + + + /******************************************************************************* + ** + ** Function: ~PeerToPeer + ** + ** Description: Free all resources. + ** + ** Returns: None + ** + *******************************************************************************/ + ~PeerToPeer (); + + + /******************************************************************************* + ** + ** Function: getInstance + ** + ** Description: Get the singleton PeerToPeer object. + ** + ** Returns: Singleton PeerToPeer object. + ** + *******************************************************************************/ + static PeerToPeer& getInstance(); + + + /******************************************************************************* + ** + ** Function: initialize + ** + ** Description: Initialize member variables. + ** + ** Returns: None + ** + *******************************************************************************/ + void initialize (); + + + /******************************************************************************* + ** + ** Function: llcpActivatedHandler + ** + ** Description: Receive LLLCP-activated event from stack. + ** nat: JVM-related data. + ** activated: Event data. + ** + ** Returns: None + ** + *******************************************************************************/ + void llcpActivatedHandler (nfc_jni_native_data* nativeData, tNFA_LLCP_ACTIVATED& activated); + + + /******************************************************************************* + ** + ** Function: llcpDeactivatedHandler + ** + ** Description: Receive LLLCP-deactivated event from stack. + ** nat: JVM-related data. + ** deactivated: Event data. + ** + ** Returns: None + ** + *******************************************************************************/ + void llcpDeactivatedHandler (nfc_jni_native_data* nativeData, tNFA_LLCP_DEACTIVATED& deactivated); + + + /******************************************************************************* + ** + ** Function: connectionEventHandler + ** + ** Description: Receive events from the stack. + ** event: Event code. + ** eventData: Event data. + ** + ** Returns: None + ** + *******************************************************************************/ + void connectionEventHandler (UINT8 event, tNFA_CONN_EVT_DATA* eventData); + + + /******************************************************************************* + ** + ** Function: registerServer + ** + ** Description: Let a server start listening for peer's connection request. + ** jniHandle: Connection handle. + ** serviceName: Server's service name. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool registerServer (tJNI_HANDLE jniHandle, const char* serviceName); + + + /******************************************************************************* + ** + ** Function: deregisterServer + ** + ** Description: Stop a P2pServer from listening for peer. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool deregisterServer (tJNI_HANDLE jniHandle); + + + /******************************************************************************* + ** + ** Function: accept + ** + ** Description: Accept a peer's request to connect. + ** serverJniHandle: Server's handle. + ** connJniHandle: Connection handle. + ** maxInfoUnit: Maximum information unit. + ** recvWindow: Receive window size. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool accept (tJNI_HANDLE serverJniHandle, tJNI_HANDLE connJniHandle, int maxInfoUnit, int recvWindow); + + + /******************************************************************************* + ** + ** Function: createClient + ** + ** Description: Create a P2pClient object for a new out-bound connection. + ** jniHandle: Connection handle. + ** miu: Maximum information unit. + ** rw: Receive window size. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool createClient (tJNI_HANDLE jniHandle, UINT16 miu, UINT8 rw); + + + /******************************************************************************* + ** + ** Function: connectConnOriented + ** + ** Description: Estabish a connection-oriented connection to a peer. + ** jniHandle: Connection handle. + ** serviceName: Peer's service name. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool connectConnOriented (tJNI_HANDLE jniHandle, const char* serviceName); + + + /******************************************************************************* + ** + ** Function: connectConnOriented + ** + ** Description: Estabish a connection-oriented connection to a peer. + ** jniHandle: Connection handle. + ** destinationSap: Peer's service access point. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool connectConnOriented (tJNI_HANDLE jniHandle, UINT8 destinationSap); + + + /******************************************************************************* + ** + ** Function: send + ** + ** Description: Send data to peer. + ** jniHandle: Handle of connection. + ** buffer: Buffer of data. + ** bufferLen: Length of data. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool send (tJNI_HANDLE jniHandle, UINT8* buffer, UINT16 bufferLen); + + + /******************************************************************************* + ** + ** Function: receive + ** + ** Description: Receive data from peer. + ** jniHandle: Handle of connection. + ** buffer: Buffer to store data. + ** bufferLen: Max length of buffer. + ** actualLen: Actual length received. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool receive (tJNI_HANDLE jniHandle, UINT8* buffer, UINT16 bufferLen, UINT16& actualLen); + + + /******************************************************************************* + ** + ** Function: disconnectConnOriented + ** + ** Description: Disconnect a connection-oriented connection with peer. + ** jniHandle: Handle of connection. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool disconnectConnOriented (tJNI_HANDLE jniHandle); + + + /******************************************************************************* + ** + ** Function: getRemoteMaxInfoUnit + ** + ** Description: Get peer's max information unit. + ** jniHandle: Handle of the connection. + ** + ** Returns: Peer's max information unit. + ** + *******************************************************************************/ + UINT16 getRemoteMaxInfoUnit (tJNI_HANDLE jniHandle); + + + /******************************************************************************* + ** + ** Function: getRemoteRecvWindow + ** + ** Description: Get peer's receive window size. + ** jniHandle: Handle of the connection. + ** + ** Returns: Peer's receive window size. + ** + *******************************************************************************/ + UINT8 getRemoteRecvWindow (tJNI_HANDLE jniHandle); + + + /******************************************************************************* + ** + ** Function: setP2pListenMask + ** + ** Description: Sets the p2p listen technology mask. + ** p2pListenMask: the p2p listen mask to be set? + ** + ** Returns: None + ** + *******************************************************************************/ + void setP2pListenMask (tNFA_TECHNOLOGY_MASK p2pListenMask); + + /******************************************************************************* + ** + ** Function: enableP2pListening + ** + ** Description: Start/stop polling/listening to peer that supports P2P. + ** isEnable: Is enable polling/listening? + ** + ** Returns: None + ** + *******************************************************************************/ + void enableP2pListening (bool isEnable); + + + /******************************************************************************* + ** + ** Function: handleNfcOnOff + ** + ** Description: Handle events related to turning NFC on/off by the user. + ** isOn: Is NFC turning on? + ** + ** Returns: None + ** + *******************************************************************************/ + void handleNfcOnOff (bool isOn); + + + /******************************************************************************* + ** + ** Function: getNextJniHandle + ** + ** Description: Get a new JNI handle. + ** + ** Returns: A new JNI handle. + ** + *******************************************************************************/ + tJNI_HANDLE getNewJniHandle (); + + /******************************************************************************* + ** + ** Function: nfaServerCallback + ** + ** Description: Receive LLCP-related events from the stack. + ** p2pEvent: Event code. + ** eventData: Event data. + ** + ** Returns: None + ** + *******************************************************************************/ + static void nfaServerCallback (tNFA_P2P_EVT p2pEvent, tNFA_P2P_EVT_DATA *eventData); + + + /******************************************************************************* + ** + ** Function: nfaClientCallback + ** + ** Description: Receive LLCP-related events from the stack. + ** p2pEvent: Event code. + ** eventData: Event data. + ** + ** Returns: None + ** + *******************************************************************************/ + static void nfaClientCallback (tNFA_P2P_EVT p2pEvent, tNFA_P2P_EVT_DATA *eventData); + +private: + static const int sMax = 10; + static PeerToPeer sP2p; + + // Variables below only accessed from a single thread + UINT16 mRemoteWKS; // Peer's well known services + bool mIsP2pListening; // If P2P listening is enabled or not + tNFA_TECHNOLOGY_MASK mP2pListenTechMask; // P2P Listen mask + + // Variable below is protected by mNewJniHandleMutex + tJNI_HANDLE mNextJniHandle; + + // Variables below protected by mMutex + // A note on locking order: mMutex in PeerToPeer is *ALWAYS* + // locked before any locks / guards in P2pServer / P2pClient + Mutex mMutex; + android::sp<P2pServer> mServers [sMax]; + android::sp<P2pClient> mClients [sMax]; + + // Synchronization variables + SyncEvent mSetTechEvent; // completion event for NFA_SetP2pListenTech() + SyncEvent mSnepDefaultServerStartStopEvent; // completion event for NFA_SnepStartDefaultServer(), NFA_SnepStopDefaultServer() + SyncEvent mSnepRegisterEvent; // completion event for NFA_SnepRegisterClient() + Mutex mDisconnectMutex; // synchronize the disconnect operation + Mutex mNewJniHandleMutex; // synchronize the creation of a new JNI handle + + /******************************************************************************* + ** + ** Function: snepClientCallback + ** + ** Description: Receive SNEP-related events from the stack. + ** snepEvent: Event code. + ** eventData: Event data. + ** + ** Returns: None + ** + *******************************************************************************/ + static void snepClientCallback (tNFA_SNEP_EVT snepEvent, tNFA_SNEP_EVT_DATA *eventData); + + + /******************************************************************************* + ** + ** Function: ndefTypeCallback + ** + ** Description: Receive NDEF-related events from the stack. + ** ndefEvent: Event code. + ** eventData: Event data. + ** + ** Returns: None + ** + *******************************************************************************/ + static void ndefTypeCallback (tNFA_NDEF_EVT event, tNFA_NDEF_EVT_DATA *evetnData); + + + /******************************************************************************* + ** + ** Function: findServer + ** + ** Description: Find a PeerToPeer object by connection handle. + ** nfaP2pServerHandle: Connectin handle. + ** + ** Returns: PeerToPeer object. + ** + *******************************************************************************/ + android::sp<P2pServer> findServerLocked (tNFA_HANDLE nfaP2pServerHandle); + + + /******************************************************************************* + ** + ** Function: findServer + ** + ** Description: Find a PeerToPeer object by connection handle. + ** serviceName: service name. + ** + ** Returns: PeerToPeer object. + ** + *******************************************************************************/ + android::sp<P2pServer> findServerLocked (tJNI_HANDLE jniHandle); + + + /******************************************************************************* + ** + ** Function: findServer + ** + ** Description: Find a PeerToPeer object by service name + ** serviceName: service name. + ** + ** Returns: PeerToPeer object. + ** + *******************************************************************************/ + android::sp<P2pServer> findServerLocked (const char *serviceName); + + + /******************************************************************************* + ** + ** Function: removeServer + ** + ** Description: Free resources related to a server. + ** jniHandle: Connection handle. + ** + ** Returns: None + ** + *******************************************************************************/ + void removeServer (tJNI_HANDLE jniHandle); + + + /******************************************************************************* + ** + ** Function: removeConn + ** + ** Description: Free resources related to a connection. + ** jniHandle: Connection handle. + ** + ** Returns: None + ** + *******************************************************************************/ + void removeConn (tJNI_HANDLE jniHandle); + + + /******************************************************************************* + ** + ** Function: createDataLinkConn + ** + ** Description: Establish a connection-oriented connection to a peer. + ** jniHandle: Connection handle. + ** serviceName: Peer's service name. + ** destinationSap: Peer's service access point. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool createDataLinkConn (tJNI_HANDLE jniHandle, const char* serviceName, UINT8 destinationSap); + + + /******************************************************************************* + ** + ** Function: findClient + ** + ** Description: Find a PeerToPeer object with a client connection handle. + ** nfaConnHandle: Connection handle. + ** + ** Returns: PeerToPeer object. + ** + *******************************************************************************/ + android::sp<P2pClient> findClient (tNFA_HANDLE nfaConnHandle); + + + /******************************************************************************* + ** + ** Function: findClient + ** + ** Description: Find a PeerToPeer object with a client connection handle. + ** jniHandle: Connection handle. + ** + ** Returns: PeerToPeer object. + ** + *******************************************************************************/ + android::sp<P2pClient> findClient (tJNI_HANDLE jniHandle); + + + /******************************************************************************* + ** + ** Function: findClientCon + ** + ** Description: Find a PeerToPeer object with a client connection handle. + ** nfaConnHandle: Connection handle. + ** + ** Returns: PeerToPeer object. + ** + *******************************************************************************/ + android::sp<P2pClient> findClientCon (tNFA_HANDLE nfaConnHandle); + + + /******************************************************************************* + ** + ** Function: findConnection + ** + ** Description: Find a PeerToPeer object with a connection handle. + ** nfaConnHandle: Connection handle. + ** + ** Returns: PeerToPeer object. + ** + *******************************************************************************/ + android::sp<NfaConn> findConnection (tNFA_HANDLE nfaConnHandle); + + + /******************************************************************************* + ** + ** Function: findConnection + ** + ** Description: Find a PeerToPeer object with a connection handle. + ** jniHandle: Connection handle. + ** + ** Returns: PeerToPeer object. + ** + *******************************************************************************/ + android::sp<NfaConn> findConnection (tJNI_HANDLE jniHandle); +}; + + +/***************************************************************************** +** +** Name: NfaConn +** +** Description: Store information about a connection related to a peer. +** +*****************************************************************************/ +class NfaConn : public android::RefBase +{ +public: + tNFA_HANDLE mNfaConnHandle; // NFA handle of the P2P connection + PeerToPeer::tJNI_HANDLE mJniHandle; // JNI handle of the P2P connection + UINT16 mMaxInfoUnit; + UINT8 mRecvWindow; + UINT16 mRemoteMaxInfoUnit; + UINT8 mRemoteRecvWindow; + SyncEvent mReadEvent; // event for reading + SyncEvent mCongEvent; // event for congestion + SyncEvent mDisconnectingEvent; // event for disconnecting + + + /******************************************************************************* + ** + ** Function: NfaConn + ** + ** Description: Initialize member variables. + ** + ** Returns: None + ** + *******************************************************************************/ + NfaConn(); +}; + + +/***************************************************************************** +** +** Name: P2pServer +** +** Description: Store information about an in-bound connection from a peer. +** +*****************************************************************************/ +class P2pServer : public android::RefBase +{ +public: + static const std::string sSnepServiceName; + + tNFA_HANDLE mNfaP2pServerHandle; // NFA p2p handle of local server + PeerToPeer::tJNI_HANDLE mJniHandle; // JNI Handle + SyncEvent mRegServerEvent; // for NFA_P2pRegisterServer() + SyncEvent mConnRequestEvent; // for accept() + std::string mServiceName; + + /******************************************************************************* + ** + ** Function: P2pServer + ** + ** Description: Initialize member variables. + ** + ** Returns: None + ** + *******************************************************************************/ + P2pServer (PeerToPeer::tJNI_HANDLE jniHandle, const char* serviceName); + + /******************************************************************************* + ** + ** Function: registerWithStack + ** + ** Description: Register this server with the stack. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool registerWithStack(); + + /******************************************************************************* + ** + ** Function: accept + ** + ** Description: Accept a peer's request to connect. + ** serverJniHandle: Server's handle. + ** connJniHandle: Connection handle. + ** maxInfoUnit: Maximum information unit. + ** recvWindow: Receive window size. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool accept (PeerToPeer::tJNI_HANDLE serverJniHandle, PeerToPeer::tJNI_HANDLE connJniHandle, + int maxInfoUnit, int recvWindow); + + /******************************************************************************* + ** + ** Function: unblockAll + ** + ** Description: Unblocks all server connections + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + void unblockAll(); + + /******************************************************************************* + ** + ** Function: findServerConnection + ** + ** Description: Find a P2pServer that has the handle. + ** nfaConnHandle: NFA connection handle. + ** + ** Returns: P2pServer object. + ** + *******************************************************************************/ + android::sp<NfaConn> findServerConnection (tNFA_HANDLE nfaConnHandle); + + /******************************************************************************* + ** + ** Function: findServerConnection + ** + ** Description: Find a P2pServer that has the handle. + ** jniHandle: JNI connection handle. + ** + ** Returns: P2pServer object. + ** + *******************************************************************************/ + android::sp<NfaConn> findServerConnection (PeerToPeer::tJNI_HANDLE jniHandle); + + /******************************************************************************* + ** + ** Function: removeServerConnection + ** + ** Description: Remove a server connection with the provided handle. + ** jniHandle: JNI connection handle. + ** + ** Returns: True if connection found and removed. + ** + *******************************************************************************/ + bool removeServerConnection(PeerToPeer::tJNI_HANDLE jniHandle); + +private: + Mutex mMutex; + // mServerConn is protected by mMutex + android::sp<NfaConn> mServerConn[MAX_NFA_CONNS_PER_SERVER]; + + /******************************************************************************* + ** + ** Function: allocateConnection + ** + ** Description: Allocate a new connection to accept on + ** jniHandle: JNI connection handle. + ** + ** Returns: Allocated connection object + ** NULL if the maximum number of connections was reached + ** + *******************************************************************************/ + android::sp<NfaConn> allocateConnection (PeerToPeer::tJNI_HANDLE jniHandle); +}; + + +/***************************************************************************** +** +** Name: P2pClient +** +** Description: Store information about an out-bound connection to a peer. +** +*****************************************************************************/ +class P2pClient : public android::RefBase +{ +public: + tNFA_HANDLE mNfaP2pClientHandle; // NFA p2p handle of client + bool mIsConnecting; // Set true while connecting + android::sp<NfaConn> mClientConn; + SyncEvent mRegisteringEvent; // For client registration + SyncEvent mConnectingEvent; // for NFA_P2pConnectByName or Sap() + SyncEvent mSnepEvent; // To wait for SNEP completion + + /******************************************************************************* + ** + ** Function: P2pClient + ** + ** Description: Initialize member variables. + ** + ** Returns: None + ** + *******************************************************************************/ + P2pClient (); + + + /******************************************************************************* + ** + ** Function: ~P2pClient + ** + ** Description: Free all resources. + ** + ** Returns: None + ** + *******************************************************************************/ + ~P2pClient (); + + + /******************************************************************************* + ** + ** Function: unblock + ** + ** Description: Unblocks any threads that are locked on this connection + ** + ** Returns: None + ** + *******************************************************************************/ + void unblock(); +}; + diff --git a/nci/jni/Pn544Interop.cpp b/nci/jni/Pn544Interop.cpp new file mode 100644 index 0000000..be92c75 --- /dev/null +++ b/nci/jni/Pn544Interop.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/***************************************************************************** +** +** Name: Pn544Interop.cpp +** +** Description: Implement operations that provide compatibility with NXP +** PN544 controller. Specifically facilitate peer-to-peer +** operations with PN544 controller. +** +*****************************************************************************/ +#include "OverrideLog.h" +#include "Pn544Interop.h" +#include "IntervalTimer.h" +#include "Mutex.h" +#include "NfcTag.h" +namespace android +{ + extern void startStopPolling (bool isStartPolling); +} + + +/***************************************************************************** +** +** private variables and functions +** +*****************************************************************************/ + + +static const int gIntervalTime = 1000; //millisecond between the check to restore polling +static IntervalTimer gTimer; +static Mutex gMutex; +static void pn544InteropStartPolling (union sigval); //callback function for interval timer +static bool gIsBusy = false; //is timer busy? +static bool gAbortNow = false; //stop timer during next callback + + +/******************************************************************************* +** +** Function: pn544InteropStopPolling +** +** Description: Stop polling to let NXP PN544 controller poll. +** PN544 should activate in P2P mode. +** +** Returns: None +** +*******************************************************************************/ +void pn544InteropStopPolling () +{ + ALOGD ("%s: enter", __FUNCTION__); + gMutex.lock (); + gTimer.kill (); + android::startStopPolling (false); + gIsBusy = true; + gAbortNow = false; + gTimer.set (gIntervalTime, pn544InteropStartPolling); //after some time, start polling again + gMutex.unlock (); + ALOGD ("%s: exit", __FUNCTION__); +} + + +/******************************************************************************* +** +** Function: pn544InteropStartPolling +** +** Description: Start polling when activation state is idle. +** sigval: Unused. +** +** Returns: None +** +*******************************************************************************/ +void pn544InteropStartPolling (union sigval) +{ + ALOGD ("%s: enter", __FUNCTION__); + gMutex.lock (); + NfcTag::ActivationState state = NfcTag::getInstance ().getActivationState (); + + if (gAbortNow) + { + ALOGD ("%s: abort now", __FUNCTION__); + gIsBusy = false; + goto TheEnd; + } + + if (state == NfcTag::Idle) + { + ALOGD ("%s: start polling", __FUNCTION__); + android::startStopPolling (true); + gIsBusy = false; + } + else + { + ALOGD ("%s: try again later", __FUNCTION__); + gTimer.set (gIntervalTime, pn544InteropStartPolling); //after some time, start polling again + } + +TheEnd: + gMutex.unlock (); + ALOGD ("%s: exit", __FUNCTION__); +} + + +/******************************************************************************* +** +** Function: pn544InteropIsBusy +** +** Description: Is the code performing operations? +** +** Returns: True if the code is busy. +** +*******************************************************************************/ +bool pn544InteropIsBusy () +{ + bool isBusy = false; + gMutex.lock (); + isBusy = gIsBusy; + gMutex.unlock (); + ALOGD ("%s: %u", __FUNCTION__, isBusy); + return isBusy; +} + + +/******************************************************************************* +** +** Function: pn544InteropAbortNow +** +** Description: Request to abort all operations. +** +** Returns: None. +** +*******************************************************************************/ +void pn544InteropAbortNow () +{ + ALOGD ("%s", __FUNCTION__); + gMutex.lock (); + gAbortNow = true; + gMutex.unlock (); +} + diff --git a/nci/jni/Pn544Interop.h b/nci/jni/Pn544Interop.h new file mode 100644 index 0000000..c9a2df6 --- /dev/null +++ b/nci/jni/Pn544Interop.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/***************************************************************************** +** +** Name: Pn544Interop.h +** +** Description: Implement operations that provide compatibility with NXP +** PN544 controller. Specifically facilitate peer-to-peer +** operations with PN544 controller. +** +*****************************************************************************/ +#pragma once +#include "NfcJniUtil.h" + + +/******************************************************************************* +** +** Function: pn544InteropStopPolling +** +** Description: Stop polling to let NXP PN544 controller poll. +** PN544 should activate in P2P mode. +** +** Returns: None +** +*******************************************************************************/ +void pn544InteropStopPolling (); + + +/******************************************************************************* +** +** Function: pn544InteropIsBusy +** +** Description: Is the code performing operations? +** +** Returns: True if the code is busy. +** +*******************************************************************************/ +bool pn544InteropIsBusy (); + + +/******************************************************************************* +** +** Function: pn544InteropAbortNow +** +** Description: Request to abort all operations. +** +** Returns: None. +** +*******************************************************************************/ +void pn544InteropAbortNow (); diff --git a/nci/jni/PowerSwitch.cpp b/nci/jni/PowerSwitch.cpp new file mode 100755 index 0000000..9d58c74 --- /dev/null +++ b/nci/jni/PowerSwitch.cpp @@ -0,0 +1,439 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Adjust the controller's power states. + */ +#include "OverrideLog.h" +#include "PowerSwitch.h" +#include "NfcJniUtil.h" +#include "config.h" +#include "SecureElement.h" + + +namespace android +{ + void doStartupConfig (); +} + + +PowerSwitch PowerSwitch::sPowerSwitch; +const PowerSwitch::PowerActivity PowerSwitch::DISCOVERY=0x01; +const PowerSwitch::PowerActivity PowerSwitch::SE_ROUTING=0x02; +const PowerSwitch::PowerActivity PowerSwitch::SE_CONNECTED=0x04; + +/******************************************************************************* +** +** Function: PowerSwitch +** +** Description: Initialize member variables. +** +** Returns: None +** +*******************************************************************************/ +PowerSwitch::PowerSwitch () +: mCurrLevel (UNKNOWN_LEVEL), + mCurrDeviceMgtPowerState (NFA_DM_PWR_STATE_UNKNOWN), + mDesiredScreenOffPowerState (0), + mCurrActivity(0) +{ +} + + +/******************************************************************************* +** +** Function: ~PowerSwitch +** +** Description: Release all resources. +** +** Returns: None +** +*******************************************************************************/ +PowerSwitch::~PowerSwitch () +{ +} + + +/******************************************************************************* +** +** Function: getInstance +** +** Description: Get the singleton of this object. +** +** Returns: Reference to this object. +** +*******************************************************************************/ +PowerSwitch& PowerSwitch::getInstance () +{ + return sPowerSwitch; +} + + +/******************************************************************************* +** +** Function: initialize +** +** Description: Initialize member variables. +** +** Returns: None +** +*******************************************************************************/ +void PowerSwitch::initialize (PowerLevel level) +{ + static const char fn [] = "PowerSwitch::initialize"; + + mMutex.lock (); + + ALOGD ("%s: level=%s (%u)", fn, powerLevelToString(level), level); + GetNumValue (NAME_SCREEN_OFF_POWER_STATE, &mDesiredScreenOffPowerState, sizeof(mDesiredScreenOffPowerState)); + ALOGD ("%s: desired screen-off state=%d", fn, mDesiredScreenOffPowerState); + + switch (level) + { + case FULL_POWER: + mCurrDeviceMgtPowerState = NFA_DM_PWR_MODE_FULL; + mCurrLevel = level; + break; + + case UNKNOWN_LEVEL: + mCurrDeviceMgtPowerState = NFA_DM_PWR_STATE_UNKNOWN; + mCurrLevel = level; + break; + + default: + ALOGE ("%s: not handled", fn); + break; + } + mMutex.unlock (); +} + + +/******************************************************************************* +** +** Function: getLevel +** +** Description: Get the current power level of the controller. +** +** Returns: Power level. +** +*******************************************************************************/ +PowerSwitch::PowerLevel PowerSwitch::getLevel () +{ + PowerLevel level = UNKNOWN_LEVEL; + mMutex.lock (); + level = mCurrLevel; + mMutex.unlock (); + return level; +} + + +/******************************************************************************* +** +** Function: setLevel +** +** Description: Set the controller's power level. +** level: power level. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool PowerSwitch::setLevel (PowerLevel newLevel) +{ + static const char fn [] = "PowerSwitch::setLevel"; + bool retval = false; + + mMutex.lock (); + + ALOGD ("%s: level=%s (%u)", fn, powerLevelToString(newLevel), newLevel); + if (mCurrLevel == newLevel) + { + retval = true; + goto TheEnd; + } + + switch (newLevel) + { + case FULL_POWER: + if (mCurrDeviceMgtPowerState == NFA_DM_PWR_MODE_OFF_SLEEP) + retval = setPowerOffSleepState (false); + break; + + case LOW_POWER: + case POWER_OFF: + if (isPowerOffSleepFeatureEnabled()) + retval = setPowerOffSleepState (true); + else if (mDesiredScreenOffPowerState == 1) //.conf file desires full-power + { + mCurrLevel = FULL_POWER; + retval = true; + } + break; + + default: + ALOGE ("%s: not handled", fn); + break; + } + +TheEnd: + mMutex.unlock (); + return retval; +} + +/******************************************************************************* +** +** Function: setModeOff +** +** Description: Set a mode to be deactive. +** +** Returns: True if any mode is still active. +** +*******************************************************************************/ +bool PowerSwitch::setModeOff (PowerActivity deactivated) +{ + bool retVal = false; + + mMutex.lock (); + mCurrActivity &= ~deactivated; + retVal = mCurrActivity != 0; + ALOGD ("PowerSwitch::setModeOff(deactivated=0x%x) : mCurrActivity=0x%x", deactivated, mCurrActivity); + mMutex.unlock (); + return retVal; +} + + +/******************************************************************************* +** +** Function: setModeOn +** +** Description: Set a mode to be active. +** +** Returns: True if any mode is active. +** +*******************************************************************************/ +bool PowerSwitch::setModeOn (PowerActivity activated) +{ + bool retVal = false; + + mMutex.lock (); + mCurrActivity |= activated; + retVal = mCurrActivity != 0; + ALOGD ("PowerSwitch::setModeOn(activated=0x%x) : mCurrActivity=0x%x", activated, mCurrActivity); + mMutex.unlock (); + return retVal; +} + + +/******************************************************************************* +** +** Function: setPowerOffSleepState +** +** Description: Adjust controller's power-off-sleep state. +** sleep: whether to enter sleep state. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool PowerSwitch::setPowerOffSleepState (bool sleep) +{ + static const char fn [] = "PowerSwitch::setPowerOffSleepState"; + ALOGD ("%s: enter; sleep=%u", fn, sleep); + tNFA_STATUS stat = NFA_STATUS_FAILED; + bool retval = false; + + if (sleep) //enter power-off-sleep state + { + //make sure the current power state is ON + if (mCurrDeviceMgtPowerState != NFA_DM_PWR_MODE_OFF_SLEEP) + { + SyncEventGuard guard (mPowerStateEvent); + ALOGD ("%s: try power off", fn); + stat = NFA_PowerOffSleepMode (TRUE); + if (stat == NFA_STATUS_OK) + { + mPowerStateEvent.wait (); + mCurrLevel = LOW_POWER; + } + else + { + ALOGE ("%s: API fail; stat=0x%X", fn, stat); + goto TheEnd; + } + } + else + { + ALOGE ("%s: power is not ON; curr device mgt power state=%s (%u)", fn, + deviceMgtPowerStateToString (mCurrDeviceMgtPowerState), mCurrDeviceMgtPowerState); + goto TheEnd; + } + } + else //exit power-off-sleep state + { + //make sure the current power state is OFF + if (mCurrDeviceMgtPowerState != NFA_DM_PWR_MODE_FULL) + { + mCurrDeviceMgtPowerState = NFA_DM_PWR_STATE_UNKNOWN; + SyncEventGuard guard (mPowerStateEvent); + ALOGD ("%s: try full power", fn); + stat = NFA_PowerOffSleepMode (FALSE); + if (stat == NFA_STATUS_OK) + { + mPowerStateEvent.wait (); + if (mCurrDeviceMgtPowerState != NFA_DM_PWR_MODE_FULL) + { + ALOGE ("%s: unable to full power; curr device mgt power stat=%s (%u)", fn, + deviceMgtPowerStateToString (mCurrDeviceMgtPowerState), mCurrDeviceMgtPowerState); + goto TheEnd; + } + android::doStartupConfig (); + mCurrLevel = FULL_POWER; + } + else + { + ALOGE ("%s: API fail; stat=0x%X", fn, stat); + goto TheEnd; + } + } + else + { + ALOGE ("%s: not in power-off state; curr device mgt power state=%s (%u)", fn, + deviceMgtPowerStateToString (mCurrDeviceMgtPowerState), mCurrDeviceMgtPowerState); + goto TheEnd; + } + } + + retval = true; +TheEnd: + ALOGD ("%s: exit; return %u", fn, retval); + return retval; +} + + +/******************************************************************************* +** +** Function: deviceMgtPowerStateToString +** +** Description: Decode power level to a string. +** deviceMgtPowerState: power level. +** +** Returns: Text representation of power level. +** +*******************************************************************************/ +const char* PowerSwitch::deviceMgtPowerStateToString (UINT8 deviceMgtPowerState) +{ + switch (deviceMgtPowerState) + { + case NFA_DM_PWR_MODE_FULL: + return "DM-FULL"; + case NFA_DM_PWR_MODE_OFF_SLEEP: + return "DM-OFF"; + default: + return "DM-unknown????"; + } +} + + +/******************************************************************************* +** +** Function: powerLevelToString +** +** Description: Decode power level to a string. +** level: power level. +** +** Returns: Text representation of power level. +** +*******************************************************************************/ +const char* PowerSwitch::powerLevelToString (PowerLevel level) +{ + switch (level) + { + case UNKNOWN_LEVEL: + return "PS-UNKNOWN"; + case FULL_POWER: + return "PS-FULL"; + case LOW_POWER: + return "PS-LOW-POWER"; + case POWER_OFF: + return "PS-POWER-OFF"; + default: + return "PS-unknown????"; + } +} + + +/******************************************************************************* +** +** Function: abort +** +** Description: Abort and unblock currrent operation. +** +** Returns: None +** +*******************************************************************************/ +void PowerSwitch::abort () +{ + static const char fn [] = "PowerSwitch::abort"; + ALOGD ("%s", fn); + SyncEventGuard guard (mPowerStateEvent); + mPowerStateEvent.notifyOne (); +} + + +/******************************************************************************* +** +** Function: deviceManagementCallback +** +** Description: Callback function for the stack. +** event: event ID. +** eventData: event's data. +** +** Returns: None +** +*******************************************************************************/ +void PowerSwitch::deviceManagementCallback (UINT8 event, tNFA_DM_CBACK_DATA* eventData) +{ + static const char fn [] = "PowerSwitch::deviceManagementCallback"; + + switch (event) + { + case NFA_DM_PWR_MODE_CHANGE_EVT: + { + tNFA_DM_PWR_MODE_CHANGE& power_mode = eventData->power_mode; + ALOGD ("%s: NFA_DM_PWR_MODE_CHANGE_EVT; status=%u; device mgt power mode=%s (%u)", fn, + power_mode.status, sPowerSwitch.deviceMgtPowerStateToString (power_mode.power_mode), power_mode.power_mode); + SyncEventGuard guard (sPowerSwitch.mPowerStateEvent); + if (power_mode.status == NFA_STATUS_OK) + sPowerSwitch.mCurrDeviceMgtPowerState = power_mode.power_mode; + sPowerSwitch.mPowerStateEvent.notifyOne (); + } + break; + } +} + + +/******************************************************************************* +** +** Function: isPowerOffSleepFeatureEnabled +** +** Description: Whether power-off-sleep feature is enabled in .conf file. +** +** Returns: True if feature is enabled. +** +*******************************************************************************/ +bool PowerSwitch::isPowerOffSleepFeatureEnabled () +{ + return mDesiredScreenOffPowerState == 0; +} + diff --git a/nci/jni/PowerSwitch.h b/nci/jni/PowerSwitch.h new file mode 100755 index 0000000..d311c23 --- /dev/null +++ b/nci/jni/PowerSwitch.h @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Adjust the controller's power states. + */ +#pragma once +#include "nfa_api.h" +#include "SyncEvent.h" + + +/***************************************************************************** +** +** Name: PowerSwitch +** +** Description: Adjust the controller's power states. +** +*****************************************************************************/ +class PowerSwitch +{ +public: + + + /******************************************************************************* + ** + ** Description: UNKNOWN_LEVEL: power level is unknown because the stack is off. + ** FULL_POWER: controller is in full-power state. + ** LOW_POWER: controller is in lower-power state. + ** + *******************************************************************************/ + enum PowerLevel {UNKNOWN_LEVEL, FULL_POWER, LOW_POWER, POWER_OFF}; + + /******************************************************************************* + ** + ** Description: DISCOVERY: Discovery is enabled + ** SE_ROUTING: Routing to SE is enabled. + ** SE_CONNECTED: SE is connected. + ** + *******************************************************************************/ + typedef int PowerActivity; + static const PowerActivity DISCOVERY; + static const PowerActivity SE_ROUTING; + static const PowerActivity SE_CONNECTED; + + /******************************************************************************* + ** + ** Description: Platform Power Level, copied from NativeNfcBrcmPowerMode.java. + ** UNKNOWN_LEVEL: power level is unknown. + ** POWER_OFF: The phone is turned OFF + ** SCREEN_OFF: The phone is turned ON but screen is OFF + ** SCREEN_ON_LOCKED: The phone is turned ON, screen is ON but user locked + ** SCREEN_ON_UNLOCKED: The phone is turned ON, screen is ON, and user unlocked + ** + *******************************************************************************/ + static const int PLATFORM_UNKNOWN_LEVEL = 0; + static const int PLATFORM_POWER_OFF = 1; + static const int PLATFORM_SCREEN_OFF = 2; + static const int PLATFORM_SCREEN_ON_LOCKED = 3; + static const int PLATFORM_SCREEN_ON_UNLOCKED = 4; + + static const int VBAT_MONITOR_ENABLED = 1; + static const int VBAT_MONITOR_PRIMARY_THRESHOLD = 5; + static const int VBAT_MONITOR_SECONDARY_THRESHOLD = 8; + /******************************************************************************* + ** + ** Function: PowerSwitch + ** + ** Description: Initialize member variables. + ** + ** Returns: None + ** + *******************************************************************************/ + PowerSwitch (); + + + /******************************************************************************* + ** + ** Function: ~PowerSwitch + ** + ** Description: Release all resources. + ** + ** Returns: None + ** + *******************************************************************************/ + ~PowerSwitch (); + + + /******************************************************************************* + ** + ** Function: getInstance + ** + ** Description: Get the singleton of this object. + ** + ** Returns: Reference to this object. + ** + *******************************************************************************/ + static PowerSwitch& getInstance (); + + /******************************************************************************* + ** + ** Function: initialize + ** + ** Description: Initialize member variables. + ** + ** Returns: None + ** + *******************************************************************************/ + void initialize (PowerLevel level); + + + /******************************************************************************* + ** + ** Function: getLevel + ** + ** Description: Get the current power level of the controller. + ** + ** Returns: Power level. + ** + *******************************************************************************/ + PowerLevel getLevel (); + + + /******************************************************************************* + ** + ** Function: setLevel + ** + ** Description: Set the controller's power level. + ** level: power level. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool setLevel (PowerLevel level); + + + /******************************************************************************* + ** + ** Function: setModeOff + ** + ** Description: Set a mode to be deactive. + ** + ** Returns: True if any mode is still active. + ** + *******************************************************************************/ + bool setModeOff (PowerActivity deactivated); + + + /******************************************************************************* + ** + ** Function: setModeOn + ** + ** Description: Set a mode to be active. + ** + ** Returns: True if any mode is active. + ** + *******************************************************************************/ + bool setModeOn (PowerActivity activated); + + + /******************************************************************************* + ** + ** Function: abort + ** + ** Description: Abort and unblock currrent operation. + ** + ** Returns: None + ** + *******************************************************************************/ + void abort (); + + + /******************************************************************************* + ** + ** Function: deviceManagementCallback + ** + ** Description: Callback function for the stack. + ** event: event ID. + ** eventData: event's data. + ** + ** Returns: None + ** + *******************************************************************************/ + static void deviceManagementCallback (UINT8 event, tNFA_DM_CBACK_DATA* eventData); + + + /******************************************************************************* + ** + ** Function: isPowerOffSleepFeatureEnabled + ** + ** Description: Whether power-off-sleep feature is enabled in .conf file. + ** + ** Returns: True if feature is enabled. + ** + *******************************************************************************/ + bool isPowerOffSleepFeatureEnabled (); + +private: + PowerLevel mCurrLevel; + UINT8 mCurrDeviceMgtPowerState; //device management power state; such as NFA_DM_PWR_STATE_??? + int mDesiredScreenOffPowerState; //read from .conf file; 0=power-off-sleep; 1=full power; 2=CE4 power + static PowerSwitch sPowerSwitch; //singleton object + static const UINT8 NFA_DM_PWR_STATE_UNKNOWN = -1; //device management power state power state is unknown + SyncEvent mPowerStateEvent; + PowerActivity mCurrActivity; + Mutex mMutex; + + + /******************************************************************************* + ** + ** Function: setPowerOffSleepState + ** + ** Description: Adjust controller's power-off-sleep state. + ** sleep: whether to enter sleep state. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool setPowerOffSleepState (bool sleep); + + + /******************************************************************************* + ** + ** Function: deviceMgtPowerStateToString + ** + ** Description: Decode power level to a string. + ** deviceMgtPowerState: power level. + ** + ** Returns: Text representation of power level. + ** + *******************************************************************************/ + const char* deviceMgtPowerStateToString (UINT8 deviceMgtPowerState); + + + /******************************************************************************* + ** + ** Function: powerLevelToString + ** + ** Description: Decode power level to a string. + ** level: power level. + ** + ** Returns: Text representation of power level. + ** + *******************************************************************************/ + const char* powerLevelToString (PowerLevel level); +}; diff --git a/nci/jni/RouteDataSet.cpp b/nci/jni/RouteDataSet.cpp new file mode 100644 index 0000000..1458776 --- /dev/null +++ b/nci/jni/RouteDataSet.cpp @@ -0,0 +1,555 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Import and export general routing data using a XML file. + */ +#include "OverrideLog.h" +#include "RouteDataSet.h" +#include "libxml/xmlmemory.h" +#include <errno.h> +#include <sys/stat.h> + +extern char bcm_nfc_location[]; + + +/******************************************************************************* +** +** Function: AidBuffer +** +** Description: Parse a string of hex numbers. Store result in an array of +** bytes. +** aid: string of hex numbers. +** +** Returns: None. +** +*******************************************************************************/ +AidBuffer::AidBuffer (std::string& aid) +: mBuffer (NULL), + mBufferLen (0) +{ + unsigned int num = 0; + const char delimiter = ':'; + std::string::size_type pos1 = 0; + std::string::size_type pos2 = aid.find_first_of (delimiter); + + //parse the AID string; each hex number is separated by a colon; + mBuffer = new UINT8 [aid.length()]; + while (true) + { + num = 0; + if (pos2 == std::string::npos) + { + sscanf (aid.substr(pos1).c_str(), "%x", &num); + mBuffer [mBufferLen] = (UINT8) num; + mBufferLen++; + break; + } + else + { + sscanf (aid.substr(pos1, pos2-pos1+1).c_str(), "%x", &num); + mBuffer [mBufferLen] = (UINT8) num; + mBufferLen++; + pos1 = pos2 + 1; + pos2 = aid.find_first_of (delimiter, pos1); + } + } +} + + +/******************************************************************************* +** +** Function: ~AidBuffer +** +** Description: Release all resources. +** +** Returns: None. +** +*******************************************************************************/ +AidBuffer::~AidBuffer () +{ + delete [] mBuffer; +} + + +/*******************************************************************************/ +/*******************************************************************************/ + + +const char* RouteDataSet::sConfigFile = "/param/route.xml"; + + +/******************************************************************************* +** +** Function: ~RouteDataSet +** +** Description: Release all resources. +** +** Returns: None. +** +*******************************************************************************/ +RouteDataSet::~RouteDataSet () +{ + deleteDatabase (); +} + + +/******************************************************************************* +** +** Function: initialize +** +** Description: Initialize resources. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool RouteDataSet::initialize () +{ + static const char fn [] = "RouteDataSet::initialize"; + ALOGD ("%s: enter", fn); + //check that the libxml2 version in use is compatible + //with the version the software has been compiled with + LIBXML_TEST_VERSION + ALOGD ("%s: exit; return=true", fn); + return true; +} + + +/******************************************************************************* +** +** Function: deleteDatabase +** +** Description: Delete all routes stored in all databases. +** +** Returns: None. +** +*******************************************************************************/ +void RouteDataSet::deleteDatabase () +{ + static const char fn [] = "RouteDataSet::deleteDatabase"; + ALOGD ("%s: default db size=%u; sec elem db size=%u", fn, mDefaultRouteDatabase.size(), mSecElemRouteDatabase.size()); + Database::iterator it; + + for (it = mDefaultRouteDatabase.begin(); it != mDefaultRouteDatabase.end(); it++) + delete (*it); + mDefaultRouteDatabase.clear (); + + for (it = mSecElemRouteDatabase.begin(); it != mSecElemRouteDatabase.end(); it++) + delete (*it); + mSecElemRouteDatabase.clear (); +} + + +/******************************************************************************* +** +** Function: import +** +** Description: Import data from an XML file. Fill the databases. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool RouteDataSet::import () +{ + static const char fn [] = "RouteDataSet::import"; + ALOGD ("%s: enter", fn); + bool retval = false; + xmlDocPtr doc; + xmlNodePtr node1; + std::string strFilename(bcm_nfc_location); + strFilename += sConfigFile; + + deleteDatabase (); + + doc = xmlParseFile (strFilename.c_str()); + if (doc == NULL) + { + ALOGD ("%s: fail parse", fn); + goto TheEnd; + } + + node1 = xmlDocGetRootElement (doc); + if (node1 == NULL) + { + ALOGE ("%s: fail root element", fn); + goto TheEnd; + } + ALOGD ("%s: root=%s", fn, node1->name); + + node1 = node1->xmlChildrenNode; + while (node1) //loop through all elements in <Routes ... + { + if (xmlStrcmp(node1->name, (const xmlChar*) "Route")==0) + { + xmlChar* value = xmlGetProp (node1, (const xmlChar*) "Type"); + if (value && (xmlStrcmp (value, (const xmlChar*) "SecElemSelectedRoutes") == 0)) + { + ALOGD ("%s: found SecElemSelectedRoutes", fn); + xmlNodePtr node2 = node1->xmlChildrenNode; + while (node2) //loop all elements in <Route Type="SecElemSelectedRoutes" ... + { + if (xmlStrcmp(node2->name, (const xmlChar*) "Proto")==0) + importProtocolRoute (node2, mSecElemRouteDatabase); + else if (xmlStrcmp(node2->name, (const xmlChar*) "Tech")==0) + importTechnologyRoute (node2, mSecElemRouteDatabase); + node2 = node2->next; + } //loop all elements in <Route Type="SecElemSelectedRoutes" ... + } + else if (value && (xmlStrcmp (value, (const xmlChar*) "DefaultRoutes") == 0)) + { + ALOGD ("%s: found DefaultRoutes", fn); + xmlNodePtr node2 = node1->xmlChildrenNode; + while (node2) //loop all elements in <Route Type="DefaultRoutes" ... + { + if (xmlStrcmp(node2->name, (const xmlChar*) "Proto")==0) + importProtocolRoute (node2, mDefaultRouteDatabase); + else if (xmlStrcmp(node2->name, (const xmlChar*) "Tech")==0) + importTechnologyRoute (node2, mDefaultRouteDatabase); + node2 = node2->next; + } //loop all elements in <Route Type="DefaultRoutes" ... + } + if (value) + xmlFree (value); + } //check <Route ... + node1 = node1->next; + } //loop through all elements in <Routes ... + retval = true; + +TheEnd: + xmlFreeDoc (doc); + xmlCleanupParser (); + ALOGD ("%s: exit; return=%u", fn, retval); + return retval; +} + + +/******************************************************************************* +** +** Function: saveToFile +** +** Description: Save XML data from a string into a file. +** routesXml: XML that represents routes. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool RouteDataSet::saveToFile (const char* routesXml) +{ + static const char fn [] = "RouteDataSet::saveToFile"; + FILE* fh = NULL; + size_t actualWritten = 0; + bool retval = false; + std::string filename (bcm_nfc_location); + + filename.append (sConfigFile); + fh = fopen (filename.c_str (), "w"); + if (fh == NULL) + { + ALOGE ("%s: fail to open file", fn); + return false; + } + + actualWritten = fwrite (routesXml, sizeof(char), strlen(routesXml), fh); + retval = actualWritten == strlen(routesXml); + fclose (fh); + ALOGD ("%s: wrote %u bytes", fn, actualWritten); + if (retval == false) + ALOGE ("%s: error during write", fn); + + //set file permission to + //owner read, write; group read; other read + chmod (filename.c_str (), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + return retval; +} + + +/******************************************************************************* +** +** Function: loadFromFile +** +** Description: Load XML data from file into a string. +** routesXml: string to receive XML data. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool RouteDataSet::loadFromFile (std::string& routesXml) +{ + static const char fn [] = "RouteDataSet::loadFromFile"; + FILE* fh = NULL; + size_t actual = 0; + char buffer [1024]; + std::string filename (bcm_nfc_location); + + filename.append (sConfigFile); + fh = fopen (filename.c_str (), "r"); + if (fh == NULL) + { + ALOGD ("%s: fail to open file", fn); + return false; + } + + while (true) + { + actual = fread (buffer, sizeof(char), sizeof(buffer), fh); + if (actual == 0) + break; + routesXml.append (buffer, actual); + } + fclose (fh); + ALOGD ("%s: read %u bytes", fn, routesXml.length()); + return true; +} + + + + +/******************************************************************************* +** +** Function: importProtocolRoute +** +** Description: Parse data for protocol routes. +** element: XML node for one protocol route. +** database: store data in this database. +** +** Returns: None. +** +*******************************************************************************/ +void RouteDataSet::importProtocolRoute (xmlNodePtr& element, Database& database) +{ + static const char fn [] = "RouteDataSet::importProtocolRoute"; + const xmlChar* id = (const xmlChar*) "Id"; + const xmlChar* secElem = (const xmlChar*) "SecElem"; + const xmlChar* trueString = (const xmlChar*) "true"; + const xmlChar* switchOn = (const xmlChar*) "SwitchOn"; + const xmlChar* switchOff = (const xmlChar*) "SwitchOff"; + const xmlChar* batteryOff = (const xmlChar*) "BatteryOff"; + RouteDataForProtocol* data = new RouteDataForProtocol; + xmlChar* value = NULL; + + ALOGD_IF (sDebug, "%s: element=%s", fn, element->name); + value = xmlGetProp (element, id); + if (value) + { + if (xmlStrcmp (value, (const xmlChar*) "T1T") == 0) + data->mProtocol = NFA_PROTOCOL_MASK_T1T; + else if (xmlStrcmp (value, (const xmlChar*) "T2T") == 0) + data->mProtocol = NFA_PROTOCOL_MASK_T2T; + else if (xmlStrcmp (value, (const xmlChar*) "T3T") == 0) + data->mProtocol = NFA_PROTOCOL_MASK_T3T; + else if (xmlStrcmp (value, (const xmlChar*) "IsoDep") == 0) + data->mProtocol = NFA_PROTOCOL_MASK_ISO_DEP; + xmlFree (value); + ALOGD_IF (sDebug, "%s: %s=0x%X", fn, id, data->mProtocol); + } + + value = xmlGetProp (element, secElem); + if (value) + { + data->mNfaEeHandle = strtol ((char*) value, NULL, 16); + xmlFree (value); + data->mNfaEeHandle = data->mNfaEeHandle | NFA_HANDLE_GROUP_EE; + ALOGD_IF (sDebug, "%s: %s=0x%X", fn, secElem, data->mNfaEeHandle); + } + + value = xmlGetProp (element, switchOn); + if (value) + { + data->mSwitchOn = (xmlStrcmp (value, trueString) == 0); + xmlFree (value); + } + + value = xmlGetProp (element, switchOff); + if (value) + { + data->mSwitchOff = (xmlStrcmp (value, trueString) == 0); + xmlFree (value); + } + + value = xmlGetProp (element, batteryOff); + if (value) + { + data->mBatteryOff = (xmlStrcmp (value, trueString) == 0); + xmlFree (value); + } + database.push_back (data); +} + + +/******************************************************************************* +** +** Function: importTechnologyRoute +** +** Description: Parse data for technology routes. +** element: XML node for one technology route. +** database: store data in this database. +** +** Returns: None. +** +*******************************************************************************/ +void RouteDataSet::importTechnologyRoute (xmlNodePtr& element, Database& database) +{ + static const char fn [] = "RouteDataSet::importTechnologyRoute"; + const xmlChar* id = (const xmlChar*) "Id"; + const xmlChar* secElem = (const xmlChar*) "SecElem"; + const xmlChar* trueString = (const xmlChar*) "true"; + const xmlChar* switchOn = (const xmlChar*) "SwitchOn"; + const xmlChar* switchOff = (const xmlChar*) "SwitchOff"; + const xmlChar* batteryOff = (const xmlChar*) "BatteryOff"; + RouteDataForTechnology* data = new RouteDataForTechnology; + xmlChar* value = NULL; + + ALOGD_IF (sDebug, "%s: element=%s", fn, element->name); + value = xmlGetProp (element, id); + if (value) + { + if (xmlStrcmp (value, (const xmlChar*) "NfcA") == 0) + data->mTechnology = NFA_TECHNOLOGY_MASK_A; + else if (xmlStrcmp (value, (const xmlChar*) "NfcB") == 0) + data->mTechnology = NFA_TECHNOLOGY_MASK_B; + else if (xmlStrcmp (value, (const xmlChar*) "NfcF") == 0) + data->mTechnology = NFA_TECHNOLOGY_MASK_F; + xmlFree (value); + ALOGD_IF (sDebug, "%s: %s=0x%X", fn, id, data->mTechnology); + } + + value = xmlGetProp (element, secElem); + if (value) + { + data->mNfaEeHandle = strtol ((char*) value, NULL, 16); + xmlFree (value); + data->mNfaEeHandle = data->mNfaEeHandle | NFA_HANDLE_GROUP_EE; + ALOGD_IF (sDebug, "%s: %s=0x%X", fn, secElem, data->mNfaEeHandle); + } + + value = xmlGetProp (element, switchOn); + if (value) + { + data->mSwitchOn = (xmlStrcmp (value, trueString) == 0); + xmlFree (value); + } + + value = xmlGetProp (element, switchOff); + if (value) + { + data->mSwitchOff = (xmlStrcmp (value, trueString) == 0); + xmlFree (value); + } + + value = xmlGetProp (element, batteryOff); + if (value) + { + data->mBatteryOff = (xmlStrcmp (value, trueString) == 0); + xmlFree (value); + } + database.push_back (data); +} + + +/******************************************************************************* +** +** Function: deleteFile +** +** Description: Delete route data XML file. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool RouteDataSet::deleteFile () +{ + static const char fn [] = "RouteDataSet::deleteFile"; + std::string filename (bcm_nfc_location); + filename.append (sConfigFile); + int stat = remove (filename.c_str()); + ALOGD ("%s: exit %u", fn, stat==0); + return stat == 0; +} + + +/******************************************************************************* +** +** Function: getDatabase +** +** Description: Obtain a database of routing data. +** selection: which database. +** +** Returns: Pointer to database. +** +*******************************************************************************/ +RouteDataSet::Database* RouteDataSet::getDatabase (DatabaseSelection selection) +{ + switch (selection) + { + case DefaultRouteDatabase: + return &mDefaultRouteDatabase; + case SecElemRouteDatabase: + return &mSecElemRouteDatabase; + } + return NULL; +} + + +/******************************************************************************* +** +** Function: printDiagnostic +** +** Description: Print some diagnostic output. +** +** Returns: None. +** +*******************************************************************************/ +void RouteDataSet::printDiagnostic () +{ + static const char fn [] = "RouteDataSet::printDiagnostic"; + Database* db = getDatabase (DefaultRouteDatabase); + + ALOGD ("%s: default route database", fn); + for (Database::iterator iter = db->begin(); iter != db->end(); iter++) + { + RouteData* routeData = *iter; + switch (routeData->mRouteType) + { + case RouteData::ProtocolRoute: + { + RouteDataForProtocol* proto = (RouteDataForProtocol*) routeData; + ALOGD ("%s: ee h=0x%X; protocol=0x%X", fn, proto->mNfaEeHandle, proto->mProtocol); + } + break; + } + } + + ALOGD ("%s: sec elem route database", fn); + db = getDatabase (SecElemRouteDatabase); + for (Database::iterator iter2 = db->begin(); iter2 != db->end(); iter2++) + { + RouteData* routeData = *iter2; + switch (routeData->mRouteType) + { + case RouteData::ProtocolRoute: + { + RouteDataForProtocol* proto = (RouteDataForProtocol*) routeData; + ALOGD ("%s: ee h=0x%X; protocol=0x%X", fn, proto->mNfaEeHandle, proto->mProtocol); + } + break; + case RouteData::TechnologyRoute: + { + RouteDataForTechnology* tech = (RouteDataForTechnology*) routeData; + ALOGD ("%s: ee h=0x%X; technology=0x%X", fn, tech->mNfaEeHandle, tech->mTechnology); + } + break; + } + } +} diff --git a/nci/jni/RouteDataSet.h b/nci/jni/RouteDataSet.h new file mode 100644 index 0000000..d2a09b8 --- /dev/null +++ b/nci/jni/RouteDataSet.h @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Import and export general routing data using a XML file. + */ +#pragma once +#include "NfcJniUtil.h" +#include "nfa_api.h" +#include <libxml/parser.h> +#include <vector> +#include <string> + + +/***************************************************************************** +** +** Name: RouteData +** +** Description: Base class for every kind of route data. +** +*****************************************************************************/ +class RouteData +{ +public: + enum RouteType {ProtocolRoute, TechnologyRoute}; + RouteType mRouteType; + +protected: + RouteData (RouteType routeType) : mRouteType (routeType) + {} +}; + + + + +/***************************************************************************** +** +** Name: RouteDataForProtocol +** +** Description: Data for protocol routes. +** +*****************************************************************************/ +class RouteDataForProtocol : public RouteData +{ +public: + int mNfaEeHandle; //for example 0x4f3, 0x4f4 + bool mSwitchOn; + bool mSwitchOff; + bool mBatteryOff; + tNFA_PROTOCOL_MASK mProtocol; + + RouteDataForProtocol () : RouteData (ProtocolRoute), mNfaEeHandle (NFA_HANDLE_INVALID), + mSwitchOn (false), mSwitchOff (false), mBatteryOff (false), + mProtocol (0) + {} +}; + + +/***************************************************************************** +** +** Name: RouteDataForTechnology +** +** Description: Data for technology routes. +** +*****************************************************************************/ +class RouteDataForTechnology : public RouteData +{ +public: + int mNfaEeHandle; //for example 0x4f3, 0x4f4 + bool mSwitchOn; + bool mSwitchOff; + bool mBatteryOff; + tNFA_TECHNOLOGY_MASK mTechnology; + + RouteDataForTechnology () : RouteData (TechnologyRoute), mNfaEeHandle (NFA_HANDLE_INVALID), + mSwitchOn (false), mSwitchOff (false), mBatteryOff (false), + mTechnology (0) + {} +}; + + +/*****************************************************************************/ +/*****************************************************************************/ + + +/***************************************************************************** +** +** Name: AidBuffer +** +** Description: Buffer to store AID after converting a string of hex +** values to bytes. +** +*****************************************************************************/ +class AidBuffer +{ +public: + + /******************************************************************************* + ** + ** Function: AidBuffer + ** + ** Description: Parse a string of hex numbers. Store result in an array of + ** bytes. + ** aid: string of hex numbers. + ** + ** Returns: None. + ** + *******************************************************************************/ + AidBuffer (std::string& aid); + + + /******************************************************************************* + ** + ** Function: ~AidBuffer + ** + ** Description: Release all resources. + ** + ** Returns: None. + ** + *******************************************************************************/ + ~AidBuffer (); + + + UINT8* buffer () {return mBuffer;}; + int length () {return mBufferLen;}; + +private: + UINT8* mBuffer; + UINT32 mBufferLen; +}; + + +/*****************************************************************************/ +/*****************************************************************************/ + + +/***************************************************************************** +** +** Name: RouteDataSet +** +** Description: Import and export general routing data using a XML file. +** See /data/bcm/param/route.xml +** +*****************************************************************************/ +class RouteDataSet +{ +public: + typedef std::vector<RouteData*> Database; + enum DatabaseSelection {DefaultRouteDatabase, SecElemRouteDatabase}; + + + /******************************************************************************* + ** + ** Function: ~RouteDataSet + ** + ** Description: Release all resources. + ** + ** Returns: None. + ** + *******************************************************************************/ + ~RouteDataSet (); + + + /******************************************************************************* + ** + ** Function: initialize + ** + ** Description: Initialize resources. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool initialize (); + + + /******************************************************************************* + ** + ** Function: import + ** + ** Description: Import data from an XML file. Fill the database. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool import (); + + + /******************************************************************************* + ** + ** Function: getDatabase + ** + ** Description: Obtain a database of routing data. + ** selection: which database. + ** + ** Returns: Pointer to database. + ** + *******************************************************************************/ + Database* getDatabase (DatabaseSelection selection); + + + /******************************************************************************* + ** + ** Function: saveToFile + ** + ** Description: Save XML data from a string into a file. + ** routesXml: XML that represents routes. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + static bool saveToFile (const char* routesXml); + + + /******************************************************************************* + ** + ** Function: loadFromFile + ** + ** Description: Load XML data from file into a string. + ** routesXml: string to receive XML data. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + static bool loadFromFile (std::string& routesXml); + + + /******************************************************************************* + ** + ** Function: deleteFile + ** + ** Description: Delete route data XML file. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + static bool deleteFile (); + + /******************************************************************************* + ** + ** Function: printDiagnostic + ** + ** Description: Print some diagnostic output. + ** + ** Returns: None. + ** + *******************************************************************************/ + void printDiagnostic (); + +private: + Database mSecElemRouteDatabase; //routes when NFC service selects sec elem + Database mDefaultRouteDatabase; //routes when NFC service deselects sec elem + static const char* sConfigFile; + static const bool sDebug = false; + + + /******************************************************************************* + ** + ** Function: deleteDatabase + ** + ** Description: Delete all routes stored in all databases. + ** + ** Returns: None. + ** + *******************************************************************************/ + void deleteDatabase (); + + + /******************************************************************************* + ** + ** Function: importProtocolRoute + ** + ** Description: Parse data for protocol routes. + ** element: XML node for one protocol route. + ** database: store data in this database. + ** + ** Returns: None. + ** + *******************************************************************************/ + void importProtocolRoute (xmlNodePtr& element, Database& database); + + + /******************************************************************************* + ** + ** Function: importTechnologyRoute + ** + ** Description: Parse data for technology routes. + ** element: XML node for one technology route. + ** database: store data in this database. + ** + ** Returns: None. + ** + *******************************************************************************/ + void importTechnologyRoute (xmlNodePtr& element, Database& database); +}; + diff --git a/nci/jni/SecureElement.cpp b/nci/jni/SecureElement.cpp new file mode 100755 index 0000000..3c9256b --- /dev/null +++ b/nci/jni/SecureElement.cpp @@ -0,0 +1,2189 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Communicate with secure elements that are attached to the NFC + * controller. + */ +#include <semaphore.h> +#include <errno.h> +#include "OverrideLog.h" +#include "SecureElement.h" +#include "config.h" +#include "PowerSwitch.h" +#include "HostAidRouter.h" +#include "JavaClassConstants.h" + + +/***************************************************************************** +** +** public variables +** +*****************************************************************************/ +int gSEId = -1; // secure element ID to use in connectEE(), -1 means not set +int gGatePipe = -1; // gate id or static pipe id to use in connectEE(), -1 means not set +bool gUseStaticPipe = false; // if true, use gGatePipe as static pipe id. if false, use as gate id + +namespace android +{ + extern void startRfDiscovery (bool isStart); +} + +////////////////////////////////////////////// +////////////////////////////////////////////// + + +SecureElement SecureElement::sSecElem; +const char* SecureElement::APP_NAME = "nfc_jni"; + + +/******************************************************************************* +** +** Function: SecureElement +** +** Description: Initialize member variables. +** +** Returns: None +** +*******************************************************************************/ +SecureElement::SecureElement () +: mActiveEeHandle (NFA_HANDLE_INVALID), + mDestinationGate (4), //loopback gate + mNfaHciHandle (NFA_HANDLE_INVALID), + mNativeData (NULL), + mIsInit (false), + mActualNumEe (0), + mNumEePresent(0), + mbNewEE (true), // by default we start w/thinking there are new EE + mNewPipeId (0), + mNewSourceGate (0), + mActiveSeOverride(0), + mCommandStatus (NFA_STATUS_OK), + mIsPiping (false), + mCurrentRouteSelection (NoRoute), + mActualResponseSize(0), + mUseOberthurWarmReset (false), + mActivatedInListenMode (false), + mOberthurWarmResetCommand (3), + mRfFieldIsOn(false) +{ + memset (&mEeInfo, 0, sizeof(mEeInfo)); + memset (&mUiccInfo, 0, sizeof(mUiccInfo)); + memset (&mHciCfg, 0, sizeof(mHciCfg)); + memset (mResponseData, 0, sizeof(mResponseData)); + memset (mAidForEmptySelect, 0, sizeof(mAidForEmptySelect)); + memset (&mLastRfFieldToggle, 0, sizeof(mLastRfFieldToggle)); +} + + +/******************************************************************************* +** +** Function: ~SecureElement +** +** Description: Release all resources. +** +** Returns: None +** +*******************************************************************************/ +SecureElement::~SecureElement () +{ +} + + +/******************************************************************************* +** +** Function: getInstance +** +** Description: Get the SecureElement singleton object. +** +** Returns: SecureElement object. +** +*******************************************************************************/ +SecureElement& SecureElement::getInstance() +{ + return sSecElem; +} + + +/******************************************************************************* +** +** Function: setActiveSeOverride +** +** Description: Specify which secure element to turn on. +** activeSeOverride: ID of secure element +** +** Returns: None +** +*******************************************************************************/ +void SecureElement::setActiveSeOverride(UINT8 activeSeOverride) +{ + ALOGD ("SecureElement::setActiveSeOverride, seid=0x%X", activeSeOverride); + mActiveSeOverride = activeSeOverride; +} + + +/******************************************************************************* +** +** Function: initialize +** +** Description: Initialize all member variables. +** native: Native data. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool SecureElement::initialize (nfc_jni_native_data* native) +{ + static const char fn [] = "SecureElement::initialize"; + tNFA_STATUS nfaStat; + UINT8 xx = 0, yy = 0; + unsigned long num = 0; + + ALOGD ("%s: enter", fn); + + if (GetNumValue("NFA_HCI_DEFAULT_DEST_GATE", &num, sizeof(num))) + mDestinationGate = num; + ALOGD ("%s: Default destination gate: %d", fn, mDestinationGate); + + // active SE, if not set active all SEs + if (GetNumValue("ACTIVE_SE", &num, sizeof(num))) + mActiveSeOverride = num; + ALOGD ("%s: Active SE override: %d", fn, mActiveSeOverride); + + if (GetNumValue("OBERTHUR_WARM_RESET_COMMAND", &num, sizeof(num))) + { + mUseOberthurWarmReset = true; + mOberthurWarmResetCommand = (UINT8) num; + } + + mActiveEeHandle = NFA_HANDLE_INVALID; + mNfaHciHandle = NFA_HANDLE_INVALID; + + mNativeData = native; + mActualNumEe = MAX_NUM_EE; + mbNewEE = true; + mNewPipeId = 0; + mNewSourceGate = 0; + mCurrentRouteSelection = NoRoute; + memset (mEeInfo, 0, sizeof(mEeInfo)); + memset (&mUiccInfo, 0, sizeof(mUiccInfo)); + memset (&mHciCfg, 0, sizeof(mHciCfg)); + mUsedAids.clear (); + memset(mAidForEmptySelect, 0, sizeof(mAidForEmptySelect)); + + // Get Fresh EE info. + if (! getEeInfo()) + return (false); + + { + SyncEventGuard guard (mEeRegisterEvent); + ALOGD ("%s: try ee register", fn); + nfaStat = NFA_EeRegister (nfaEeCallback); + if (nfaStat != NFA_STATUS_OK) + { + ALOGE ("%s: fail ee register; error=0x%X", fn, nfaStat); + return (false); + } + mEeRegisterEvent.wait (); + } + + // If the controller has an HCI Network, register for that + for (xx = 0; xx < mActualNumEe; xx++) + { + if ((mEeInfo[xx].num_interface > 0) && (mEeInfo[xx].ee_interface[0] == NCI_NFCEE_INTERFACE_HCI_ACCESS) ) + { + ALOGD ("%s: Found HCI network, try hci register", fn); + + SyncEventGuard guard (mHciRegisterEvent); + + nfaStat = NFA_HciRegister (const_cast<char*>(APP_NAME), nfaHciCallback, TRUE); + if (nfaStat != NFA_STATUS_OK) + { + ALOGE ("%s: fail hci register; error=0x%X", fn, nfaStat); + return (false); + } + mHciRegisterEvent.wait(); + break; + } + } + + mRouteDataSet.initialize (); + mRouteDataSet.import (); //read XML file + HostAidRouter::getInstance().initialize (); + + GetStrValue(NAME_AID_FOR_EMPTY_SELECT, (char*)&mAidForEmptySelect[0], sizeof(mAidForEmptySelect)); + + mIsInit = true; + ALOGD ("%s: exit", fn); + return (true); +} + + +/******************************************************************************* +** +** Function: finalize +** +** Description: Release all resources. +** +** Returns: None +** +*******************************************************************************/ +void SecureElement::finalize () +{ + static const char fn [] = "SecureElement::finalize"; + ALOGD ("%s: enter", fn); + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + + NFA_EeDeregister (nfaEeCallback); + + if (mNfaHciHandle != NFA_HANDLE_INVALID) + NFA_HciDeregister (const_cast<char*>(APP_NAME)); + + mNfaHciHandle = NFA_HANDLE_INVALID; + mNativeData = NULL; + mIsInit = false; + mActualNumEe = 0; + mNumEePresent = 0; + mNewPipeId = 0; + mNewSourceGate = 0; + mIsPiping = false; + memset (mEeInfo, 0, sizeof(mEeInfo)); + memset (&mUiccInfo, 0, sizeof(mUiccInfo)); + + ALOGD ("%s: exit", fn); +} + + +/******************************************************************************* +** +** Function: getEeInfo +** +** Description: Get latest information about execution environments from stack. +** +** Returns: True if at least 1 EE is available. +** +*******************************************************************************/ +bool SecureElement::getEeInfo() +{ + static const char fn [] = "SecureElement::getEeInfo"; + ALOGD ("%s: enter; mbNewEE=%d, mActualNumEe=%d", fn, mbNewEE, mActualNumEe); + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + UINT8 xx = 0, yy = 0; + + // If mbNewEE is true then there is new EE info. + if (mbNewEE) + { + mActualNumEe = MAX_NUM_EE; + + if ((nfaStat = NFA_EeGetInfo (&mActualNumEe, mEeInfo)) != NFA_STATUS_OK) + { + ALOGE ("%s: fail get info; error=0x%X", fn, nfaStat); + mActualNumEe = 0; + } + else + { + mbNewEE = false; + + ALOGD ("%s: num EEs discovered: %u", fn, mActualNumEe); + if (mActualNumEe != 0) + { + for (UINT8 xx = 0; xx < mActualNumEe; xx++) + { + if ((mEeInfo[xx].num_interface != 0) && (mEeInfo[xx].ee_interface[0] != NCI_NFCEE_INTERFACE_HCI_ACCESS) ) + mNumEePresent++; + + ALOGD ("%s: EE[%u] Handle: 0x%04x Status: %s Num I/f: %u: (0x%02x, 0x%02x) Num TLVs: %u", + fn, xx, mEeInfo[xx].ee_handle, eeStatusToString(mEeInfo[xx].ee_status), mEeInfo[xx].num_interface, + mEeInfo[xx].ee_interface[0], mEeInfo[xx].ee_interface[1], mEeInfo[xx].num_tlvs); + + for (yy = 0; yy < mEeInfo[xx].num_tlvs; yy++) + { + ALOGD ("%s: EE[%u] TLV[%u] Tag: 0x%02x Len: %u Values[]: 0x%02x 0x%02x 0x%02x ...", + fn, xx, yy, mEeInfo[xx].ee_tlv[yy].tag, mEeInfo[xx].ee_tlv[yy].len, mEeInfo[xx].ee_tlv[yy].info[0], + mEeInfo[xx].ee_tlv[yy].info[1], mEeInfo[xx].ee_tlv[yy].info[2]); + } + } + } + } + } + ALOGD ("%s: exit; mActualNumEe=%d, mNumEePresent=%d", fn, mActualNumEe,mNumEePresent); + return (mActualNumEe != 0); +} + + +/******************************************************************************* +** +** Function TimeDiff +** +** Description Computes time difference in milliseconds. +** +** Returns Time difference in milliseconds +** +*******************************************************************************/ +static UINT32 TimeDiff(timespec start, timespec end) +{ + end.tv_sec -= start.tv_sec; + end.tv_nsec -= start.tv_nsec; + + if (end.tv_nsec < 0) { + end.tv_nsec += 10e8; + end.tv_sec -=1; + } + + return (end.tv_sec * 1000) + (end.tv_nsec / 10e5); +} + +/******************************************************************************* +** +** Function: isRfFieldOn +** +** Description: Can be used to determine if the SE is in an RF field +** +** Returns: True if the SE is activated in an RF field +** +*******************************************************************************/ +bool SecureElement::isRfFieldOn() { + AutoMutex mutex(mMutex); + if (mRfFieldIsOn) { + return true; + } + struct timespec now; + int ret = clock_gettime(CLOCK_MONOTONIC, &now); + if (ret == -1) { + ALOGE("isRfFieldOn(): clock_gettime failed"); + return false; + } + if (TimeDiff(mLastRfFieldToggle, now) < 50) { + // If it was less than 50ms ago that RF field + // was turned off, still return ON. + return true; + } else { + return false; + } +} + +/******************************************************************************* +** +** Function: isActivatedInListenMode +** +** Description: Can be used to determine if the SE is activated in listen mode +** +** Returns: True if the SE is activated in listen mode +** +*******************************************************************************/ +bool SecureElement::isActivatedInListenMode() { + return mActivatedInListenMode; +} + +/******************************************************************************* +** +** Function: getListOfEeHandles +** +** Description: Get the list of handles of all execution environments. +** e: Java Virtual Machine. +** +** Returns: List of handles of all execution environments. +** +*******************************************************************************/ +jintArray SecureElement::getListOfEeHandles (JNIEnv* e) +{ + static const char fn [] = "SecureElement::getListOfEeHandles"; + ALOGD ("%s: enter", fn); + if (mNumEePresent == 0) + return NULL; + jintArray list = NULL; + + if (!mIsInit) + { + ALOGE ("%s: not init", fn); + return (NULL); + } + + // Get Fresh EE info. + if (! getEeInfo()) + return (NULL); + + list = e->NewIntArray (mNumEePresent); //allocate array + jint jj = 0; + int cnt = 0; + for (int ii = 0; ii < mActualNumEe && cnt < mNumEePresent; ii++) + { + ALOGD ("%s: %u = 0x%X", fn, ii, mEeInfo[ii].ee_handle); + if ((mEeInfo[ii].num_interface == 0) || (mEeInfo[ii].ee_interface[0] == NCI_NFCEE_INTERFACE_HCI_ACCESS) ) + { + continue; + } + + jj = mEeInfo[ii].ee_handle & ~NFA_HANDLE_GROUP_EE; + e->SetIntArrayRegion (list, cnt++, 1, &jj); + } + //e->DeleteLocalRef (list); + + ALOGD("%s: exit", fn); + return list; +} + + +/******************************************************************************* +** +** Function: activate +** +** Description: Turn on the secure element. +** seID: ID of secure element. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool SecureElement::activate (jint seID) +{ + static const char fn [] = "SecureElement::activate"; + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + int numActivatedEe = 0; + + ALOGD ("%s: enter; seID=0x%X", fn, seID); + + if (!mIsInit) + { + ALOGE ("%s: not init", fn); + return false; + } + + if (mActiveEeHandle != NFA_HANDLE_INVALID) + { + ALOGD ("%s: already active", fn); + return true; + } + + // Get Fresh EE info if needed. + if (! getEeInfo()) + { + ALOGE ("%s: no EE info", fn); + return false; + } + + mActiveEeHandle = getDefaultEeHandle(); + ALOGD ("%s: active ee h=0x%X, override se=0x%X", fn, mActiveEeHandle, mActiveSeOverride); + if (mActiveEeHandle == NFA_HANDLE_INVALID) + { + ALOGE ("%s: ee not found", fn); + return false; + } + + UINT16 override_se = 0; + if (mActiveSeOverride) + override_se = NFA_HANDLE_GROUP_EE | mActiveSeOverride; + + if (mRfFieldIsOn) { + ALOGE("%s: RF field indication still on, resetting", fn); + mRfFieldIsOn = false; + } + + ALOGD ("%s: override seid=0x%X", fn, override_se ); + //activate every discovered secure element + for (int index=0; index < mActualNumEe; index++) + { + tNFA_EE_INFO& eeItem = mEeInfo[index]; + + if ((eeItem.ee_handle == EE_HANDLE_0xF3) || (eeItem.ee_handle == EE_HANDLE_0xF4)) + { + if (override_se && (override_se != eeItem.ee_handle) ) + continue; // do not enable all SEs; only the override one + + if (eeItem.ee_status != NFC_NFCEE_STATUS_INACTIVE) + { + ALOGD ("%s: h=0x%X already activated", fn, eeItem.ee_handle); + numActivatedEe++; + continue; + } + + { + SyncEventGuard guard (mEeSetModeEvent); + ALOGD ("%s: set EE mode activate; h=0x%X", fn, eeItem.ee_handle); + if ((nfaStat = NFA_EeModeSet (eeItem.ee_handle, NFA_EE_MD_ACTIVATE)) == NFA_STATUS_OK) + { + mEeSetModeEvent.wait (); //wait for NFA_EE_MODE_SET_EVT + if (eeItem.ee_status == NFC_NFCEE_STATUS_ACTIVE) + numActivatedEe++; + } + else + ALOGE ("%s: NFA_EeModeSet failed; error=0x%X", fn, nfaStat); + } + } + } //for + + for (UINT8 xx = 0; xx < mActualNumEe; xx++) + { + if ((mEeInfo[xx].num_interface != 0) && (mEeInfo[xx].ee_interface[0] != NCI_NFCEE_INTERFACE_HCI_ACCESS) && + (mEeInfo[xx].ee_status != NFC_NFCEE_STATUS_INACTIVE)) + { + mActiveEeHandle = mEeInfo[xx].ee_handle; + break; + } + } + + ALOGD ("%s: exit; ok=%u", fn, numActivatedEe > 0); + return numActivatedEe > 0; +} + + +/******************************************************************************* +** +** Function: deactivate +** +** Description: Turn off the secure element. +** seID: ID of secure element. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool SecureElement::deactivate (jint seID) +{ + static const char fn [] = "SecureElement::deactivate"; + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + int numDeactivatedEe = 0; + bool retval = false; + + ALOGD ("%s: enter; seID=0x%X, mActiveEeHandle=0x%X", fn, seID, mActiveEeHandle); + + if (!mIsInit) + { + ALOGE ("%s: not init", fn); + goto TheEnd; + } + + //if the controller is routing to sec elems or piping, + //then the secure element cannot be deactivated + if ((mCurrentRouteSelection == SecElemRoute) || mIsPiping) + { + ALOGE ("%s: still busy", fn); + goto TheEnd; + } + + if (mActiveEeHandle == NFA_HANDLE_INVALID) + { + ALOGE ("%s: invalid EE handle", fn); + goto TheEnd; + } + + mActiveEeHandle = NFA_HANDLE_INVALID; + retval = true; + +TheEnd: + ALOGD ("%s: exit; ok=%u", fn, retval); + return retval; +} + + +/******************************************************************************* +** +** Function: notifyTransactionListenersOfAid +** +** Description: Notify the NFC service about a transaction event from secure element. +** aid: Buffer contains application ID. +** aidLen: Length of application ID. +** +** Returns: None +** +*******************************************************************************/ +void SecureElement::notifyTransactionListenersOfAid (const UINT8* aidBuffer, UINT8 aidBufferLen) +{ + static const char fn [] = "SecureElement::notifyTransactionListenersOfAid"; + ALOGD ("%s: enter; aid len=%u", fn, aidBufferLen); + + if (aidBufferLen == 0) + return; + + jobject tlvJavaArray = NULL; + JNIEnv* e = NULL; + UINT8* tlv = 0; + const UINT16 tlvMaxLen = aidBufferLen + 10; + UINT16 tlvActualLen = 0; + bool stat = false; + + mNativeData->vm->AttachCurrentThread (&e, NULL); + if (e == NULL) + { + ALOGE ("%s: jni env is null", fn); + return; + } + + tlv = new UINT8 [tlvMaxLen]; + if (tlv == NULL) + { + ALOGE ("%s: fail allocate tlv", fn); + goto TheEnd; + } + + memcpy (tlv, aidBuffer, aidBufferLen); + tlvActualLen = aidBufferLen; + + tlvJavaArray = e->NewByteArray (tlvActualLen); + if (tlvJavaArray == NULL) + { + ALOGE ("%s: fail allocate array", fn); + goto TheEnd; + } + + e->SetByteArrayRegion ((jbyteArray)tlvJavaArray, 0, tlvActualLen, (jbyte *)tlv); + if (e->ExceptionCheck()) + { + e->ExceptionClear(); + ALOGE ("%s: fail fill array", fn); + goto TheEnd; + } + + e->CallVoidMethod (mNativeData->manager, android::gCachedNfcManagerNotifyTransactionListeners, tlvJavaArray); + if (e->ExceptionCheck()) + { + e->ExceptionClear(); + ALOGE ("%s: fail notify", fn); + goto TheEnd; + } + +TheEnd: + if (tlvJavaArray) + e->DeleteLocalRef (tlvJavaArray); + mNativeData->vm->DetachCurrentThread (); + delete [] tlv; + ALOGD ("%s: exit", fn); +} + + +/******************************************************************************* +** +** Function: connectEE +** +** Description: Connect to the execution environment. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool SecureElement::connectEE () +{ + static const char fn [] = "SecureElement::connectEE"; + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + bool retVal = false; + UINT8 destHost = 0; + unsigned long num = 0; + char pipeConfName[40]; + tNFA_HANDLE eeHandle = mActiveEeHandle; + + ALOGD ("%s: enter, mActiveEeHandle: 0x%04x, SEID: 0x%x, pipe_gate_num=%d, use pipe=%d", + fn, mActiveEeHandle, gSEId, gGatePipe, gUseStaticPipe); + + if (!mIsInit) + { + ALOGE ("%s: not init", fn); + return (false); + } + + if (gSEId != -1) + { + eeHandle = gSEId | NFA_HANDLE_GROUP_EE; + ALOGD ("%s: Using SEID: 0x%x", fn, eeHandle ); + } + + if (eeHandle == NFA_HANDLE_INVALID) + { + ALOGE ("%s: invalid handle 0x%X", fn, eeHandle); + return (false); + } + + tNFA_EE_INFO *pEE = findEeByHandle (eeHandle); + + if (pEE == NULL) + { + ALOGE ("%s: Handle 0x%04x NOT FOUND !!", fn, eeHandle); + return (false); + } + + // Disable RF discovery completely while the DH is connected + android::startRfDiscovery(false); + + mNewSourceGate = 0; + + if (gGatePipe == -1) + { + // pipe/gate num was not specifed by app, get from config file + mNewPipeId = 0; + + // Construct the PIPE name based on the EE handle (e.g. NFA_HCI_STATIC_PIPE_ID_F3 for UICC0). + snprintf (pipeConfName, sizeof(pipeConfName), "NFA_HCI_STATIC_PIPE_ID_%02X", eeHandle & NFA_HANDLE_MASK); + + if (GetNumValue(pipeConfName, &num, sizeof(num)) && (num != 0)) + { + mNewPipeId = num; + ALOGD ("%s: Using static pipe id: 0x%X", __FUNCTION__, mNewPipeId); + } + else + { + ALOGD ("%s: Did not find value '%s' defined in the .conf", __FUNCTION__, pipeConfName); + } + } + else + { + if (gUseStaticPipe) + { + mNewPipeId = gGatePipe; + } + else + { + mNewPipeId = 0; + mDestinationGate= gGatePipe; + } + } + + // If the .conf file had a static pipe to use, just use it. + if (mNewPipeId != 0) + { + UINT8 host = (mNewPipeId == STATIC_PIPE_0x70) ? 0x02 : 0x03; + UINT8 gate = (mNewPipeId == STATIC_PIPE_0x70) ? 0xF0 : 0xF1; + nfaStat = NFA_HciAddStaticPipe(mNfaHciHandle, host, gate, mNewPipeId); + if (nfaStat != NFA_STATUS_OK) + { + ALOGE ("%s: fail create static pipe; error=0x%X", fn, nfaStat); + retVal = false; + goto TheEnd; + } + } + else + { + if ( (pEE->num_tlvs >= 1) && (pEE->ee_tlv[0].tag == NFA_EE_TAG_HCI_HOST_ID) ) + destHost = pEE->ee_tlv[0].info[0]; + else + destHost = 2; + + // Get a list of existing gates and pipes + { + ALOGD ("%s: get gate, pipe list", fn); + SyncEventGuard guard (mPipeListEvent); + nfaStat = NFA_HciGetGateAndPipeList (mNfaHciHandle); + if (nfaStat == NFA_STATUS_OK) + { + mPipeListEvent.wait(); + if (mHciCfg.status == NFA_STATUS_OK) + { + for (UINT8 xx = 0; xx < mHciCfg.num_pipes; xx++) + { + if ( (mHciCfg.pipe[xx].dest_host == destHost) + && (mHciCfg.pipe[xx].dest_gate == mDestinationGate) ) + { + mNewSourceGate = mHciCfg.pipe[xx].local_gate; + mNewPipeId = mHciCfg.pipe[xx].pipe_id; + + ALOGD ("%s: found configured gate: 0x%02x pipe: 0x%02x", fn, mNewSourceGate, mNewPipeId); + break; + } + } + } + } + } + + if (mNewSourceGate == 0) + { + ALOGD ("%s: allocate gate", fn); + //allocate a source gate and store in mNewSourceGate + SyncEventGuard guard (mAllocateGateEvent); + if ((nfaStat = NFA_HciAllocGate (mNfaHciHandle)) != NFA_STATUS_OK) + { + ALOGE ("%s: fail allocate source gate; error=0x%X", fn, nfaStat); + goto TheEnd; + } + mAllocateGateEvent.wait (); + if (mCommandStatus != NFA_STATUS_OK) + goto TheEnd; + } + + if (mNewPipeId == 0) + { + ALOGD ("%s: create pipe", fn); + SyncEventGuard guard (mCreatePipeEvent); + nfaStat = NFA_HciCreatePipe (mNfaHciHandle, mNewSourceGate, destHost, mDestinationGate); + if (nfaStat != NFA_STATUS_OK) + { + ALOGE ("%s: fail create pipe; error=0x%X", fn, nfaStat); + goto TheEnd; + } + mCreatePipeEvent.wait (); + if (mCommandStatus != NFA_STATUS_OK) + goto TheEnd; + } + + { + ALOGD ("%s: open pipe", fn); + SyncEventGuard guard (mPipeOpenedEvent); + nfaStat = NFA_HciOpenPipe (mNfaHciHandle, mNewPipeId); + if (nfaStat != NFA_STATUS_OK) + { + ALOGE ("%s: fail open pipe; error=0x%X", fn, nfaStat); + goto TheEnd; + } + mPipeOpenedEvent.wait (); + if (mCommandStatus != NFA_STATUS_OK) + goto TheEnd; + } + } + + retVal = true; + +TheEnd: + mIsPiping = retVal; + if (!retVal) + { + // if open failed we need to de-allocate the gate + disconnectEE(0); + } + + ALOGD ("%s: exit; ok=%u", fn, retVal); + return retVal; +} + + +/******************************************************************************* +** +** Function: disconnectEE +** +** Description: Disconnect from the execution environment. +** seID: ID of secure element. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool SecureElement::disconnectEE (jint seID) +{ + static const char fn [] = "SecureElement::disconnectEE"; + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + tNFA_HANDLE eeHandle = seID; + + ALOGD("%s: seID=0x%X; handle=0x%04x", fn, seID, eeHandle); + + if (mUseOberthurWarmReset) + { + //send warm-reset command to Oberthur secure element which deselects the applet; + //this is an Oberthur-specific command; + ALOGD("%s: try warm-reset on pipe id 0x%X; cmd=0x%X", fn, mNewPipeId, mOberthurWarmResetCommand); + SyncEventGuard guard (mRegistryEvent); + nfaStat = NFA_HciSetRegistry (mNfaHciHandle, mNewPipeId, + 1, 1, &mOberthurWarmResetCommand); + if (nfaStat == NFA_STATUS_OK) + { + mRegistryEvent.wait (); + ALOGD("%s: completed warm-reset on pipe 0x%X", fn, mNewPipeId); + } + } + + if (mNewSourceGate) + { + SyncEventGuard guard (mDeallocateGateEvent); + if ((nfaStat = NFA_HciDeallocGate (mNfaHciHandle, mNewSourceGate)) == NFA_STATUS_OK) + mDeallocateGateEvent.wait (); + else + ALOGE ("%s: fail dealloc gate; error=0x%X", fn, nfaStat); + } + mIsPiping = false; + // Re-enable RF discovery + // Note that it only effactuates the current configuration, + // so if polling/listening were configured OFF (forex because + // the screen was off), they will stay OFF with this call. + android::startRfDiscovery(true); + return true; +} + + +/******************************************************************************* +** +** Function: transceive +** +** Description: Send data to the secure element; read it's response. +** xmitBuffer: Data to transmit. +** xmitBufferSize: Length of data. +** recvBuffer: Buffer to receive response. +** recvBufferMaxSize: Maximum size of buffer. +** recvBufferActualSize: Actual length of response. +** timeoutMillisec: timeout in millisecond. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool SecureElement::transceive (UINT8* xmitBuffer, INT32 xmitBufferSize, UINT8* recvBuffer, + INT32 recvBufferMaxSize, INT32& recvBufferActualSize, INT32 timeoutMillisec) +{ + static const char fn [] = "SecureElement::transceive"; + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + bool isSuccess = false; + bool waitOk = false; + UINT8 newSelectCmd[NCI_MAX_AID_LEN + 10]; + + ALOGD ("%s: enter; xmitBufferSize=%ld; recvBufferMaxSize=%ld; timeout=%ld", fn, xmitBufferSize, recvBufferMaxSize, timeoutMillisec); + + // Check if we need to replace an "empty" SELECT command. + // 1. Has there been a AID configured, and + // 2. Is that AID a valid length (i.e 16 bytes max), and + // 3. Is the APDU at least 4 bytes (for header), and + // 4. Is INS == 0xA4 (SELECT command), and + // 5. Is P1 == 0x04 (SELECT by AID), and + // 6. Is the APDU len 4 or 5 bytes. + // + // Note, the length of the configured AID is in the first + // byte, and AID starts from the 2nd byte. + if (mAidForEmptySelect[0] // 1 + && (mAidForEmptySelect[0] <= NCI_MAX_AID_LEN) // 2 + && (xmitBufferSize >= 4) // 3 + && (xmitBuffer[1] == 0xA4) // 4 + && (xmitBuffer[2] == 0x04) // 5 + && (xmitBufferSize <= 5)) // 6 + { + UINT8 idx = 0; + + // Copy APDU command header from the input buffer. + memcpy(&newSelectCmd[0], &xmitBuffer[0], 4); + idx = 4; + + // Set the Lc value to length of the new AID + newSelectCmd[idx++] = mAidForEmptySelect[0]; + + // Copy the AID + memcpy(&newSelectCmd[idx], &mAidForEmptySelect[1], mAidForEmptySelect[0]); + idx += mAidForEmptySelect[0]; + + // If there is an Le (5th byte of APDU), add it to the end. + if (xmitBufferSize == 5) + newSelectCmd[idx++] = xmitBuffer[4]; + + // Point to the new APDU + xmitBuffer = &newSelectCmd[0]; + xmitBufferSize = idx; + + ALOGD ("%s: Empty AID SELECT cmd detected, substituting AID from config file, new length=%d", fn, idx); + } + + { + SyncEventGuard guard (mTransceiveEvent); + mActualResponseSize = 0; + memset (mResponseData, 0, sizeof(mResponseData)); + if ((mNewPipeId == STATIC_PIPE_0x70) || (mNewPipeId == STATIC_PIPE_0x71)) + nfaStat = NFA_HciSendEvent (mNfaHciHandle, mNewPipeId, EVT_SEND_DATA, xmitBufferSize, xmitBuffer, sizeof(mResponseData), mResponseData, 0); + else + nfaStat = NFA_HciSendEvent (mNfaHciHandle, mNewPipeId, NFA_HCI_EVT_POST_DATA, xmitBufferSize, xmitBuffer, sizeof(mResponseData), mResponseData, 0); + + if (nfaStat == NFA_STATUS_OK) + { + waitOk = mTransceiveEvent.wait (timeoutMillisec); + if (waitOk == false) //timeout occurs + { + ALOGE ("%s: wait response timeout", fn); + goto TheEnd; + } + } + else + { + ALOGE ("%s: fail send data; error=0x%X", fn, nfaStat); + goto TheEnd; + } + } + + if (mActualResponseSize > recvBufferMaxSize) + recvBufferActualSize = recvBufferMaxSize; + else + recvBufferActualSize = mActualResponseSize; + + memcpy (recvBuffer, mResponseData, recvBufferActualSize); + isSuccess = true; + +TheEnd: + ALOGD ("%s: exit; isSuccess: %d; recvBufferActualSize: %ld", fn, isSuccess, recvBufferActualSize); + return (isSuccess); +} + + +/******************************************************************************* +** +** Function: notifyListenModeState +** +** Description: Notify the NFC service about whether the SE was activated +** in listen mode. +** isActive: Whether the secure element is activated. +** +** Returns: None +** +*******************************************************************************/ +void SecureElement::notifyListenModeState (bool isActivated) { + static const char fn [] = "SecureElement::notifyListenMode"; + JNIEnv *e = NULL; + + ALOGD ("%s: enter; listen mode active=%u", fn, isActivated); + mNativeData->vm->AttachCurrentThread (&e, NULL); + + if (e == NULL) + { + ALOGE ("%s: jni env is null", fn); + return; + } + + mActivatedInListenMode = isActivated; + if (isActivated) { + e->CallVoidMethod (mNativeData->manager, android::gCachedNfcManagerNotifySeListenActivated); + } + else { + e->CallVoidMethod (mNativeData->manager, android::gCachedNfcManagerNotifySeListenDeactivated); + } + + if (e->ExceptionCheck()) + { + e->ExceptionClear(); + ALOGE ("%s: fail notify", fn); + } + + mNativeData->vm->DetachCurrentThread (); + ALOGD ("%s: exit", fn); +} + +/******************************************************************************* +** +** Function: notifyRfFieldEvent +** +** Description: Notify the NFC service about RF field events from the stack. +** isActive: Whether any secure element is activated. +** +** Returns: None +** +*******************************************************************************/ +void SecureElement::notifyRfFieldEvent (bool isActive) +{ + static const char fn [] = "SecureElement::notifyRfFieldEvent"; + JNIEnv *e = NULL; + + ALOGD ("%s: enter; is active=%u", fn, isActive); + mNativeData->vm->AttachCurrentThread (&e, NULL); + + if (e == NULL) + { + ALOGE ("%s: jni env is null", fn); + return; + } + + mMutex.lock(); + int ret = clock_gettime (CLOCK_MONOTONIC, &mLastRfFieldToggle); + if (ret == -1) { + ALOGE("%s: clock_gettime failed", fn); + // There is no good choice here... + } + if (isActive) { + mRfFieldIsOn = true; + e->CallVoidMethod (mNativeData->manager, android::gCachedNfcManagerNotifySeFieldActivated); + } + else { + mRfFieldIsOn = false; + e->CallVoidMethod (mNativeData->manager, android::gCachedNfcManagerNotifySeFieldDeactivated); + } + mMutex.unlock(); + + if (e->ExceptionCheck()) + { + e->ExceptionClear(); + ALOGE ("%s: fail notify", fn); + } + mNativeData->vm->DetachCurrentThread (); + ALOGD ("%s: exit", fn); +} + + +/******************************************************************************* +** +** Function: storeUiccInfo +** +** Description: Store a copy of the execution environment information from the stack. +** info: execution environment information. +** +** Returns: None +** +*******************************************************************************/ +void SecureElement::storeUiccInfo (tNFA_EE_DISCOVER_REQ& info) +{ + static const char fn [] = "SecureElement::storeUiccInfo"; + ALOGD ("%s: Status: %u Num EE: %u", fn, info.status, info.num_ee); + + SyncEventGuard guard (mUiccInfoEvent); + memcpy (&mUiccInfo, &info, sizeof(mUiccInfo)); + for (UINT8 xx = 0; xx < info.num_ee; xx++) + { + //for each technology (A, B, F, B'), print the bit field that shows + //what protocol(s) is support by that technology + ALOGD ("%s EE[%u] Handle: 0x%04x techA: 0x%02x techB: 0x%02x techF: 0x%02x techBprime: 0x%02x", + fn, xx, info.ee_disc_info[xx].ee_handle, + info.ee_disc_info[xx].la_protocol, + info.ee_disc_info[xx].lb_protocol, + info.ee_disc_info[xx].lf_protocol, + info.ee_disc_info[xx].lbp_protocol); + } + mUiccInfoEvent.notifyOne (); +} + + +/******************************************************************************* +** +** Function: getUiccId +** +** Description: Get the ID of the secure element. +** eeHandle: Handle to the secure element. +** uid: Array to receive the ID. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool SecureElement::getUiccId (tNFA_HANDLE eeHandle, jbyteArray& uid) +{ + static const char fn [] = "SecureElement::getUiccId"; + ALOGD ("%s: ee h=0x%X", fn, eeHandle); + bool retval = false; + JNIEnv* e = NULL; + + mNativeData->vm->AttachCurrentThread (&e, NULL); + if (e == NULL) + { + ALOGE ("%s: jni env is null", fn); + return false; + } + + findUiccByHandle (eeHandle); + //cannot get UID from the stack; nothing to do + +TheEnd: + mNativeData->vm->DetachCurrentThread (); + ALOGD ("%s: exit; ret=%u", fn, retval); + return retval; +} + + +/******************************************************************************* +** +** Function: getTechnologyList +** +** Description: Get all the technologies supported by a secure element. +** eeHandle: Handle of secure element. +** techList: List to receive the technologies. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool SecureElement::getTechnologyList (tNFA_HANDLE eeHandle, jintArray& techList) +{ + static const char fn [] = "SecureElement::getTechnologyList"; + ALOGD ("%s: ee h=0x%X", fn, eeHandle); + bool retval = false; + JNIEnv* e = NULL; + jint theList = 0; + + mNativeData->vm->AttachCurrentThread (&e, NULL); + if (e == NULL) + { + ALOGE ("%s: jni env is null", fn); + return false; + } + + tNFA_EE_DISCOVER_INFO *pUICC = findUiccByHandle (eeHandle); + + if (pUICC->la_protocol != 0) + theList = TARGET_TYPE_ISO14443_3A; + else if (pUICC->lb_protocol != 0) + theList = TARGET_TYPE_ISO14443_3B; + else if (pUICC->lf_protocol != 0) + theList = TARGET_TYPE_FELICA; + else if (pUICC->lbp_protocol != 0) + theList = TARGET_TYPE_ISO14443_3B; + else + theList = TARGET_TYPE_UNKNOWN; + +TheEnd: + mNativeData->vm->DetachCurrentThread (); + ALOGD ("%s: exit; ret=%u", fn, retval); + return retval; +} + + +/******************************************************************************* +** +** Function: adjustRoutes +** +** Description: Adjust routes in the controller's listen-mode routing table. +** selection: which set of routes to configure the controller. +** +** Returns: None +** +*******************************************************************************/ +void SecureElement::adjustRoutes (RouteSelection selection) +{ + static const char fn [] = "SecureElement::adjustRoutes"; + ALOGD ("%s: enter; selection=%u", fn, selection); + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + RouteDataSet::Database* db = mRouteDataSet.getDatabase (RouteDataSet::DefaultRouteDatabase); + + if (selection == SecElemRoute) + db = mRouteDataSet.getDatabase (RouteDataSet::SecElemRouteDatabase); + + mCurrentRouteSelection = selection; + adjustProtocolRoutes (db, selection); + adjustTechnologyRoutes (db, selection); + HostAidRouter::getInstance ().deleteAllRoutes (); //stop all AID routes to host + + if (db->empty()) + { + ALOGD ("%s: no route configuration", fn); + goto TheEnd; + } + + +TheEnd: + NFA_EeUpdateNow (); //apply new routes now + ALOGD ("%s: exit", fn); +} + + +/******************************************************************************* +** +** Function: applyRoutes +** +** Description: Read route data from file and apply them again. +** +** Returns: None +** +*******************************************************************************/ +void SecureElement::applyRoutes () +{ + static const char fn [] = "SecureElement::applyRoutes"; + ALOGD ("%s: enter", fn); + if (mCurrentRouteSelection != NoRoute) + { + mRouteDataSet.import (); //read XML file + adjustRoutes (mCurrentRouteSelection); + } + ALOGD ("%s: exit", fn); +} + + +/******************************************************************************* +** +** Function: adjustProtocolRoutes +** +** Description: Adjust default routing based on protocol in NFC listen mode. +** isRouteToEe: Whether routing to EE (true) or host (false). +** +** Returns: None +** +*******************************************************************************/ +void SecureElement::adjustProtocolRoutes (RouteDataSet::Database* db, RouteSelection routeSelection) +{ + static const char fn [] = "SecureElement::adjustProtocolRoutes"; + ALOGD ("%s: enter", fn); + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + const tNFA_PROTOCOL_MASK protoMask = NFA_PROTOCOL_MASK_ISO_DEP; + + /////////////////////// + // delete route to host + /////////////////////// + { + ALOGD ("%s: delete route to host", fn); + SyncEventGuard guard (mRoutingEvent); + if ((nfaStat = NFA_EeSetDefaultProtoRouting (NFA_EE_HANDLE_DH, 0, 0, 0)) == NFA_STATUS_OK) + mRoutingEvent.wait (); + else + ALOGE ("%s: fail delete route to host; error=0x%X", fn, nfaStat); + } + + /////////////////////// + // delete route to every sec elem + /////////////////////// + for (int i=0; i < mActualNumEe; i++) + { + if ((mEeInfo[i].num_interface != 0) && + (mEeInfo[i].ee_interface[0] != NFC_NFCEE_INTERFACE_HCI_ACCESS) && + (mEeInfo[i].ee_status == NFA_EE_STATUS_ACTIVE)) + { + ALOGD ("%s: delete route to EE h=0x%X", fn, mEeInfo[i].ee_handle); + SyncEventGuard guard (mRoutingEvent); + if ((nfaStat = NFA_EeSetDefaultProtoRouting (mEeInfo[i].ee_handle, 0, 0, 0)) == NFA_STATUS_OK) + mRoutingEvent.wait (); + else + ALOGE ("%s: fail delete route to EE; error=0x%X", fn, nfaStat); + } + } + + ////////////////////// + // configure route for every discovered sec elem + ////////////////////// + for (int i=0; i < mActualNumEe; i++) + { + //if sec elem is active + if ((mEeInfo[i].num_interface != 0) && + (mEeInfo[i].ee_interface[0] != NFC_NFCEE_INTERFACE_HCI_ACCESS) && + (mEeInfo[i].ee_status == NFA_EE_STATUS_ACTIVE)) + { + tNFA_PROTOCOL_MASK protocolsSwitchOn = 0; //all protocols that are active at full power + tNFA_PROTOCOL_MASK protocolsSwitchOff = 0; //all protocols that are active when phone is turned off + tNFA_PROTOCOL_MASK protocolsBatteryOff = 0; //all protocols that are active when there is no power + + //for every route in XML, look for protocol route; + //collect every protocol according to it's desired power mode + for (RouteDataSet::Database::iterator iter = db->begin(); iter != db->end(); iter++) + { + RouteData* routeData = *iter; + RouteDataForProtocol* route = NULL; + if (routeData->mRouteType != RouteData::ProtocolRoute) + continue; //skip other kinds of routing data + route = (RouteDataForProtocol*) (*iter); + if (route->mNfaEeHandle == mEeInfo[i].ee_handle) + { + if (route->mSwitchOn) + protocolsSwitchOn |= route->mProtocol; + if (route->mSwitchOff) + protocolsSwitchOff |= route->mProtocol; + if (route->mBatteryOff) + protocolsBatteryOff |= route->mProtocol; + } + } + + if (protocolsSwitchOn | protocolsSwitchOff | protocolsBatteryOff) + { + ALOGD ("%s: route to EE h=0x%X", fn, mEeInfo[i].ee_handle); + SyncEventGuard guard (mRoutingEvent); + nfaStat = NFA_EeSetDefaultProtoRouting (mEeInfo[i].ee_handle, + protocolsSwitchOn, protocolsSwitchOff, protocolsBatteryOff); + if (nfaStat == NFA_STATUS_OK) + mRoutingEvent.wait (); + else + ALOGE ("%s: fail route to EE; error=0x%X", fn, nfaStat); + } + } //if sec elem is active + } //for every discovered sec elem + + ////////////////////// + // configure route to host + ////////////////////// + { + tNFA_PROTOCOL_MASK protocolsSwitchOn = 0; //all protocols that are active at full power + tNFA_PROTOCOL_MASK protocolsSwitchOff = 0; //all protocols that are active when phone is turned off + tNFA_PROTOCOL_MASK protocolsBatteryOff = 0; //all protocols that are active when there is no power + + //for every route in XML, look for protocol route; + //collect every protocol according to it's desired power mode + for (RouteDataSet::Database::iterator iter = db->begin(); iter != db->end(); iter++) + { + RouteData* routeData = *iter; + RouteDataForProtocol* route = NULL; + if (routeData->mRouteType != RouteData::ProtocolRoute) + continue; //skip other kinds of routing data + route = (RouteDataForProtocol*) (*iter); + if (route->mNfaEeHandle == NFA_EE_HANDLE_DH) + { + if (route->mSwitchOn) + protocolsSwitchOn |= route->mProtocol; + if (route->mSwitchOff) + protocolsSwitchOff |= route->mProtocol; + if (route->mBatteryOff) + protocolsBatteryOff |= route->mProtocol; + } + } + + if (protocolsSwitchOn | protocolsSwitchOff | protocolsBatteryOff) + { + ALOGD ("%s: route to EE h=0x%X", fn, NFA_EE_HANDLE_DH); + SyncEventGuard guard (mRoutingEvent); + nfaStat = NFA_EeSetDefaultProtoRouting (NFA_EE_HANDLE_DH, + protocolsSwitchOn, protocolsSwitchOff, protocolsBatteryOff); + if (nfaStat == NFA_STATUS_OK) + mRoutingEvent.wait (); + else + ALOGE ("%s: fail route to EE; error=0x%X", fn, nfaStat); + } + } + + ////////////////////// + // if route database is empty, setup a default route + ////////////////////// + if (db->empty()) + { + tNFA_HANDLE eeHandle = NFA_EE_HANDLE_DH; + if (routeSelection == SecElemRoute) + eeHandle = getDefaultEeHandle (); + ALOGD ("%s: route to default EE h=0x%X", fn, eeHandle); + SyncEventGuard guard (mRoutingEvent); + nfaStat = NFA_EeSetDefaultProtoRouting (eeHandle, protoMask, 0, 0); + if (nfaStat == NFA_STATUS_OK) + mRoutingEvent.wait (); + else + ALOGE ("%s: fail route to EE; error=0x%X", fn, nfaStat); + } + ALOGD ("%s: exit", fn); +} + + +/******************************************************************************* +** +** Function: adjustTechnologyRoutes +** +** Description: Adjust default routing based on technology in NFC listen mode. +** isRouteToEe: Whether routing to EE (true) or host (false). +** +** Returns: None +** +*******************************************************************************/ +void SecureElement::adjustTechnologyRoutes (RouteDataSet::Database* db, RouteSelection routeSelection) +{ + static const char fn [] = "SecureElement::adjustTechnologyRoutes"; + ALOGD ("%s: enter", fn); + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + const tNFA_TECHNOLOGY_MASK techMask = NFA_TECHNOLOGY_MASK_A | NFA_TECHNOLOGY_MASK_B; + + /////////////////////// + // delete route to host + /////////////////////// + { + ALOGD ("%s: delete route to host", fn); + SyncEventGuard guard (mRoutingEvent); + if ((nfaStat = NFA_EeSetDefaultTechRouting (NFA_EE_HANDLE_DH, 0, 0, 0)) == NFA_STATUS_OK) + mRoutingEvent.wait (); + else + ALOGE ("%s: fail delete route to host; error=0x%X", fn, nfaStat); + } + + /////////////////////// + // delete route to every sec elem + /////////////////////// + for (int i=0; i < mActualNumEe; i++) + { + if ((mEeInfo[i].num_interface != 0) && + (mEeInfo[i].ee_interface[0] != NFC_NFCEE_INTERFACE_HCI_ACCESS) && + (mEeInfo[i].ee_status == NFA_EE_STATUS_ACTIVE)) + { + ALOGD ("%s: delete route to EE h=0x%X", fn, mEeInfo[i].ee_handle); + SyncEventGuard guard (mRoutingEvent); + if ((nfaStat = NFA_EeSetDefaultTechRouting (mEeInfo[i].ee_handle, 0, 0, 0)) == NFA_STATUS_OK) + mRoutingEvent.wait (); + else + ALOGE ("%s: fail delete route to EE; error=0x%X", fn, nfaStat); + } + } + + ////////////////////// + // configure route for every discovered sec elem + ////////////////////// + for (int i=0; i < mActualNumEe; i++) + { + //if sec elem is active + if ((mEeInfo[i].num_interface != 0) && + (mEeInfo[i].ee_interface[0] != NFC_NFCEE_INTERFACE_HCI_ACCESS) && + (mEeInfo[i].ee_status == NFA_EE_STATUS_ACTIVE)) + { + tNFA_TECHNOLOGY_MASK techsSwitchOn = 0; //all techs that are active at full power + tNFA_TECHNOLOGY_MASK techsSwitchOff = 0; //all techs that are active when phone is turned off + tNFA_TECHNOLOGY_MASK techsBatteryOff = 0; //all techs that are active when there is no power + + //for every route in XML, look for tech route; + //collect every tech according to it's desired power mode + for (RouteDataSet::Database::iterator iter = db->begin(); iter != db->end(); iter++) + { + RouteData* routeData = *iter; + RouteDataForTechnology* route = NULL; + if (routeData->mRouteType != RouteData::TechnologyRoute) + continue; //skip other kinds of routing data + route = (RouteDataForTechnology*) (*iter); + if (route->mNfaEeHandle == mEeInfo[i].ee_handle) + { + if (route->mSwitchOn) + techsSwitchOn |= route->mTechnology; + if (route->mSwitchOff) + techsSwitchOff |= route->mTechnology; + if (route->mBatteryOff) + techsBatteryOff |= route->mTechnology; + } + } + + if (techsSwitchOn | techsSwitchOff | techsBatteryOff) + { + ALOGD ("%s: route to EE h=0x%X", fn, mEeInfo[i].ee_handle); + SyncEventGuard guard (mRoutingEvent); + nfaStat = NFA_EeSetDefaultTechRouting (mEeInfo[i].ee_handle, + techsSwitchOn, techsSwitchOff, techsBatteryOff); + if (nfaStat == NFA_STATUS_OK) + mRoutingEvent.wait (); + else + ALOGE ("%s: fail route to EE; error=0x%X", fn, nfaStat); + } + } //if sec elem is active + } //for every discovered sec elem + + ////////////////////// + // configure route to host + ////////////////////// + { + tNFA_TECHNOLOGY_MASK techsSwitchOn = 0; //all techs that are active at full power + tNFA_TECHNOLOGY_MASK techsSwitchOff = 0; //all techs that are active when phone is turned off + tNFA_TECHNOLOGY_MASK techsBatteryOff = 0; //all techs that are active when there is no power + + //for every route in XML, look for protocol route; + //collect every protocol according to it's desired power mode + for (RouteDataSet::Database::iterator iter = db->begin(); iter != db->end(); iter++) + { + RouteData* routeData = *iter; + RouteDataForTechnology * route = NULL; + if (routeData->mRouteType != RouteData::TechnologyRoute) + continue; //skip other kinds of routing data + route = (RouteDataForTechnology*) (*iter); + if (route->mNfaEeHandle == NFA_EE_HANDLE_DH) + { + if (route->mSwitchOn) + techsSwitchOn |= route->mTechnology; + if (route->mSwitchOff) + techsSwitchOff |= route->mTechnology; + if (route->mBatteryOff) + techsBatteryOff |= route->mTechnology; + } + } + + if (techsSwitchOn | techsSwitchOff | techsBatteryOff) + { + ALOGD ("%s: route to EE h=0x%X", fn, NFA_EE_HANDLE_DH); + SyncEventGuard guard (mRoutingEvent); + nfaStat = NFA_EeSetDefaultTechRouting (NFA_EE_HANDLE_DH, + techsSwitchOn, techsSwitchOff, techsBatteryOff); + if (nfaStat == NFA_STATUS_OK) + mRoutingEvent.wait (); + else + ALOGE ("%s: fail route to EE; error=0x%X", fn, nfaStat); + } + } + + ////////////////////// + // if route database is empty, setup a default route + ////////////////////// + if (db->empty()) + { + tNFA_HANDLE eeHandle = NFA_EE_HANDLE_DH; + if (routeSelection == SecElemRoute) + eeHandle = getDefaultEeHandle (); + ALOGD ("%s: route to default EE h=0x%X", fn, eeHandle); + SyncEventGuard guard (mRoutingEvent); + nfaStat = NFA_EeSetDefaultTechRouting (eeHandle, techMask, 0, 0); + if (nfaStat == NFA_STATUS_OK) + mRoutingEvent.wait (); + else + ALOGE ("%s: fail route to EE; error=0x%X", fn, nfaStat); + } + ALOGD ("%s: exit", fn); +} + + +/******************************************************************************* +** +** Function: nfaEeCallback +** +** Description: Receive execution environment-related events from stack. +** event: Event code. +** eventData: Event data. +** +** Returns: None +** +*******************************************************************************/ +void SecureElement::nfaEeCallback (tNFA_EE_EVT event, tNFA_EE_CBACK_DATA* eventData) +{ + static const char fn [] = "SecureElement::nfaEeCallback"; + + switch (event) + { + case NFA_EE_REGISTER_EVT: + { + SyncEventGuard guard (sSecElem.mEeRegisterEvent); + ALOGD ("%s: NFA_EE_REGISTER_EVT; status=%u", fn, eventData->ee_register); + sSecElem.mEeRegisterEvent.notifyOne(); + } + break; + + case NFA_EE_MODE_SET_EVT: + { + ALOGD ("%s: NFA_EE_MODE_SET_EVT; status: 0x%04X handle: 0x%04X mActiveEeHandle: 0x%04X", fn, + eventData->mode_set.status, eventData->mode_set.ee_handle, sSecElem.mActiveEeHandle); + + if (eventData->mode_set.status == NFA_STATUS_OK) + { + tNFA_EE_INFO *pEE = sSecElem.findEeByHandle (eventData->mode_set.ee_handle); + if (pEE) + { + pEE->ee_status ^= 1; + ALOGD ("%s: NFA_EE_MODE_SET_EVT; pEE->ee_status: %s (0x%04x)", fn, SecureElement::eeStatusToString(pEE->ee_status), pEE->ee_status); + } + else + ALOGE ("%s: NFA_EE_MODE_SET_EVT; EE: 0x%04x not found. mActiveEeHandle: 0x%04x", fn, eventData->mode_set.ee_handle, sSecElem.mActiveEeHandle); + } + SyncEventGuard guard (sSecElem.mEeSetModeEvent); + sSecElem.mEeSetModeEvent.notifyOne(); + } + break; + + case NFA_EE_SET_TECH_CFG_EVT: + { + ALOGD ("%s: NFA_EE_SET_TECH_CFG_EVT; status=0x%X", fn, eventData->status); + SyncEventGuard guard (sSecElem.mRoutingEvent); + sSecElem.mRoutingEvent.notifyOne (); + } + break; + + case NFA_EE_SET_PROTO_CFG_EVT: + { + ALOGD ("%s: NFA_EE_SET_PROTO_CFG_EVT; status=0x%X", fn, eventData->status); + SyncEventGuard guard (sSecElem.mRoutingEvent); + sSecElem.mRoutingEvent.notifyOne (); + } + break; + + case NFA_EE_ACTION_EVT: + { + tNFA_EE_ACTION& action = eventData->action; + if (action.trigger == NFC_EE_TRIG_SELECT) + ALOGD ("%s: NFA_EE_ACTION_EVT; h=0x%X; trigger=select (0x%X)", fn, action.ee_handle, action.trigger); + else if (action.trigger == NFC_EE_TRIG_APP_INIT) + { + tNFC_APP_INIT& app_init = action.param.app_init; + ALOGD ("%s: NFA_EE_ACTION_EVT; h=0x%X; trigger=app-init (0x%X); aid len=%u; data len=%u", fn, + action.ee_handle, action.trigger, app_init.len_aid, app_init.len_data); + //if app-init operation is successful; + //app_init.data[] contains two bytes, which are the status codes of the event; + //app_init.data[] does not contain an APDU response; + //see EMV Contactless Specification for Payment Systems; Book B; Entry Point Specification; + //version 2.1; March 2011; section 3.3.3.5; + if ( (app_init.len_data > 1) && + (app_init.data[0] == 0x90) && + (app_init.data[1] == 0x00) ) + { + sSecElem.notifyTransactionListenersOfAid (app_init.aid, app_init.len_aid); + } + } + else if (action.trigger == NFC_EE_TRIG_RF_PROTOCOL) + ALOGD ("%s: NFA_EE_ACTION_EVT; h=0x%X; trigger=rf protocol (0x%X)", fn, action.ee_handle, action.trigger); + else if (action.trigger == NFC_EE_TRIG_RF_TECHNOLOGY) + ALOGD ("%s: NFA_EE_ACTION_EVT; h=0x%X; trigger=rf tech (0x%X)", fn, action.ee_handle, action.trigger); + else + ALOGE ("%s: NFA_EE_ACTION_EVT; h=0x%X; unknown trigger (0x%X)", fn, action.ee_handle, action.trigger); + } + break; + + case NFA_EE_DISCOVER_REQ_EVT: + ALOGD ("%s: NFA_EE_DISCOVER_REQ_EVT; status=0x%X; num ee=%u", __FUNCTION__, + eventData->discover_req.status, eventData->discover_req.num_ee); + sSecElem.storeUiccInfo (eventData->discover_req); + break; + + case NFA_EE_NO_CB_ERR_EVT: + ALOGD ("%s: NFA_EE_NO_CB_ERR_EVT status=%u", fn, eventData->status); + break; + + case NFA_EE_ADD_AID_EVT: + { + ALOGD ("%s: NFA_EE_ADD_AID_EVT status=%u", fn, eventData->status); + SyncEventGuard guard (sSecElem.mAidAddRemoveEvent); + sSecElem.mAidAddRemoveEvent.notifyOne (); + } + break; + + case NFA_EE_REMOVE_AID_EVT: + { + ALOGD ("%s: NFA_EE_REMOVE_AID_EVT status=%u", fn, eventData->status); + SyncEventGuard guard (sSecElem.mAidAddRemoveEvent); + sSecElem.mAidAddRemoveEvent.notifyOne (); + } + break; + + case NFA_EE_NEW_EE_EVT: + { + ALOGD ("%s: NFA_EE_NEW_EE_EVT h=0x%X; status=%u", fn, + eventData->new_ee.ee_handle, eventData->new_ee.ee_status); + // Indicate there are new EE + sSecElem.mbNewEE = true; + } + break; + + default: + ALOGE ("%s: unknown event=%u ????", fn, event); + break; + } +} + +/******************************************************************************* +** +** Function getSeVerInfo +** +** Description Gets version information and id for a secure element. The +** seIndex parmeter is the zero based index of the secure +** element to get verion info for. The version infommation +** is returned as a string int the verInfo parameter. +** +** Returns ture on success, false on failure +** +*******************************************************************************/ +bool SecureElement::getSeVerInfo(int seIndex, char * verInfo, int verInfoSz, UINT8 * seid) +{ + ALOGD("%s: enter, seIndex=%d", __FUNCTION__, seIndex); + + if (seIndex > (mActualNumEe-1)) + { + ALOGE("%s: invalid se index: %d, only %d SEs in system", __FUNCTION__, seIndex, mActualNumEe); + return false; + } + + *seid = mEeInfo[seIndex].ee_handle; + + if ((mEeInfo[seIndex].num_interface == 0) || (mEeInfo[seIndex].ee_interface[0] == NCI_NFCEE_INTERFACE_HCI_ACCESS) ) + { + return false; + } + + strncpy(verInfo, "Version info not available", verInfoSz-1); + verInfo[verInfoSz-1] = '\0'; + + UINT8 pipe = (mEeInfo[seIndex].ee_handle == EE_HANDLE_0xF3) ? 0x70 : 0x71; + UINT8 host = (pipe == STATIC_PIPE_0x70) ? 0x02 : 0x03; + UINT8 gate = (pipe == STATIC_PIPE_0x70) ? 0xF0 : 0xF1; + + tNFA_STATUS nfaStat = NFA_HciAddStaticPipe(mNfaHciHandle, host, gate, pipe); + if (nfaStat != NFA_STATUS_OK) + { + ALOGE ("%s: NFA_HciAddStaticPipe() failed, pipe = 0x%x, error=0x%X", __FUNCTION__, pipe, nfaStat); + return true; + } + + SyncEventGuard guard (mVerInfoEvent); + if (NFA_STATUS_OK == (nfaStat = NFA_HciGetRegistry (mNfaHciHandle, pipe, 0x02))) + { + if (false == mVerInfoEvent.wait(200)) + { + ALOGE ("%s: wait response timeout", __FUNCTION__); + } + else + { + snprintf(verInfo, verInfoSz-1, "Oberthur OS S/N: 0x%02x%02x%02x", mVerInfo[0], mVerInfo[1], mVerInfo[2]); + verInfo[verInfoSz-1] = '\0'; + } + } + else + { + ALOGE ("%s: NFA_HciGetRegistry () failed: 0x%X", __FUNCTION__, nfaStat); + } + return true; +} + +/******************************************************************************* +** +** Function getActualNumEe +** +** Description Returns number of secure elements we know about. +** +** Returns Number of secure elements we know about. +** +*******************************************************************************/ +UINT8 SecureElement::getActualNumEe() +{ + return mActualNumEe; +} + +/******************************************************************************* +** +** Function: nfaHciCallback +** +** Description: Receive Host Controller Interface-related events from stack. +** event: Event code. +** eventData: Event data. +** +** Returns: None +** +*******************************************************************************/ +void SecureElement::nfaHciCallback (tNFA_HCI_EVT event, tNFA_HCI_EVT_DATA* eventData) +{ + static const char fn [] = "SecureElement::nfaHciCallback"; + ALOGD ("%s: event=0x%X", fn, event); + + switch (event) + { + case NFA_HCI_REGISTER_EVT: + { + ALOGD ("%s: NFA_HCI_REGISTER_EVT; status=0x%X; handle=0x%X", fn, + eventData->hci_register.status, eventData->hci_register.hci_handle); + SyncEventGuard guard (sSecElem.mHciRegisterEvent); + sSecElem.mNfaHciHandle = eventData->hci_register.hci_handle; + sSecElem.mHciRegisterEvent.notifyOne(); + } + break; + + case NFA_HCI_ALLOCATE_GATE_EVT: + { + ALOGD ("%s: NFA_HCI_ALLOCATE_GATE_EVT; status=0x%X; gate=0x%X", fn, eventData->status, eventData->allocated.gate); + SyncEventGuard guard (sSecElem.mAllocateGateEvent); + sSecElem.mCommandStatus = eventData->status; + sSecElem.mNewSourceGate = (eventData->allocated.status == NFA_STATUS_OK) ? eventData->allocated.gate : 0; + sSecElem.mAllocateGateEvent.notifyOne(); + } + break; + + case NFA_HCI_DEALLOCATE_GATE_EVT: + { + tNFA_HCI_DEALLOCATE_GATE& deallocated = eventData->deallocated; + ALOGD ("%s: NFA_HCI_DEALLOCATE_GATE_EVT; status=0x%X; gate=0x%X", fn, deallocated.status, deallocated.gate); + SyncEventGuard guard (sSecElem.mDeallocateGateEvent); + sSecElem.mDeallocateGateEvent.notifyOne(); + } + break; + + case NFA_HCI_GET_GATE_PIPE_LIST_EVT: + { + ALOGD ("%s: NFA_HCI_GET_GATE_PIPE_LIST_EVT; status=0x%X; num_pipes: %u num_gates: %u", fn, + eventData->gates_pipes.status, eventData->gates_pipes.num_pipes, eventData->gates_pipes.num_gates); + SyncEventGuard guard (sSecElem.mPipeListEvent); + sSecElem.mCommandStatus = eventData->gates_pipes.status; + sSecElem.mHciCfg = eventData->gates_pipes; + sSecElem.mPipeListEvent.notifyOne(); + } + break; + + case NFA_HCI_CREATE_PIPE_EVT: + { + ALOGD ("%s: NFA_HCI_CREATE_PIPE_EVT; status=0x%X; pipe=0x%X; src gate=0x%X; dest host=0x%X; dest gate=0x%X", fn, + eventData->created.status, eventData->created.pipe, eventData->created.source_gate, eventData->created.dest_host, eventData->created.dest_gate); + SyncEventGuard guard (sSecElem.mCreatePipeEvent); + sSecElem.mCommandStatus = eventData->created.status; + sSecElem.mNewPipeId = eventData->created.pipe; + sSecElem.mCreatePipeEvent.notifyOne(); + } + break; + + case NFA_HCI_OPEN_PIPE_EVT: + { + ALOGD ("%s: NFA_HCI_OPEN_PIPE_EVT; status=0x%X; pipe=0x%X", fn, eventData->opened.status, eventData->opened.pipe); + SyncEventGuard guard (sSecElem.mPipeOpenedEvent); + sSecElem.mCommandStatus = eventData->opened.status; + sSecElem.mPipeOpenedEvent.notifyOne(); + } + break; + + case NFA_HCI_EVENT_SENT_EVT: + ALOGD ("%s: NFA_HCI_EVENT_SENT_EVT; status=0x%X", fn, eventData->evt_sent.status); + break; + + case NFA_HCI_RSP_RCVD_EVT: //response received from secure element + { + tNFA_HCI_RSP_RCVD& rsp_rcvd = eventData->rsp_rcvd; + ALOGD ("%s: NFA_HCI_RSP_RCVD_EVT; status: 0x%X; code: 0x%X; pipe: 0x%X; len: %u", fn, + rsp_rcvd.status, rsp_rcvd.rsp_code, rsp_rcvd.pipe, rsp_rcvd.rsp_len); + } + break; + + case NFA_HCI_GET_REG_RSP_EVT : + ALOGD ("%s: NFA_HCI_GET_REG_RSP_EVT; status: 0x%X; pipe: 0x%X, len: %d", fn, + eventData->registry.status, eventData->registry.pipe, eventData->registry.data_len); + if (eventData->registry.data_len >= 19 && ((eventData->registry.pipe == STATIC_PIPE_0x70) || (eventData->registry.pipe == STATIC_PIPE_0x71))) + { + SyncEventGuard guard (sSecElem.mVerInfoEvent); + // Oberthur OS version is in bytes 16,17, and 18 + sSecElem.mVerInfo[0] = eventData->registry.reg_data[16]; + sSecElem.mVerInfo[1] = eventData->registry.reg_data[17]; + sSecElem.mVerInfo[2] = eventData->registry.reg_data[18]; + sSecElem.mVerInfoEvent.notifyOne (); + } + break; + + case NFA_HCI_EVENT_RCVD_EVT: + ALOGD ("%s: NFA_HCI_EVENT_RCVD_EVT; code: 0x%X; pipe: 0x%X; data len: %u", fn, + eventData->rcvd_evt.evt_code, eventData->rcvd_evt.pipe, eventData->rcvd_evt.evt_len); + if ((eventData->rcvd_evt.pipe == STATIC_PIPE_0x70) || (eventData->rcvd_evt.pipe == STATIC_PIPE_0x71)) + { + ALOGD ("%s: NFA_HCI_EVENT_RCVD_EVT; data from static pipe", fn); + SyncEventGuard guard (sSecElem.mTransceiveEvent); + sSecElem.mActualResponseSize = (eventData->rcvd_evt.evt_len > MAX_RESPONSE_SIZE) ? MAX_RESPONSE_SIZE : eventData->rcvd_evt.evt_len; + sSecElem.mTransceiveEvent.notifyOne (); + } + else if (eventData->rcvd_evt.evt_code == NFA_HCI_EVT_POST_DATA) + { + ALOGD ("%s: NFA_HCI_EVENT_RCVD_EVT; NFA_HCI_EVT_POST_DATA", fn); + SyncEventGuard guard (sSecElem.mTransceiveEvent); + sSecElem.mActualResponseSize = (eventData->rcvd_evt.evt_len > MAX_RESPONSE_SIZE) ? MAX_RESPONSE_SIZE : eventData->rcvd_evt.evt_len; + sSecElem.mTransceiveEvent.notifyOne (); + } + else if (eventData->rcvd_evt.evt_code == NFA_HCI_EVT_TRANSACTION) + { + ALOGD ("%s: NFA_HCI_EVENT_RCVD_EVT; NFA_HCI_EVT_TRANSACTION", fn); + // If we got an AID, notify any listeners + if ((eventData->rcvd_evt.evt_len > 3) && (eventData->rcvd_evt.p_evt_buf[0] == 0x81) ) + sSecElem.notifyTransactionListenersOfAid (&eventData->rcvd_evt.p_evt_buf[2], eventData->rcvd_evt.p_evt_buf[1]); + } + break; + + case NFA_HCI_SET_REG_RSP_EVT: //received response to write registry command + { + tNFA_HCI_REGISTRY& registry = eventData->registry; + ALOGD ("%s: NFA_HCI_SET_REG_RSP_EVT; status=0x%X; pipe=0x%X", fn, registry.status, registry.pipe); + SyncEventGuard guard (sSecElem.mRegistryEvent); + sSecElem.mRegistryEvent.notifyOne (); + break; + } + + default: + ALOGE ("%s: unknown event code=0x%X ????", fn, event); + break; + } +} + + +/******************************************************************************* +** +** Function: findEeByHandle +** +** Description: Find information about an execution environment. +** eeHandle: Handle to execution environment. +** +** Returns: Information about an execution environment. +** +*******************************************************************************/ +tNFA_EE_INFO *SecureElement::findEeByHandle (tNFA_HANDLE eeHandle) +{ + for (UINT8 xx = 0; xx < mActualNumEe; xx++) + { + if (mEeInfo[xx].ee_handle == eeHandle) + return (&mEeInfo[xx]); + } + return (NULL); +} + + +/******************************************************************************* +** +** Function: getDefaultEeHandle +** +** Description: Get the handle to the execution environment. +** +** Returns: Handle to the execution environment. +** +*******************************************************************************/ +tNFA_HANDLE SecureElement::getDefaultEeHandle () +{ + // Find the first EE that is not the HCI Access i/f. + for (UINT8 xx = 0; xx < mActualNumEe; xx++) + { + if ((mEeInfo[xx].num_interface != 0) && (mEeInfo[xx].ee_interface[0] != NCI_NFCEE_INTERFACE_HCI_ACCESS) ) + return (mEeInfo[xx].ee_handle); + } + return NFA_HANDLE_INVALID; +} + + + /******************************************************************************* + ** + ** Function: findUiccByHandle + ** + ** Description: Find information about an execution environment. + ** eeHandle: Handle of the execution environment. + ** + ** Returns: Information about the execution environment. + ** + *******************************************************************************/ +tNFA_EE_DISCOVER_INFO *SecureElement::findUiccByHandle (tNFA_HANDLE eeHandle) +{ + for (UINT8 index = 0; index < mUiccInfo.num_ee; index++) + { + if (mUiccInfo.ee_disc_info[index].ee_handle == eeHandle) + { + return (&mUiccInfo.ee_disc_info[index]); + } + } + ALOGE ("SecureElement::findUiccByHandle: ee h=0x%4x not found", eeHandle); + return NULL; +} + + +/******************************************************************************* +** +** Function: eeStatusToString +** +** Description: Convert status code to status text. +** status: Status code +** +** Returns: None +** +*******************************************************************************/ +const char* SecureElement::eeStatusToString (UINT8 status) +{ + switch (status) + { + case NFC_NFCEE_STATUS_ACTIVE: + return("Connected/Active"); + case NFC_NFCEE_STATUS_INACTIVE: + return("Connected/Inactive"); + case NFC_NFCEE_STATUS_REMOVED: + return("Removed"); + } + return("?? Unknown ??"); +} + + +/******************************************************************************* +** +** Function: connectionEventHandler +** +** Description: Receive card-emulation related events from stack. +** event: Event code. +** eventData: Event data. +** +** Returns: None +** +*******************************************************************************/ +void SecureElement::connectionEventHandler (UINT8 event, tNFA_CONN_EVT_DATA* eventData) +{ + switch (event) + { + case NFA_CE_UICC_LISTEN_CONFIGURED_EVT: + { + SyncEventGuard guard (mUiccListenEvent); + mUiccListenEvent.notifyOne (); + } + break; + } +} + + +/******************************************************************************* +** +** Function: routeToSecureElement +** +** Description: Adjust controller's listen-mode routing table so transactions +** are routed to the secure elements. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool SecureElement::routeToSecureElement () +{ + static const char fn [] = "SecureElement::routeToSecureElement"; + ALOGD ("%s: enter", fn); + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + tNFA_TECHNOLOGY_MASK tech_mask = NFA_TECHNOLOGY_MASK_A | NFA_TECHNOLOGY_MASK_B; + bool retval = false; + + if (! mIsInit) + { + ALOGE ("%s: not init", fn); + return false; + } + + if (mCurrentRouteSelection == SecElemRoute) + { + ALOGE ("%s: already sec elem route", fn); + return true; + } + + if (mActiveEeHandle == NFA_HANDLE_INVALID) + { + ALOGE ("%s: invalid EE handle", fn); + return false; + } + + adjustRoutes (SecElemRoute); + + { + unsigned long num = 0; + if (GetNumValue("UICC_LISTEN_TECH_MASK", &num, sizeof(num))) + tech_mask = num; + ALOGD ("%s: start UICC listen; h=0x%X; tech mask=0x%X", fn, mActiveEeHandle, tech_mask); + SyncEventGuard guard (mUiccListenEvent); + nfaStat = NFA_CeConfigureUiccListenTech (mActiveEeHandle, tech_mask); + if (nfaStat == NFA_STATUS_OK) + { + mUiccListenEvent.wait (); + retval = true; + } + else + ALOGE ("%s: fail to start UICC listen", fn); + } + + ALOGD ("%s: exit; ok=%u", fn, retval); + return retval; +} + + +/******************************************************************************* +** +** Function: routeToDefault +** +** Description: Adjust controller's listen-mode routing table so transactions +** are routed to the default destination. +** +** Returns: True if ok. +** +*******************************************************************************/ +bool SecureElement::routeToDefault () +{ + static const char fn [] = "SecureElement::routeToDefault"; + tNFA_STATUS nfaStat = NFA_STATUS_FAILED; + bool retval = false; + + ALOGD ("%s: enter", fn); + if (! mIsInit) + { + ALOGE ("%s: not init", fn); + return false; + } + + if (mCurrentRouteSelection == DefaultRoute) + { + ALOGD ("%s: already default route", fn); + return true; + } + + if (mActiveEeHandle != NFA_HANDLE_INVALID) + { + ALOGD ("%s: stop UICC listen; EE h=0x%X", fn, mActiveEeHandle); + SyncEventGuard guard (mUiccListenEvent); + nfaStat = NFA_CeConfigureUiccListenTech (mActiveEeHandle, 0); + if (nfaStat == NFA_STATUS_OK) + { + mUiccListenEvent.wait (); + retval = true; + } + else + ALOGE ("%s: fail to stop UICC listen", fn); + } + else + retval = true; + + adjustRoutes (DefaultRoute); + + ALOGD ("%s: exit; ok=%u", fn, retval); + return retval; +} + + +/******************************************************************************* +** +** Function: isBusy +** +** Description: Whether controller is routing listen-mode events to +** secure elements or a pipe is connected. +** +** Returns: True if either case is true. +** +*******************************************************************************/ +bool SecureElement::isBusy () +{ + bool retval = (mCurrentRouteSelection == SecElemRoute) || mIsPiping; + ALOGD ("SecureElement::isBusy: %u", retval); + return retval; +} + diff --git a/nci/jni/SecureElement.h b/nci/jni/SecureElement.h new file mode 100755 index 0000000..1758590 --- /dev/null +++ b/nci/jni/SecureElement.h @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Communicate with secure elements that are attached to the NFC + * controller. + */ +#pragma once +#include "SyncEvent.h" +#include "DataQueue.h" +#include "NfcJniUtil.h" +#include "RouteDataSet.h" +extern "C" +{ + #include "nfa_ee_api.h" + #include "nfa_hci_api.h" + #include "nfa_hci_defs.h" + #include "nfa_ce_api.h" +} + + +class SecureElement +{ +public: + tNFA_HANDLE mActiveEeHandle; + + + /******************************************************************************* + ** + ** Function: getInstance + ** + ** Description: Get the SecureElement singleton object. + ** + ** Returns: SecureElement object. + ** + *******************************************************************************/ + static SecureElement& getInstance (); + + + /******************************************************************************* + ** + ** Function: initialize + ** + ** Description: Initialize all member variables. + ** native: Native data. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool initialize (nfc_jni_native_data* native); + + + /******************************************************************************* + ** + ** Function: finalize + ** + ** Description: Release all resources. + ** + ** Returns: None + ** + *******************************************************************************/ + void finalize (); + + + /******************************************************************************* + ** + ** Function: getListOfEeHandles + ** + ** Description: Get the list of handles of all execution environments. + ** e: Java Virtual Machine. + ** + ** Returns: List of handles of all execution environments. + ** + *******************************************************************************/ + jintArray getListOfEeHandles (JNIEnv* e); + + + /******************************************************************************* + ** + ** Function: activate + ** + ** Description: Turn on the secure element. + ** seID: ID of secure element. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool activate (jint seID); + + + /******************************************************************************* + ** + ** Function: deactivate + ** + ** Description: Turn off the secure element. + ** seID: ID of secure element. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool deactivate (jint seID); + + + /******************************************************************************* + ** + ** Function: connectEE + ** + ** Description: Connect to the execution environment. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool connectEE (); + + + /******************************************************************************* + ** + ** Function: disconnectEE + ** + ** Description: Disconnect from the execution environment. + ** seID: ID of secure element. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool disconnectEE (jint seID); + + + /******************************************************************************* + ** + ** Function: transceive + ** + ** Description: Send data to the secure element; read it's response. + ** xmitBuffer: Data to transmit. + ** xmitBufferSize: Length of data. + ** recvBuffer: Buffer to receive response. + ** recvBufferMaxSize: Maximum size of buffer. + ** recvBufferActualSize: Actual length of response. + ** timeoutMillisec: timeout in millisecond + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool transceive (UINT8* xmitBuffer, INT32 xmitBufferSize, UINT8* recvBuffer, + INT32 recvBufferMaxSize, INT32& recvBufferActualSize, INT32 timeoutMillisec); + + /******************************************************************************* + ** + ** Function: notifyListenModeState + ** + ** Description: Notify the NFC service about whether the SE was activated + ** in listen mode. + ** isActive: Whether the secure element is activated. + ** + ** Returns: None + ** + *******************************************************************************/ + void notifyListenModeState (bool isActivated); + + /******************************************************************************* + ** + ** Function: notifyRfFieldEvent + ** + ** Description: Notify the NFC service about RF field events from the stack. + ** isActive: Whether any secure element is activated. + ** + ** Returns: None + ** + *******************************************************************************/ + void notifyRfFieldEvent (bool isActive); + + + /******************************************************************************* + ** + ** Function: storeUiccInfo + ** + ** Description: Store a copy of the execution environment information from the stack. + ** info: execution environment information. + ** + ** Returns: None + ** + *******************************************************************************/ + void storeUiccInfo (tNFA_EE_DISCOVER_REQ& info); + + + /******************************************************************************* + ** + ** Function: getUiccId + ** + ** Description: Get the ID of the secure element. + ** eeHandle: Handle to the secure element. + ** uid: Array to receive the ID. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool getUiccId (tNFA_HANDLE eeHandle, jbyteArray& uid); + + + /******************************************************************************* + ** + ** Function: getTechnologyList + ** + ** Description: Get all the technologies supported by a secure element. + ** eeHandle: Handle of secure element. + ** techList: List to receive the technologies. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool getTechnologyList (tNFA_HANDLE eeHandle, jintArray& techList); + + + /******************************************************************************* + ** + ** Function: notifyTransactionListenersOfAid + ** + ** Description: Notify the NFC service about a transaction event from secure element. + ** aid: Buffer contains application ID. + ** aidLen: Length of application ID. + ** + ** Returns: None + ** + *******************************************************************************/ + void notifyTransactionListenersOfAid (const UINT8* aid, UINT8 aidLen); + + + /******************************************************************************* + ** + ** Function: notifyTransactionListenersOfTlv + ** + ** Description: Notify the NFC service about a transaction event from secure element. + ** The type-length-value contains AID and parameter. + ** tlv: type-length-value encoded in Basic Encoding Rule. + ** tlvLen: Length tlv. + ** + ** Returns: None + ** + *******************************************************************************/ + void notifyTransactionListenersOfTlv (const UINT8* tlv, UINT8 tlvLen); + + + /******************************************************************************* + ** + ** Function: connectionEventHandler + ** + ** Description: Receive card-emulation related events from stack. + ** event: Event code. + ** eventData: Event data. + ** + ** Returns: None + ** + *******************************************************************************/ + void connectionEventHandler (UINT8 event, tNFA_CONN_EVT_DATA* eventData); + + + /******************************************************************************* + ** + ** Function: applyRoutes + ** + ** Description: Read route data from XML and apply them again + ** to every secure element. + ** + ** Returns: None + ** + *******************************************************************************/ + void applyRoutes (); + + + /******************************************************************************* + ** + ** Function: setActiveSeOverride + ** + ** Description: Specify which secure element to turn on. + ** activeSeOverride: ID of secure element + ** + ** Returns: None + ** + *******************************************************************************/ + void setActiveSeOverride (UINT8 activeSeOverride); + + + /******************************************************************************* + ** + ** Function: routeToSecureElement + ** + ** Description: Adjust controller's listen-mode routing table so transactions + ** are routed to the secure elements as specified in route.xml. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool routeToSecureElement (); + + + /******************************************************************************* + ** + ** Function: routeToDefault + ** + ** Description: Adjust controller's listen-mode routing table so transactions + ** are routed to the default destination specified in route.xml. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool routeToDefault (); + + + /******************************************************************************* + ** + ** Function: isBusy + ** + ** Description: Whether NFC controller is routing listen-mode events or a pipe is connected. + ** + ** Returns: True if either case is true. + ** + *******************************************************************************/ + bool isBusy (); + + + /******************************************************************************* + ** + ** Function getActualNumEe + ** + ** Description Returns number of secure elements we know about. + ** + ** Returns Number of secure elements we know about. + ** + *******************************************************************************/ + UINT8 getActualNumEe(); + + + /******************************************************************************* + ** + ** Function getSeVerInfo + ** + ** Description Gets version information and id for a secure element. The + ** seIndex parmeter is the zero based index of the secure + ** element to get verion info for. The version infommation + ** is returned as a string int the verInfo parameter. + ** + ** Returns ture on success, false on failure + ** + *******************************************************************************/ + bool getSeVerInfo(int seIndex, char * verInfo, int verInfoSz, UINT8 * seid); + + + /******************************************************************************* + ** + ** Function: isActivatedInListenMode + ** + ** Description: Can be used to determine if the SE is activated in listen mode + ** + ** Returns: True if the SE is activated in listen mode + ** + *******************************************************************************/ + bool isActivatedInListenMode(); + + /******************************************************************************* + ** + ** Function: isRfFieldOn + ** + ** Description: Can be used to determine if the SE is in an RF field + ** + ** Returns: True if the SE is activated in an RF field + ** + *******************************************************************************/ + bool isRfFieldOn(); + +private: + static const unsigned int MAX_RESPONSE_SIZE = 1024; + enum RouteSelection {NoRoute, DefaultRoute, SecElemRoute}; + static const int MAX_NUM_EE = 5; //max number of EE's + static const UINT8 STATIC_PIPE_0x70 = 0x70; //Broadcom's proprietary static pipe + static const UINT8 STATIC_PIPE_0x71 = 0x71; //Broadcom's proprietary static pipe + static const UINT8 EVT_SEND_DATA = 0x10; //see specification ETSI TS 102 622 v9.0.0 (Host Controller Interface); section 9.3.3.3 + static const tNFA_HANDLE EE_HANDLE_0xF3 = 0x4F3; //handle to secure element in slot 0 + static const tNFA_HANDLE EE_HANDLE_0xF4 = 0x4F4; //handle to secure element in slot 1 + static SecureElement sSecElem; + static const char* APP_NAME; + + UINT8 mDestinationGate; //destination gate of the UICC + tNFA_HANDLE mNfaHciHandle; //NFA handle to NFA's HCI component + nfc_jni_native_data* mNativeData; + bool mIsInit; // whether EE is initialized + UINT8 mActualNumEe; // actual number of EE's reported by the stack + UINT8 mNumEePresent; // actual number of usable EE's + bool mbNewEE; + UINT8 mNewPipeId; + UINT8 mNewSourceGate; + UINT16 mActiveSeOverride; // active "enable" seid, 0 means activate all SEs + tNFA_STATUS mCommandStatus; //completion status of the last command + bool mIsPiping; //is a pipe connected to the controller? + RouteSelection mCurrentRouteSelection; + int mActualResponseSize; //number of bytes in the response received from secure element + bool mUseOberthurWarmReset; //whether to use warm-reset command + bool mActivatedInListenMode; // whether we're activated in listen mode + UINT8 mOberthurWarmResetCommand; //warm-reset command byte + tNFA_EE_INFO mEeInfo [MAX_NUM_EE]; //actual size stored in mActualNumEe + tNFA_EE_DISCOVER_REQ mUiccInfo; + tNFA_HCI_GET_GATE_PIPE_LIST mHciCfg; + SyncEvent mEeRegisterEvent; + SyncEvent mHciRegisterEvent; + SyncEvent mEeSetModeEvent; + SyncEvent mPipeListEvent; + SyncEvent mCreatePipeEvent; + SyncEvent mPipeOpenedEvent; + SyncEvent mAllocateGateEvent; + SyncEvent mDeallocateGateEvent; + SyncEvent mRoutingEvent; + SyncEvent mUiccInfoEvent; + SyncEvent mUiccListenEvent; + SyncEvent mAidAddRemoveEvent; + SyncEvent mTransceiveEvent; + SyncEvent mVerInfoEvent; + SyncEvent mRegistryEvent; + UINT8 mVerInfo [3]; + UINT8 mResponseData [MAX_RESPONSE_SIZE]; + RouteDataSet mRouteDataSet; //routing data + std::vector<std::string> mUsedAids; //AID's that are used in current routes + UINT8 mAidForEmptySelect[NCI_MAX_AID_LEN+1]; + Mutex mMutex; // protects fields below + bool mRfFieldIsOn; // last known RF field state + struct timespec mLastRfFieldToggle; // last time RF field went off + /******************************************************************************* + ** + ** Function: SecureElement + ** + ** Description: Initialize member variables. + ** + ** Returns: None + ** + *******************************************************************************/ + SecureElement (); + + + /******************************************************************************* + ** + ** Function: ~SecureElement + ** + ** Description: Release all resources. + ** + ** Returns: None + ** + *******************************************************************************/ + ~SecureElement (); + + + /******************************************************************************* + ** + ** Function: nfaEeCallback + ** + ** Description: Receive execution environment-related events from stack. + ** event: Event code. + ** eventData: Event data. + ** + ** Returns: None + ** + *******************************************************************************/ + static void nfaEeCallback (tNFA_EE_EVT event, tNFA_EE_CBACK_DATA* eventData); + + + /******************************************************************************* + ** + ** Function: nfaHciCallback + ** + ** Description: Receive Host Controller Interface-related events from stack. + ** event: Event code. + ** eventData: Event data. + ** + ** Returns: None + ** + *******************************************************************************/ + static void nfaHciCallback (tNFA_HCI_EVT event, tNFA_HCI_EVT_DATA* eventData); + + + /******************************************************************************* + ** + ** Function: findEeByHandle + ** + ** Description: Find information about an execution environment. + ** eeHandle: Handle to execution environment. + ** + ** Returns: Information about an execution environment. + ** + *******************************************************************************/ + tNFA_EE_INFO *findEeByHandle (tNFA_HANDLE eeHandle); + + + /******************************************************************************* + ** + ** Function: findUiccByHandle + ** + ** Description: Find information about an execution environment. + ** eeHandle: Handle of the execution environment. + ** + ** Returns: Information about the execution environment. + ** + *******************************************************************************/ + tNFA_EE_DISCOVER_INFO *findUiccByHandle (tNFA_HANDLE eeHandle); + + + /******************************************************************************* + ** + ** Function: getDefaultEeHandle + ** + ** Description: Get the handle to the execution environment. + ** + ** Returns: Handle to the execution environment. + ** + *******************************************************************************/ + tNFA_HANDLE getDefaultEeHandle (); + + + /******************************************************************************* + ** + ** Function: adjustRoutes + ** + ** Description: Adjust routes in the controller's listen-mode routing table. + ** selection: which set of routes to configure the controller. + ** + ** Returns: None + ** + *******************************************************************************/ + void adjustRoutes (RouteSelection selection); + + + /******************************************************************************* + ** + ** Function: adjustProtocolRoutes + ** + ** Description: Adjust default routing based on protocol in NFC listen mode. + ** isRouteToEe: Whether routing to EE (true) or host (false). + ** + ** Returns: None + ** + *******************************************************************************/ + void adjustProtocolRoutes (RouteDataSet::Database* db, RouteSelection routeSelection); + + + /******************************************************************************* + ** + ** Function: adjustTechnologyRoutes + ** + ** Description: Adjust default routing based on technology in NFC listen mode. + ** isRouteToEe: Whether routing to EE (true) or host (false). + ** + ** Returns: None + ** + *******************************************************************************/ + void adjustTechnologyRoutes (RouteDataSet::Database* db, RouteSelection routeSelection); + + + /******************************************************************************* + ** + ** Function: getEeInfo + ** + ** Description: Get latest information about execution environments from stack. + ** + ** Returns: True if at least 1 EE is available. + ** + *******************************************************************************/ + bool getEeInfo (); + + /******************************************************************************* + ** + ** Function: eeStatusToString + ** + ** Description: Convert status code to status text. + ** status: Status code + ** + ** Returns: None + ** + *******************************************************************************/ + static const char* eeStatusToString (UINT8 status); + + + /******************************************************************************* + ** + ** Function: encodeAid + ** + ** Description: Encode AID in type-length-value using Basic Encoding Rule. + ** tlv: Buffer to store TLV. + ** tlvMaxLen: TLV buffer's maximum length. + ** tlvActualLen: TLV buffer's actual length. + ** aid: Buffer of Application ID. + ** aidLen: Aid buffer's actual length. + ** + ** Returns: True if ok. + ** + *******************************************************************************/ + bool encodeAid (UINT8* tlv, UINT16 tlvMaxLen, UINT16& tlvActualLen, const UINT8* aid, UINT8 aidLen); +}; + diff --git a/nci/jni/SyncEvent.h b/nci/jni/SyncEvent.h new file mode 100644 index 0000000..5fd389e --- /dev/null +++ b/nci/jni/SyncEvent.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Synchronize two or more threads using a condition variable and a mutex. + */ +#pragma once +#include "CondVar.h" +#include "Mutex.h" + + +class SyncEvent +{ +public: + /******************************************************************************* + ** + ** Function: ~SyncEvent + ** + ** Description: Cleanup all resources. + ** + ** Returns: None. + ** + *******************************************************************************/ + ~SyncEvent () + { + } + + + /******************************************************************************* + ** + ** Function: start + ** + ** Description: Start a synchronization operation. + ** + ** Returns: None. + ** + *******************************************************************************/ + void start () + { + mMutex.lock (); + } + + + /******************************************************************************* + ** + ** Function: wait + ** + ** Description: Block the thread and wait for the event to occur. + ** + ** Returns: None. + ** + *******************************************************************************/ + void wait () + { + mCondVar.wait (mMutex); + } + + + /******************************************************************************* + ** + ** Function: wait + ** + ** Description: Block the thread and wait for the event to occur. + ** millisec: Timeout in milliseconds. + ** + ** Returns: True if wait is successful; false if timeout occurs. + ** + *******************************************************************************/ + bool wait (long millisec) + { + bool retVal = mCondVar.wait (mMutex, millisec); + return retVal; + } + + + /******************************************************************************* + ** + ** Function: notifyOne + ** + ** Description: Notify a blocked thread that the event has occured. Unblocks it. + ** + ** Returns: None. + ** + *******************************************************************************/ + void notifyOne () + { + mCondVar.notifyOne (); + } + + + /******************************************************************************* + ** + ** Function: end + ** + ** Description: End a synchronization operation. + ** + ** Returns: None. + ** + *******************************************************************************/ + void end () + { + mMutex.unlock (); + } + +private: + CondVar mCondVar; + Mutex mMutex; +}; + + +/*****************************************************************************/ +/*****************************************************************************/ + + +/***************************************************************************** +** +** Name: SyncEventGuard +** +** Description: Automatically start and end a synchronization event. +** +*****************************************************************************/ +class SyncEventGuard +{ +public: + /******************************************************************************* + ** + ** Function: SyncEventGuard + ** + ** Description: Start a synchronization operation. + ** + ** Returns: None. + ** + *******************************************************************************/ + SyncEventGuard (SyncEvent& event) + : mEvent (event) + { + event.start (); //automatically start operation + }; + + + /******************************************************************************* + ** + ** Function: ~SyncEventGuard + ** + ** Description: End a synchronization operation. + ** + ** Returns: None. + ** + *******************************************************************************/ + ~SyncEventGuard () + { + mEvent.end (); //automatically end operation + }; + +private: + SyncEvent& mEvent; +}; + |