diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2012-12-13 16:24:26 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2012-12-13 16:24:26 -0800 |
commit | 525c260303268a83da4c3413b953d13c9084e834 (patch) | |
tree | 110a4a6bc2621f34fa2cf7f4c8ba2625598873aa | |
parent | 7a1d2adf875d414625853b3a5360663e420a3769 (diff) | |
download | packages_apps_nfc-525c260303268a83da4c3413b953d13c9084e834.zip packages_apps_nfc-525c260303268a83da4c3413b953d13c9084e834.tar.gz packages_apps_nfc-525c260303268a83da4c3413b953d13c9084e834.tar.bz2 |
Snapshot 1a6bcf3cca90fedfbad33c1cdd6d05af5774fc01
Change-Id: I3ccb25bf7cde2c22f52260cae0e9957517e6bb5f
60 files changed, 17536 insertions, 313 deletions
@@ -1,4 +1,8 @@ LOCAL_PATH:= $(call my-dir) + +######################################## +# NXP Configuration +######################################## include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional @@ -6,13 +10,8 @@ LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := \ $(call all-java-files-under, src) -ifeq ($(NFC_USE_NCI_STACK), true) - LOCAL_SRC_FILES += \ - $(call all-java-files-under, nci) -else - LOCAL_SRC_FILES += \ +LOCAL_SRC_FILES += \ $(call all-java-files-under, nxp) -endif LOCAL_PACKAGE_NAME := Nfc LOCAL_CERTIFICATE := platform @@ -25,6 +24,30 @@ LOCAL_PROGUARD_ENABLED := disabled include $(BUILD_PACKAGE) +######################################## +# NCI Configuration +######################################## +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src) + +LOCAL_SRC_FILES += \ + $(call all-java-files-under, nci) + +LOCAL_PACKAGE_NAME := NfcNci +LOCAL_OVERRIDES_PACKAGES := Nfc +LOCAL_CERTIFICATE := platform + +LOCAL_STATIC_JAVA_LIBRARIES := NfcLogTags + +LOCAL_JNI_SHARED_LIBRARIES := libnfc_nci_jni + +LOCAL_PROGUARD_ENABLED := disabled + +include $(BUILD_PACKAGE) ##### # static lib for the log tags diff --git a/AndroidManifest.xml b/AndroidManifest.xml index f0c027f..710e2ee 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -20,10 +20,12 @@ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> + <uses-permission android:name="android.permission.MANAGE_USERS" /> + <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" /> <uses-permission android:name="com.android.permission.WHITELIST_BLUETOOTH_DEVICE" /> <uses-permission android:name="com.android.permission.HANDOVER_STATUS" /> - - <application android:name=".NfcService" + <application android:name=".NfcApplication" android:icon="@drawable/icon" android:label="@string/app_name" android:persistent="true" diff --git a/nci/Android.mk b/nci/Android.mk new file mode 100644 index 0000000..34f4385 --- /dev/null +++ b/nci/Android.mk @@ -0,0 +1,3 @@ +LOCAL_PATH:= $(call my-dir) + +include $(call all-makefiles-under,$(LOCAL_PATH)) 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; +}; + diff --git a/nci/src/com/android/nfc/dhimpl/NativeLlcpConnectionlessSocket.java b/nci/src/com/android/nfc/dhimpl/NativeLlcpConnectionlessSocket.java new file mode 100755 index 0000000..db78496 --- /dev/null +++ b/nci/src/com/android/nfc/dhimpl/NativeLlcpConnectionlessSocket.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.nfc.dhimpl; + +import com.android.nfc.DeviceHost; +import com.android.nfc.LlcpPacket; + +import java.io.IOException; + +/** + * LlcpConnectionlessSocket represents a LLCP Connectionless object to be used + * in a connectionless communication + */ +public class NativeLlcpConnectionlessSocket implements DeviceHost.LlcpConnectionlessSocket { + + private int mHandle; + private int mSap; + private int mLinkMiu; + + public NativeLlcpConnectionlessSocket() { } + + public native boolean doSendTo(int sap, byte[] data); + + public native LlcpPacket doReceiveFrom(int linkMiu); + + public native boolean doClose(); + + @Override + public int getLinkMiu(){ + return mLinkMiu; + } + + @Override + public int getSap(){ + return mSap; + } + + @Override + public void send(int sap, byte[] data) throws IOException { + if (!doSendTo(sap, data)) { + throw new IOException(); + } + } + + @Override + public LlcpPacket receive() throws IOException { + LlcpPacket packet = doReceiveFrom(mLinkMiu); + if (packet == null) { + throw new IOException(); + } + return packet; + } + + public int getHandle(){ + return mHandle; + } + + @Override + public void close() throws IOException { + if (!doClose()) { + throw new IOException(); + } + } +} diff --git a/nci/src/com/android/nfc/dhimpl/NativeLlcpServiceSocket.java b/nci/src/com/android/nfc/dhimpl/NativeLlcpServiceSocket.java new file mode 100755 index 0000000..3a7e57f --- /dev/null +++ b/nci/src/com/android/nfc/dhimpl/NativeLlcpServiceSocket.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.nfc.dhimpl; + +import com.android.nfc.DeviceHost; +import com.android.nfc.DeviceHost.LlcpSocket; + +import java.io.IOException; + +/** + * LlcpServiceSocket represents a LLCP Service to be used in a + * Connection-oriented communication + */ +public class NativeLlcpServiceSocket implements DeviceHost.LlcpServerSocket { + private int mHandle; + private int mLocalMiu; + private int mLocalRw; + private int mLocalLinearBufferLength; + private int mSap; + private String mServiceName; + + public NativeLlcpServiceSocket(){ } + + private native NativeLlcpSocket doAccept(int miu, int rw, int linearBufferLength); + @Override + public LlcpSocket accept() throws IOException { + LlcpSocket socket = doAccept(mLocalMiu, mLocalRw, mLocalLinearBufferLength); + if (socket == null) throw new IOException(); + return socket; + } + + private native boolean doClose(); + @Override + public void close() throws IOException { + if (!doClose()) { + throw new IOException(); + } + } +} diff --git a/nci/src/com/android/nfc/dhimpl/NativeLlcpSocket.java b/nci/src/com/android/nfc/dhimpl/NativeLlcpSocket.java new file mode 100755 index 0000000..69506c5 --- /dev/null +++ b/nci/src/com/android/nfc/dhimpl/NativeLlcpSocket.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.nfc.dhimpl; + +import com.android.nfc.DeviceHost; + +import java.io.IOException; + +/** + * LlcpClientSocket represents a LLCP Connection-Oriented client to be used in a + * connection-oriented communication + */ +public class NativeLlcpSocket implements DeviceHost.LlcpSocket { + private int mHandle; + private int mSap; + private int mLocalMiu; + private int mLocalRw; + + public NativeLlcpSocket(){ } + + private native boolean doConnect(int nSap); + @Override + public void connectToSap(int sap) throws IOException { + if (!doConnect(sap)) { + throw new IOException(); + } + } + + private native boolean doConnectBy(String sn); + @Override + public void connectToService(String serviceName) throws IOException { + if (!doConnectBy(serviceName)) { + throw new IOException(); + } + } + + private native boolean doClose(); + @Override + public void close() throws IOException { + if (!doClose()) { + throw new IOException(); + } + } + + private native boolean doSend(byte[] data); + @Override + public void send(byte[] data) throws IOException { + if (!doSend(data)) { + throw new IOException(); + } + } + + private native int doReceive(byte[] recvBuff); + @Override + public int receive(byte[] recvBuff) throws IOException { + int receiveLength = doReceive(recvBuff); + if (receiveLength == -1) { + throw new IOException(); + } + return receiveLength; + } + + private native int doGetRemoteSocketMiu(); + @Override + public int getRemoteMiu() { return doGetRemoteSocketMiu(); } + + private native int doGetRemoteSocketRw(); + @Override + public int getRemoteRw() { return doGetRemoteSocketRw(); } + + @Override + public int getLocalSap(){ + return mSap; + } + + @Override + public int getLocalMiu(){ + return mLocalMiu; + } + + @Override + public int getLocalRw(){ + return mLocalRw; + } +} diff --git a/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java new file mode 100755 index 0000000..1437e5d --- /dev/null +++ b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.nfc.dhimpl; + +import com.android.nfc.DeviceHost; +import com.android.nfc.LlcpException; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.content.Context; +import android.content.SharedPreferences; +import android.nfc.ErrorCodes; +import android.nfc.tech.Ndef; +import android.nfc.tech.TagTechnology; +import android.util.Log; + +/** + * Native interface to the NFC Manager functions + */ +public class NativeNfcManager implements DeviceHost { + private static final String TAG = "NativeNfcManager"; + static final String PREF = "NciDeviceHost"; + + static final int DEFAULT_LLCP_MIU = 1980; + static final int DEFAULT_LLCP_RWSIZE = 2; + + static final String DRIVER_NAME = "android-nci"; + + private static final byte[][] EE_WIPE_APDUS = { + {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x00}, + {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x07, (byte)0xa0, (byte)0x00, + (byte)0x00, (byte)0x04, (byte)0x76, (byte)0x20, (byte)0x10, (byte)0x00}, + {(byte)0x80, (byte)0xe2, (byte)0x01, (byte)0x03, (byte)0x00}, + {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x00}, + {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x07, (byte)0xa0, (byte)0x00, + (byte)0x00, (byte)0x04, (byte)0x76, (byte)0x30, (byte)0x30, (byte)0x00}, + {(byte)0x80, (byte)0xb4, (byte)0x00, (byte)0x00, (byte)0x00}, + {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x00}, + }; + + static { + System.loadLibrary("nfc_nci_jni"); + } + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String INTERNAL_TARGET_DESELECTED_ACTION = "com.android.nfc.action.INTERNAL_TARGET_DESELECTED"; + + /* Native structure */ + private int mNative; + + private final DeviceHostListener mListener; + private final Context mContext; + + public NativeNfcManager(Context context, DeviceHostListener listener) { + mListener = listener; + initializeNativeStructure(); + mContext = context; + } + + public native boolean initializeNativeStructure(); + + private native boolean doDownload(); + + public native int doGetLastError(); + + @Override + public void checkFirmware() { + } + + private native boolean doInitialize(); + + @Override + public boolean initialize() { + SharedPreferences prefs = mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + + if (prefs.getBoolean(NativeNfcSecureElement.PREF_SE_WIRED, false)) { + try { + Thread.sleep (12000); + editor.putBoolean(NativeNfcSecureElement.PREF_SE_WIRED, false); + editor.apply(); + } catch (InterruptedException e) { } + } + + return doInitialize(); + } + + private native boolean doDeinitialize(); + + @Override + public boolean deinitialize() { + SharedPreferences prefs = mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + + editor.putBoolean(NativeNfcSecureElement.PREF_SE_WIRED, false); + editor.apply(); + + return doDeinitialize(); + } + + @Override + public String getName() { + return DRIVER_NAME; + } + + @Override + public native void enableDiscovery(); + + @Override + public native void disableDiscovery(); + + @Override + public native int[] doGetSecureElementList(); + + @Override + public native void doSelectSecureElement(); + + @Override + public native void doDeselectSecureElement(); + + + private native NativeLlcpConnectionlessSocket doCreateLlcpConnectionlessSocket(int nSap, + String sn); + + @Override + public LlcpConnectionlessSocket createLlcpConnectionlessSocket(int nSap, String sn) + throws LlcpException { + LlcpConnectionlessSocket socket = doCreateLlcpConnectionlessSocket(nSap, sn); + if (socket != null) { + return socket; + } else { + /* Get Error Status */ + int error = doGetLastError(); + + Log.d(TAG, "failed to create llcp socket: " + ErrorCodes.asString(error)); + + switch (error) { + case ErrorCodes.ERROR_BUFFER_TO_SMALL: + case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES: + throw new LlcpException(error); + default: + throw new LlcpException(ErrorCodes.ERROR_SOCKET_CREATION); + } + } + } + + private native NativeLlcpServiceSocket doCreateLlcpServiceSocket(int nSap, String sn, int miu, + int rw, int linearBufferLength); + @Override + public LlcpServerSocket createLlcpServerSocket(int nSap, String sn, int miu, + int rw, int linearBufferLength) throws LlcpException { + LlcpServerSocket socket = doCreateLlcpServiceSocket(nSap, sn, miu, rw, linearBufferLength); + if (socket != null) { + return socket; + } else { + /* Get Error Status */ + int error = doGetLastError(); + + Log.d(TAG, "failed to create llcp socket: " + ErrorCodes.asString(error)); + + switch (error) { + case ErrorCodes.ERROR_BUFFER_TO_SMALL: + case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES: + throw new LlcpException(error); + default: + throw new LlcpException(ErrorCodes.ERROR_SOCKET_CREATION); + } + } + } + + private native NativeLlcpSocket doCreateLlcpSocket(int sap, int miu, int rw, + int linearBufferLength); + @Override + public LlcpSocket createLlcpSocket(int sap, int miu, int rw, + int linearBufferLength) throws LlcpException { + LlcpSocket socket = doCreateLlcpSocket(sap, miu, rw, linearBufferLength); + if (socket != null) { + return socket; + } else { + /* Get Error Status */ + int error = doGetLastError(); + + Log.d(TAG, "failed to create llcp socket: " + ErrorCodes.asString(error)); + + switch (error) { + case ErrorCodes.ERROR_BUFFER_TO_SMALL: + case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES: + throw new LlcpException(error); + default: + throw new LlcpException(ErrorCodes.ERROR_SOCKET_CREATION); + } + } + } + + @Override + public native boolean doCheckLlcp(); + + @Override + public native boolean doActivateLlcp(); + + private native void doResetTimeouts(); + + @Override + public void resetTimeouts() { + doResetTimeouts(); + } + + @Override + public native void doAbort(); + + private native boolean doSetTimeout(int tech, int timeout); + @Override + public boolean setTimeout(int tech, int timeout) { + return doSetTimeout(tech, timeout); + } + + private native int doGetTimeout(int tech); + @Override + public int getTimeout(int tech) { + return doGetTimeout(tech); + } + + + @Override + public boolean canMakeReadOnly(int ndefType) { + return (ndefType == Ndef.TYPE_1 || ndefType == Ndef.TYPE_2); + } + + @Override + public int getMaxTransceiveLength(int technology) { + switch (technology) { + case (TagTechnology.NFC_A): + case (TagTechnology.MIFARE_CLASSIC): + case (TagTechnology.MIFARE_ULTRALIGHT): + return 253; // PN544 RF buffer = 255 bytes, subtract two for CRC + case (TagTechnology.NFC_B): + ///////////////////////////////////////////////////////////////// + // Broadcom: Since BCM2079x supports this, set NfcB max size. + //return 0; // PN544 does not support transceive of raw NfcB + return 253; // PN544 does not support transceive of raw NfcB + case (TagTechnology.NFC_V): + return 253; // PN544 RF buffer = 255 bytes, subtract two for CRC + case (TagTechnology.ISO_DEP): + /* The maximum length of a normal IsoDep frame consists of: + * CLA, INS, P1, P2, LC, LE + 255 payload bytes = 261 bytes + * such a frame is supported. Extended length frames however + * are not supported. + */ + return 261; // Will be automatically split in two frames on the RF layer + case (TagTechnology.NFC_F): + return 252; // PN544 RF buffer = 255 bytes, subtract one for SoD, two for CRC + default: + return 0; + } + + } + + private native void doSetP2pInitiatorModes(int modes); + @Override + public void setP2pInitiatorModes(int modes) { + doSetP2pInitiatorModes(modes); + } + + private native void doSetP2pTargetModes(int modes); + @Override + public void setP2pTargetModes(int modes) { + doSetP2pTargetModes(modes); + } + @Override + public boolean getExtendedLengthApdusSupported() { + // TODO check BCM support + return false; + } + + @Override + public boolean enablePN544Quirks() { + return false; + } + + @Override + public byte[][] getWipeApdus() { + return EE_WIPE_APDUS; + } + + @Override + public int getDefaultLlcpMiu() { + return DEFAULT_LLCP_MIU; + } + + @Override + public int getDefaultLlcpRwSize() { + return DEFAULT_LLCP_RWSIZE; + } + + private native String doDump(); + @Override + public String dump() { + return doDump(); + } + + /** + * Notifies Ndef Message (TODO: rename into notifyTargetDiscovered) + */ + private void notifyNdefMessageListeners(NativeNfcTag tag) { + mListener.onRemoteEndpointDiscovered(tag); + } + + /** + * Notifies transaction + */ + private void notifyTargetDeselected() { + mListener.onCardEmulationDeselected(); + } + + /** + * Notifies transaction + */ + private void notifyTransactionListeners(byte[] aid) { + mListener.onCardEmulationAidSelected(aid); + } + + /** + * Notifies P2P Device detected, to activate LLCP link + */ + private void notifyLlcpLinkActivation(NativeP2pDevice device) { + mListener.onLlcpLinkActivated(device); + } + + /** + * Notifies P2P Device detected, to activate LLCP link + */ + private void notifyLlcpLinkDeactivated(NativeP2pDevice device) { + mListener.onLlcpLinkDeactivated(device); + } + + private void notifySeFieldActivated() { + mListener.onRemoteFieldActivated(); + } + + private void notifySeFieldDeactivated() { + mListener.onRemoteFieldDeactivated(); + } + + private void notifySeListenActivated() { + mListener.onSeListenActivated(); + } + + private void notifySeListenDeactivated() { + mListener.onSeListenDeactivated(); + } + + private void notifySeApduReceived(byte[] apdu) { + mListener.onSeApduReceived(apdu); + } + + private void notifySeEmvCardRemoval() { + mListener.onSeEmvCardRemoval(); + } + + private void notifySeMifareAccess(byte[] block) { + mListener.onSeMifareAccess(block); + } + +} diff --git a/nci/src/com/android/nfc/dhimpl/NativeNfcSecureElement.java b/nci/src/com/android/nfc/dhimpl/NativeNfcSecureElement.java new file mode 100755 index 0000000..e2d91ec --- /dev/null +++ b/nci/src/com/android/nfc/dhimpl/NativeNfcSecureElement.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.nfc.dhimpl; + +import android.content.Context; +import android.content.SharedPreferences; + + +/** + * Native interface to the NFC Secure Element functions + * + * {@hide} + */ +public class NativeNfcSecureElement { + + static final String PREF_SE_WIRED = "se_wired"; + + private final Context mContext; + + SharedPreferences mPrefs; + SharedPreferences.Editor mPrefsEditor; + + public NativeNfcSecureElement(Context context) { + mContext = context; + + mPrefs = mContext.getSharedPreferences(NativeNfcManager.PREF, Context.MODE_PRIVATE); + mPrefsEditor = mPrefs.edit(); + } + + private native int doNativeOpenSecureElementConnection(); + + public int doOpenSecureElementConnection() { + mPrefsEditor.putBoolean(PREF_SE_WIRED, true); + mPrefsEditor.apply(); + + return doNativeOpenSecureElementConnection(); + } + + private native boolean doNativeDisconnectSecureElementConnection(int handle); + + public boolean doDisconnect(int handle) { + mPrefsEditor.putBoolean(PREF_SE_WIRED, false); + mPrefsEditor.apply(); + + return doNativeDisconnectSecureElementConnection(handle); + } + + public native byte[] doTransceive(int handle, byte[] data); + + public native int[] doGetTechList(int handle); + + public native byte [] doGetUid(int handle); +} diff --git a/nci/src/com/android/nfc/dhimpl/NativeNfcTag.java b/nci/src/com/android/nfc/dhimpl/NativeNfcTag.java new file mode 100755 index 0000000..eb8410f --- /dev/null +++ b/nci/src/com/android/nfc/dhimpl/NativeNfcTag.java @@ -0,0 +1,811 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.nfc.dhimpl; + +import com.android.nfc.DeviceHost.TagEndpoint; + +import android.nfc.FormatException; +import android.nfc.NdefMessage; +import android.nfc.tech.IsoDep; +import android.nfc.tech.MifareClassic; +import android.nfc.tech.MifareUltralight; +import android.nfc.tech.Ndef; +import android.nfc.tech.NfcA; +import android.nfc.tech.NfcB; +import android.nfc.tech.NfcF; +import android.nfc.tech.NfcV; +import android.nfc.tech.TagTechnology; +import android.os.Bundle; +import android.util.Log; + +/** + * Native interface to the NFC tag functions + */ +public class NativeNfcTag implements TagEndpoint { + static final boolean DBG = true; + + static final int STATUS_CODE_TARGET_LOST = 146; + + private int[] mTechList; + private int[] mTechHandles; + private int[] mTechLibNfcTypes; + private Bundle[] mTechExtras; + private byte[][] mTechPollBytes; + private byte[][] mTechActBytes; + private byte[] mUid; + + // mConnectedHandle stores the *real* libnfc handle + // that we're connected to. + private int mConnectedHandle; + + // mConnectedTechIndex stores to which technology + // the upper layer stack is connected. Note that + // we may be connected to a libnfchandle without being + // connected to a technology - technology changes + // may occur runtime, whereas the underlying handle + // could stay present. Usually all technologies are on the + // same handle, with the exception of multi-protocol + // tags. + private int mConnectedTechIndex; // Index in mTechHandles + + private final String TAG = "NativeNfcTag"; + + private boolean mIsPresent; // Whether the tag is known to be still present + + private PresenceCheckWatchdog mWatchdog; + class PresenceCheckWatchdog extends Thread { + + private int watchdogTimeout = 125; + + private boolean isPresent = true; + private boolean isStopped = false; + private boolean isPaused = false; + private boolean doCheck = true; + + public synchronized void pause() { + isPaused = true; + doCheck = false; + this.notifyAll(); + } + + public synchronized void doResume() { + isPaused = false; + // We don't want to resume presence checking immediately, + // but go through at least one more wait period. + doCheck = false; + this.notifyAll(); + } + + public synchronized void end() { + isStopped = true; + doCheck = false; + this.notifyAll(); + } + + public synchronized void setTimeout(int timeout) { + watchdogTimeout = timeout; + doCheck = false; // Do it only after we have waited "timeout" ms again + this.notifyAll(); + } + + @Override + public synchronized void run() { + if (DBG) Log.d(TAG, "Starting background presence check"); + while (isPresent && !isStopped) { + try { + if (!isPaused) { + doCheck = true; + } + this.wait(watchdogTimeout); + if (doCheck) { + isPresent = doPresenceCheck(); + } else { + // 1) We are paused, waiting for unpause + // 2) We just unpaused, do pres check in next iteration + // (after watchdogTimeout ms sleep) + // 3) We just set the timeout, wait for this timeout + // to expire once first. + // 4) We just stopped, exit loop anyway + } + } catch (InterruptedException e) { + // Activity detected, loop + } + } + mIsPresent = false; + // Restart the polling loop + + Log.d(TAG, "Tag lost, restarting polling loop"); + doDisconnect(); + if (DBG) Log.d(TAG, "Stopping background presence check"); + } + } + + private native int doConnect(int handle); + public synchronized int connectWithStatus(int technology) { + if (mWatchdog != null) { + mWatchdog.pause(); + } + int status = -1; + for (int i = 0; i < mTechList.length; i++) { + if (mTechList[i] == technology) { + // Get the handle and connect, if not already connected + if (mConnectedHandle != mTechHandles[i]) { + // We're not yet connected to this handle, there are + // a few scenario's here: + // 1) We are not connected to anything yet - allow + // 2) We are connected to a technology which has + // a different handle (multi-protocol tag); we support + // switching to that. + if (mConnectedHandle == -1) { + // Not connected yet + //status = doConnect(mTechHandles[i]); + status = doConnect(i); + } else { + // Connect to a tech with a different handle + Log.d(TAG,"Connect to a tech with a different handle"); + //status = reconnectWithStatus(mTechHandles[i]); + status = reconnectWithStatus(i); + } + if (status == 0) { + mConnectedHandle = mTechHandles[i]; + mConnectedTechIndex = i; + } + } else { + // 1) We are connected to a technology which has the same + // handle; we do not support connecting at a different + // level (libnfc auto-activates to the max level on + // any handle). + // 2) We are connecting to the ndef technology - always + // allowed. + if ((technology == TagTechnology.NDEF) || + (technology == TagTechnology.NDEF_FORMATABLE)) { + // special case for NDEF, this will cause switch to ISO_DEP frame intf + i = 0; + // status = 0; + } + status = reconnectWithStatus(i); + /* + if ((technology != TagTechnology.ISO_DEP) && + (hasTechOnHandle(TagTechnology.ISO_DEP, mTechHandles[i]))) { + // Don't allow to connect a -4 tag at a different level + // than IsoDep, as this is not supported by + // libNFC. + // revised for NFCA... do allow to connect a -4 tag at this level. + Log.d(TAG,"Connect to a tech with same different handle (rf intf change)"); + status = reconnectWithStatus(i); + if (status == 0) { + mConnectedHandle = mTechHandles[i]; + mConnectedTechIndex = i; + } + //status = 0; + } else { + status = 0; + } + */ + + + if (status == 0) { + mConnectedTechIndex = i; + // Handle was already identical + } + } + break; + } + } + if (mWatchdog != null) { + mWatchdog.doResume(); + } + return status; + } + @Override + public synchronized boolean connect(int technology) { + return connectWithStatus(technology) == 0; + } + + @Override + public synchronized void startPresenceChecking() { + // Once we start presence checking, we allow the upper layers + // to know the tag is in the field. + mIsPresent = true; + if (mWatchdog == null) { + mWatchdog = new PresenceCheckWatchdog(); + mWatchdog.start(); + } + } + + @Override + public synchronized boolean isPresent() { + // Returns whether the tag is still in the field to the best + // of our knowledge. + return mIsPresent; + } + native boolean doDisconnect(); + @Override + public synchronized boolean disconnect() { + boolean result = false; + + mIsPresent = false; + if (mWatchdog != null) { + // Watchdog has already disconnected or will do it + mWatchdog.end(); + try { + mWatchdog.join(); + } catch (InterruptedException e) { + // Should never happen. + } + mWatchdog = null; + result = true; + } else { + result = doDisconnect(); + } + + mConnectedTechIndex = -1; + mConnectedHandle = -1; + return result; + } + + native int doReconnect(); + public synchronized int reconnectWithStatus() { + if (mWatchdog != null) { + mWatchdog.pause(); + } + int status = doReconnect(); + if (mWatchdog != null) { + mWatchdog.doResume(); + } + return status; + } + @Override + public synchronized boolean reconnect() { + return reconnectWithStatus() == 0; + } + + native int doHandleReconnect(int handle); + public synchronized int reconnectWithStatus(int handle) { + if (mWatchdog != null) { + mWatchdog.pause(); + } + int status = doHandleReconnect(handle); + if (mWatchdog != null) { + mWatchdog.doResume(); + } + return status; + } + + private native byte[] doTransceive(byte[] data, boolean raw, int[] returnCode); + @Override + public synchronized byte[] transceive(byte[] data, boolean raw, int[] returnCode) { + if (mWatchdog != null) { + mWatchdog.pause(); + } + byte[] result = doTransceive(data, raw, returnCode); + if (mWatchdog != null) { + mWatchdog.doResume(); + } + return result; + } + + private native int doCheckNdef(int[] ndefinfo); + private synchronized int checkNdefWithStatus(int[] ndefinfo) { + if (mWatchdog != null) { + mWatchdog.pause(); + } + int status = doCheckNdef(ndefinfo); + if (mWatchdog != null) { + mWatchdog.doResume(); + } + return status; + } + @Override + public synchronized boolean checkNdef(int[] ndefinfo) { + return checkNdefWithStatus(ndefinfo) == 0; + } + + private native byte[] doRead(); + @Override + public synchronized byte[] readNdef() { + if (mWatchdog != null) { + mWatchdog.pause(); + } + byte[] result = doRead(); + if (mWatchdog != null) { + mWatchdog.doResume(); + } + return result; + } + + private native boolean doWrite(byte[] buf); + @Override + public synchronized boolean writeNdef(byte[] buf) { + if (mWatchdog != null) { + mWatchdog.pause(); + } + boolean result = doWrite(buf); + if (mWatchdog != null) { + mWatchdog.doResume(); + } + return result; + } + + native boolean doPresenceCheck(); + @Override + public synchronized boolean presenceCheck() { + if (mWatchdog != null) { + mWatchdog.pause(); + } + boolean result = doPresenceCheck(); + if (mWatchdog != null) { + mWatchdog.doResume(); + } + return result; + } + + native boolean doNdefFormat(byte[] key); + @Override + public synchronized boolean formatNdef(byte[] key) { + if (mWatchdog != null) { + mWatchdog.pause(); + } + boolean result = doNdefFormat(key); + if (mWatchdog != null) { + mWatchdog.doResume(); + } + return result; + } + + native boolean doMakeReadonly(byte[] key); + @Override + public synchronized boolean makeReadOnly() { + if (mWatchdog != null) { + mWatchdog.pause(); + } + boolean result; + if (hasTech(TagTechnology.MIFARE_CLASSIC)) { + result = doMakeReadonly(MifareClassic.KEY_DEFAULT); + } else { + // No key needed for other technologies + result = doMakeReadonly(new byte[] {}); + } + if (mWatchdog != null) { + mWatchdog.doResume(); + } + return result; + } + + native boolean doIsIsoDepNdefFormatable(byte[] poll, byte[] act); + @Override + public synchronized boolean isNdefFormatable() { + // Let native code decide whether the currently activated tag + // is formatable. Although the name of the JNI function refers + // to ISO-DEP, the JNI function checks all tag types. + return doIsIsoDepNdefFormatable(mTechPollBytes[0], + mTechActBytes[0]); + } + + @Override + public int getHandle() { + // This is just a handle for the clients; it can simply use the first + // technology handle we have. + if (mTechHandles.length > 0) { + return mTechHandles[0]; + } else { + return 0; + } + } + + @Override + public byte[] getUid() { + return mUid; + } + + @Override + public int[] getTechList() { + return mTechList; + } + + private int getConnectedHandle() { + return mConnectedHandle; + } + + private int getConnectedLibNfcType() { + if (mConnectedTechIndex != -1 && mConnectedTechIndex < mTechLibNfcTypes.length) { + return mTechLibNfcTypes[mConnectedTechIndex]; + } else { + return 0; + } + } + + @Override + public int getConnectedTechnology() { + if (mConnectedTechIndex != -1 && mConnectedTechIndex < mTechList.length) { + return mTechList[mConnectedTechIndex]; + } else { + return 0; + } + } + native int doGetNdefType(int libnfctype, int javatype); + private int getNdefType(int libnfctype, int javatype) { + return doGetNdefType(libnfctype, javatype); + } + + private void addTechnology(int tech, int handle, int libnfctype) { + int[] mNewTechList = new int[mTechList.length + 1]; + System.arraycopy(mTechList, 0, mNewTechList, 0, mTechList.length); + mNewTechList[mTechList.length] = tech; + mTechList = mNewTechList; + + int[] mNewHandleList = new int[mTechHandles.length + 1]; + System.arraycopy(mTechHandles, 0, mNewHandleList, 0, mTechHandles.length); + mNewHandleList[mTechHandles.length] = handle; + mTechHandles = mNewHandleList; + + int[] mNewTypeList = new int[mTechLibNfcTypes.length + 1]; + System.arraycopy(mTechLibNfcTypes, 0, mNewTypeList, 0, mTechLibNfcTypes.length); + mNewTypeList[mTechLibNfcTypes.length] = libnfctype; + mTechLibNfcTypes = mNewTypeList; + } + + @Override + public void removeTechnology(int tech) { + synchronized (this) { + int techIndex = getTechIndex(tech); + if (techIndex != -1) { + int[] mNewTechList = new int[mTechList.length - 1]; + System.arraycopy(mTechList, 0, mNewTechList, 0, techIndex); + System.arraycopy(mTechList, techIndex + 1, mNewTechList, techIndex, + mTechList.length - techIndex - 1); + mTechList = mNewTechList; + + int[] mNewHandleList = new int[mTechHandles.length - 1]; + System.arraycopy(mTechHandles, 0, mNewHandleList, 0, techIndex); + System.arraycopy(mTechHandles, techIndex + 1, mNewTechList, techIndex, + mTechHandles.length - techIndex - 1); + mTechHandles = mNewHandleList; + + int[] mNewTypeList = new int[mTechLibNfcTypes.length - 1]; + System.arraycopy(mTechLibNfcTypes, 0, mNewTypeList, 0, techIndex); + System.arraycopy(mTechLibNfcTypes, techIndex + 1, mNewTypeList, techIndex, + mTechLibNfcTypes.length - techIndex - 1); + mTechLibNfcTypes = mNewTypeList; + + //The technology must be removed from the mTechExtras array, + //just like the above arrays. + //Remove the specified element from the array, + //then shift the remaining elements by one. + if (mTechExtras != null) + { + Bundle[] mNewTechExtras = new Bundle[mTechExtras.length - 1]; + System.arraycopy(mTechExtras, 0, mNewTechExtras, 0, techIndex); + System.arraycopy(mTechExtras, techIndex + 1, mNewTechExtras, techIndex, + mTechExtras.length - techIndex - 1); + mTechExtras = mNewTechExtras; + } + } + } + } + + public void addNdefFormatableTechnology(int handle, int libnfcType) { + synchronized (this) { + addTechnology(TagTechnology.NDEF_FORMATABLE, handle, libnfcType); + } + } + + // This method exists to "patch in" the ndef technologies, + // which is done inside Java instead of the native JNI code. + // To not create some nasty dependencies on the order on which things + // are called (most notably getTechExtras()), it needs some additional + // checking. + public void addNdefTechnology(NdefMessage msg, int handle, int libnfcType, + int javaType, int maxLength, int cardState) { + synchronized (this) { + addTechnology(TagTechnology.NDEF, handle, libnfcType); + + Bundle extras = new Bundle(); + extras.putParcelable(Ndef.EXTRA_NDEF_MSG, msg); + extras.putInt(Ndef.EXTRA_NDEF_MAXLENGTH, maxLength); + extras.putInt(Ndef.EXTRA_NDEF_CARDSTATE, cardState); + extras.putInt(Ndef.EXTRA_NDEF_TYPE, getNdefType(libnfcType, javaType)); + + if (mTechExtras == null) { + // This will build the tech extra's for the first time, + // including a NULL ref for the NDEF tech we generated above. + Bundle[] builtTechExtras = getTechExtras(); + builtTechExtras[builtTechExtras.length - 1] = extras; + } + else { + // Tech extras were built before, patch the NDEF one in + Bundle[] oldTechExtras = getTechExtras(); + Bundle[] newTechExtras = new Bundle[oldTechExtras.length + 1]; + System.arraycopy(oldTechExtras, 0, newTechExtras, 0, oldTechExtras.length); + newTechExtras[oldTechExtras.length] = extras; + mTechExtras = newTechExtras; + } + + + } + } + + private int getTechIndex(int tech) { + int techIndex = -1; + for (int i = 0; i < mTechList.length; i++) { + if (mTechList[i] == tech) { + techIndex = i; + break; + } + } + return techIndex; + } + + private boolean hasTech(int tech) { + boolean hasTech = false; + for (int i = 0; i < mTechList.length; i++) { + if (mTechList[i] == tech) { + hasTech = true; + break; + } + } + return hasTech; + } + + private boolean hasTechOnHandle(int tech, int handle) { + boolean hasTech = false; + for (int i = 0; i < mTechList.length; i++) { + if (mTechList[i] == tech && mTechHandles[i] == handle) { + hasTech = true; + break; + } + } + return hasTech; + + } + + private boolean isUltralightC() { + /* Make a best-effort attempt at classifying ULTRALIGHT + * vs ULTRALIGHT-C (based on NXP's public AN1303). + * The memory layout is as follows: + * Page # BYTE1 BYTE2 BYTE3 BYTE4 + * 2 INT1 INT2 LOCK LOCK + * 3 OTP OTP OTP OTP (NDEF CC if NDEF-formatted) + * 4 DATA DATA DATA DATA (version info if factory-state) + * + * Read four blocks from page 2, which will get us both + * the lock page, the OTP page and the version info. + */ + boolean isUltralightC = false; + byte[] readCmd = { 0x30, 0x02 }; + int[] retCode = new int[2]; + byte[] respData = transceive(readCmd, false, retCode); + if (respData != null && respData.length == 16) { + // Check the lock bits (last 2 bytes in page2) + // and the OTP bytes (entire page 3) + if (respData[2] == 0 && respData[3] == 0 && respData[4] == 0 && + respData[5] == 0 && respData[6] == 0 && respData[7] == 0) { + // Very likely to be a blank card, look at version info + // in page 4. + if ((respData[8] == (byte)0x02) && respData[9] == (byte)0x00) { + // This is Ultralight-C + isUltralightC = true; + } else { + // 0xFF 0xFF would indicate Ultralight, but we also use Ultralight + // as a fallback if it's anything else + isUltralightC = false; + } + } else { + // See if we can find the NDEF CC in the OTP page and if it's + // smaller than major version two + if (respData[4] == (byte)0xE1 && ((respData[5] & 0xff) < 0x20)) { + // OK, got NDEF. Technically we'd have to search for the + // NDEF TLV as well. However, this would add too much + // time for discovery and we can make already make a good guess + // with the data we have here. Byte 2 of the OTP page + // indicates the size of the tag - 0x06 is UL, anything + // above indicates UL-C. + if ((respData[6] & 0xff) > 0x06) { + isUltralightC = true; + } + } else { + // Fall back to ultralight + isUltralightC = false; + } + } + } + return isUltralightC; + } + + @Override + public Bundle[] getTechExtras() { + synchronized (this) { + if (mTechExtras != null) return mTechExtras; + mTechExtras = new Bundle[mTechList.length]; + for (int i = 0; i < mTechList.length; i++) { + Bundle extras = new Bundle(); + switch (mTechList[i]) { + case TagTechnology.NFC_A: { + byte[] actBytes = mTechActBytes[i]; + if ((actBytes != null) && (actBytes.length > 0)) { + extras.putShort(NfcA.EXTRA_SAK, (short) (actBytes[0] & (short) 0xFF)); + } else { + // Unfortunately Jewel doesn't have act bytes, + // ignore this case. + } + extras.putByteArray(NfcA.EXTRA_ATQA, mTechPollBytes[i]); + break; + } + + case TagTechnology.NFC_B: { + // What's returned from the PN544 is actually: + // 4 bytes app data + // 3 bytes prot info + byte[] appData = new byte[4]; + byte[] protInfo = new byte[3]; + if (mTechPollBytes[i].length >= 7) { + System.arraycopy(mTechPollBytes[i], 0, appData, 0, 4); + System.arraycopy(mTechPollBytes[i], 4, protInfo, 0, 3); + + extras.putByteArray(NfcB.EXTRA_APPDATA, appData); + extras.putByteArray(NfcB.EXTRA_PROTINFO, protInfo); + } + break; + } + + case TagTechnology.NFC_F: { + byte[] pmm = new byte[8]; + byte[] sc = new byte[2]; + if (mTechPollBytes[i].length >= 8) { + // At least pmm is present + System.arraycopy(mTechPollBytes[i], 0, pmm, 0, 8); + extras.putByteArray(NfcF.EXTRA_PMM, pmm); + } + if (mTechPollBytes[i].length == 10) { + System.arraycopy(mTechPollBytes[i], 8, sc, 0, 2); + extras.putByteArray(NfcF.EXTRA_SC, sc); + } + break; + } + + case TagTechnology.ISO_DEP: { + if (hasTech(TagTechnology.NFC_A)) { + extras.putByteArray(IsoDep.EXTRA_HIST_BYTES, mTechActBytes[i]); + } + else { + extras.putByteArray(IsoDep.EXTRA_HI_LAYER_RESP, mTechActBytes[i]); + } + break; + } + + case TagTechnology.NFC_V: { + // First byte response flags, second byte DSFID + if (mTechPollBytes[i] != null && mTechPollBytes[i].length >= 2) { + extras.putByte(NfcV.EXTRA_RESP_FLAGS, mTechPollBytes[i][0]); + extras.putByte(NfcV.EXTRA_DSFID, mTechPollBytes[i][1]); + } + break; + } + + case TagTechnology.MIFARE_ULTRALIGHT: { + boolean isUlc = isUltralightC(); + extras.putBoolean(MifareUltralight.EXTRA_IS_UL_C, isUlc); + break; + } + + default: { + // Leave the entry in the array null + continue; + } + } + mTechExtras[i] = extras; + } + return mTechExtras; + } + } + + @Override + public NdefMessage findAndReadNdef() { + // Try to find NDEF on any of the technologies. + int[] technologies = getTechList(); + int[] handles = mTechHandles; + NdefMessage ndefMsg = null; + boolean foundFormattable = false; + int formattableHandle = 0; + int formattableLibNfcType = 0; + int status; + + for (int techIndex = 0; techIndex < technologies.length; techIndex++) { + // have we seen this handle before? + for (int i = 0; i < techIndex; i++) { + if (handles[i] == handles[techIndex]) { + continue; // don't check duplicate handles + } + } + + status = connectWithStatus(technologies[techIndex]); + if (status != 0) { + Log.d(TAG, "Connect Failed - status = "+ status); + if (status == STATUS_CODE_TARGET_LOST) { + break; + } + continue; // try next handle + } + // Check if this type is NDEF formatable + if (!foundFormattable) { + if (isNdefFormatable()) { + foundFormattable = true; + formattableHandle = getConnectedHandle(); + formattableLibNfcType = getConnectedLibNfcType(); + // We'll only add formattable tech if no ndef is + // found - this is because libNFC refuses to format + // an already NDEF formatted tag. + } + reconnect(); + } + + int[] ndefinfo = new int[2]; + status = checkNdefWithStatus(ndefinfo); + if (status != 0) { + Log.d(TAG, "Check NDEF Failed - status = " + status); + if (status == STATUS_CODE_TARGET_LOST) { + break; + } + continue; // try next handle + } + + // found our NDEF handle + boolean generateEmptyNdef = false; + + int supportedNdefLength = ndefinfo[0]; + int cardState = ndefinfo[1]; + byte[] buff = readNdef(); + if (buff != null) { + try { + ndefMsg = new NdefMessage(buff); + addNdefTechnology(ndefMsg, + getConnectedHandle(), + getConnectedLibNfcType(), + getConnectedTechnology(), + supportedNdefLength, cardState); + reconnect(); + } catch (FormatException e) { + // Create an intent anyway, without NDEF messages + generateEmptyNdef = true; + } + } else { + generateEmptyNdef = true; + } + + if (generateEmptyNdef) { + ndefMsg = null; + addNdefTechnology(null, + getConnectedHandle(), + getConnectedLibNfcType(), + getConnectedTechnology(), + supportedNdefLength, cardState); + reconnect(); + } + break; + } + + if (ndefMsg == null && foundFormattable) { + // Tag is not NDEF yet, and found a formattable target, + // so add formattable tech to tech list. + addNdefFormatableTechnology( + formattableHandle, + formattableLibNfcType); + } + + return ndefMsg; + } +} diff --git a/nci/src/com/android/nfc/dhimpl/NativeP2pDevice.java b/nci/src/com/android/nfc/dhimpl/NativeP2pDevice.java new file mode 100755 index 0000000..094f46a --- /dev/null +++ b/nci/src/com/android/nfc/dhimpl/NativeP2pDevice.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.nfc.dhimpl; + +import com.android.nfc.DeviceHost.NfcDepEndpoint; + +/** + * Native interface to the P2P Initiator functions + */ +public class NativeP2pDevice implements NfcDepEndpoint { + + private int mHandle; + + private int mMode; + + private byte[] mGeneralBytes; + + private native byte[] doReceive(); + @Override + public byte[] receive() { + return doReceive(); + } + + private native boolean doSend(byte[] data); + @Override + public boolean send(byte[] data) { + return doSend(data); + } + + private native boolean doConnect(); + @Override + public boolean connect() { + return doConnect(); + } + + private native boolean doDisconnect(); + @Override + public boolean disconnect() { + return doDisconnect(); + } + + public native byte[] doTransceive(byte[] data); + @Override + public byte[] transceive(byte[] data) { + return doTransceive(data); + } + + @Override + public int getHandle() { + return mHandle; + } + + @Override + public int getMode() { + return mMode; + } + + @Override + public byte[] getGeneralBytes() { + return mGeneralBytes; + } + +} diff --git a/nxp/src/com/android/nfc/dhimpl/NativeNfcManager.java b/nxp/src/com/android/nfc/dhimpl/NativeNfcManager.java index f969627..dc6ea7c 100755 --- a/nxp/src/com/android/nfc/dhimpl/NativeNfcManager.java +++ b/nxp/src/com/android/nfc/dhimpl/NativeNfcManager.java @@ -43,6 +43,25 @@ public class NativeNfcManager implements DeviceHost { private static final String PREF_FIRMWARE_MODTIME = "firmware_modtime"; private static final long FIRMWARE_MODTIME_DEFAULT = -1; + static final String DRIVER_NAME = "nxp"; + + static final int DEFAULT_LLCP_MIU = 128; + static final int DEFAULT_LLCP_RWSIZE = 1; + + //TODO: dont hardcode this + private static final byte[][] EE_WIPE_APDUS = { + {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x00}, + {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x07, (byte)0xa0, (byte)0x00, + (byte)0x00, (byte)0x04, (byte)0x76, (byte)0x20, (byte)0x10, (byte)0x00}, + {(byte)0x80, (byte)0xe2, (byte)0x01, (byte)0x03, (byte)0x00}, + {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x00}, + {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x07, (byte)0xa0, (byte)0x00, + (byte)0x00, (byte)0x04, (byte)0x76, (byte)0x30, (byte)0x30, (byte)0x00}, + {(byte)0x80, (byte)0xb4, (byte)0x00, (byte)0x00, (byte)0x00}, + {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x00}, + }; + + static { System.loadLibrary("nfc_jni"); } @@ -144,6 +163,11 @@ public class NativeNfcManager implements DeviceHost { } @Override + public String getName() { + return DRIVER_NAME; + } + + @Override public native void enableDiscovery(); @Override @@ -305,11 +329,32 @@ public class NativeNfcManager implements DeviceHost { doSetP2pTargetModes(modes); } + @Override public boolean getExtendedLengthApdusSupported() { // Not supported on the PN544 return false; } + @Override + public boolean enablePN544Quirks() { + return true; + } + + @Override + public byte[][] getWipeApdus() { + return EE_WIPE_APDUS; + } + + @Override + public int getDefaultLlcpMiu() { + return DEFAULT_LLCP_MIU; + } + + @Override + public int getDefaultLlcpRwSize() { + return DEFAULT_LLCP_RWSIZE; + } + private native String doDump(); @Override public String dump() { @@ -370,4 +415,5 @@ public class NativeNfcManager implements DeviceHost { private void notifySeMifareAccess(byte[] block) { mListener.onSeMifareAccess(block); } + } diff --git a/nxp/src/com/android/nfc/dhimpl/NativeNfcTag.java b/nxp/src/com/android/nfc/dhimpl/NativeNfcTag.java index eddde94..992e6d2 100755 --- a/nxp/src/com/android/nfc/dhimpl/NativeNfcTag.java +++ b/nxp/src/com/android/nfc/dhimpl/NativeNfcTag.java @@ -136,6 +136,10 @@ public class NativeNfcTag implements TagEndpoint { private native int doConnect(int handle); public synchronized int connectWithStatus(int technology) { + if (technology == TagTechnology.NFC_B) { + // Not supported by PN544 + return -1; + } if (mWatchdog != null) { mWatchdog.pause(); } @@ -785,6 +789,7 @@ public class NativeNfcTag implements TagEndpoint { getConnectedLibNfcType(), getConnectedTechnology(), supportedNdefLength, cardState); + foundFormattable = false; reconnect(); } break; diff --git a/src/com/android/nfc/DeviceHost.java b/src/com/android/nfc/DeviceHost.java index 047e3d5..e514bb6 100644 --- a/src/com/android/nfc/DeviceHost.java +++ b/src/com/android/nfc/DeviceHost.java @@ -49,6 +49,16 @@ public interface DeviceHost { public void onRemoteFieldDeactivated(); + /** + * Notifies that the SE has been activated in listen mode + */ + public void onSeListenActivated(); + + /** + * Notifies that the SE has been deactivated + */ + public void onSeListenDeactivated(); + public void onSeApduReceived(byte[] apdu); public void onSeEmvCardRemoval(); @@ -175,6 +185,8 @@ public interface DeviceHost { public boolean deinitialize(); + public String getName(); + public void enableDiscovery(); public void disableDiscovery(); @@ -216,5 +228,13 @@ public interface DeviceHost { boolean getExtendedLengthApdusSupported(); + boolean enablePN544Quirks(); + + byte[][] getWipeApdus(); + + int getDefaultLlcpMiu(); + + int getDefaultLlcpRwSize(); + String dump(); } diff --git a/src/com/android/nfc/FireflyRenderer.java b/src/com/android/nfc/FireflyRenderer.java index 4ce58b4..40c931d 100644 --- a/src/com/android/nfc/FireflyRenderer.java +++ b/src/com/android/nfc/FireflyRenderer.java @@ -200,6 +200,7 @@ public class FireflyRenderer { for (int i = 0; i < 3; i++) { // Call eglSwapBuffers 3 times - this will allocate the necessary // buffers, and make sure the animation looks smooth from the start. + mGL.glClear(GL10.GL_COLOR_BUFFER_BIT); if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { Log.e(LOG_TAG, "Could not swap buffers"); mFinished = true; diff --git a/src/com/android/nfc/NfcApplication.java b/src/com/android/nfc/NfcApplication.java new file mode 100644 index 0000000..867b8bb --- /dev/null +++ b/src/com/android/nfc/NfcApplication.java @@ -0,0 +1,24 @@ +package com.android.nfc; + +import android.app.Application; +import android.os.UserHandle; +import android.util.Log; + +public class NfcApplication extends Application { + + public static final String TAG = "NfcApplication"; + NfcService mNfcService; + + public NfcApplication() { + + } + + @Override + public void onCreate() { + super.onCreate(); + + if (UserHandle.myUserId() == 0) { + mNfcService = new NfcService(this); + } + } +} diff --git a/src/com/android/nfc/NfcDispatcher.java b/src/com/android/nfc/NfcDispatcher.java index b3ab97c..1721d1a 100644 --- a/src/com/android/nfc/NfcDispatcher.java +++ b/src/com/android/nfc/NfcDispatcher.java @@ -20,6 +20,7 @@ import com.android.nfc.RegisteredComponentCache.ComponentInfo; import com.android.nfc.handover.HandoverManager; import android.app.Activity; +import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.PendingIntent; @@ -30,6 +31,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.net.Uri; import android.nfc.NdefMessage; @@ -38,6 +40,7 @@ import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.tech.Ndef; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.io.FileDescriptor; @@ -58,7 +61,6 @@ public class NfcDispatcher { final Context mContext; final IActivityManager mIActivityManager; final RegisteredComponentCache mTechListFilters; - final PackageManager mPackageManager; final ContentResolver mContentResolver; final HandoverManager mHandoverManager; @@ -72,7 +74,6 @@ public class NfcDispatcher { mIActivityManager = ActivityManagerNative.getDefault(); mTechListFilters = new RegisteredComponentCache(mContext, NfcAdapter.ACTION_TECH_DISCOVERED, NfcAdapter.ACTION_TECH_DISCOVERED); - mPackageManager = context.getPackageManager(); mContentResolver = context.getContentResolver(); mHandoverManager = handoverManager; } @@ -157,19 +158,21 @@ public class NfcDispatcher { // is not available on Context. Instead, we query the PackageManager beforehand // to determine if there is an Activity to handle this intent, and base the // result of off that. - List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0); + List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser(intent, 0, + ActivityManager.getCurrentUser()); if (activities.size() > 0) { - context.startActivity(rootIntent); + context.startActivityAsUser(rootIntent, UserHandle.CURRENT); return true; } return false; } boolean tryStartActivity(Intent intentToStart) { - List<ResolveInfo> activities = packageManager.queryIntentActivities(intentToStart, 0); + List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser( + intentToStart, 0, ActivityManager.getCurrentUser()); if (activities.size() > 0) { rootIntent.putExtra(NfcRootActivity.EXTRA_LAUNCH_INTENT, intentToStart); - context.startActivity(rootIntent); + context.startActivityAsUser(rootIntent, UserHandle.CURRENT); return true; } return false; @@ -305,7 +308,10 @@ public class NfcDispatcher { if (message == null) { return false; } - dispatch.setNdefIntent(); + Intent intent = dispatch.setNdefIntent(); + + // Bail out if the intent does not contain filterable NDEF data + if (intent == null) return false; // Try to start AAR activity with matching filter List<String> aarPackages = extractAarPackages(message); @@ -320,7 +326,16 @@ public class NfcDispatcher { // Try to perform regular launch of the first AAR if (aarPackages.size() > 0) { String firstPackage = aarPackages.get(0); - Intent appLaunchIntent = mPackageManager.getLaunchIntentForPackage(firstPackage); + PackageManager pm; + try { + UserHandle currentUser = new UserHandle(ActivityManager.getCurrentUser()); + pm = mContext.createPackageContextAsUser("android", 0, + currentUser).getPackageManager(); + } catch (NameNotFoundException e) { + Log.e(TAG, "Could not create user package context"); + return false; + } + Intent appLaunchIntent = pm.getLaunchIntentForPackage(firstPackage); if (appLaunchIntent != null && dispatch.tryStartActivity(appLaunchIntent)) { if (DBG) Log.i(TAG, "matched AAR to application launch"); return true; @@ -364,11 +379,20 @@ public class NfcDispatcher { ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>(); List<ComponentInfo> registered = mTechListFilters.getComponents(); + PackageManager pm; + try { + UserHandle currentUser = new UserHandle(ActivityManager.getCurrentUser()); + pm = mContext.createPackageContextAsUser("android", 0, + currentUser).getPackageManager(); + } catch (NameNotFoundException e) { + Log.e(TAG, "Could not create user package context"); + return false; + } // Check each registered activity to see if it matches for (ComponentInfo info : registered) { // Don't allow wild card matching if (filterMatch(tagTechs, info.techs) && - isComponentEnabled(mPackageManager, info.resolveInfo)) { + isComponentEnabled(pm, info.resolveInfo)) { // Add the activity as a match if it's not already in the list if (!matches.contains(info.resolveInfo)) { matches.add(info.resolveInfo); diff --git a/src/com/android/nfc/NfcRootActivity.java b/src/com/android/nfc/NfcRootActivity.java index 1325ead..cc216f2 100644 --- a/src/com/android/nfc/NfcRootActivity.java +++ b/src/com/android/nfc/NfcRootActivity.java @@ -17,9 +17,11 @@ package com.android.nfc; import android.app.Activity; +import android.app.ActivityManager; import android.content.ActivityNotFoundException; import android.content.Intent; import android.os.Bundle; +import android.os.UserHandle; public class NfcRootActivity extends Activity { @@ -33,7 +35,7 @@ public class NfcRootActivity extends Activity { final Intent launchIntent = intent.getParcelableExtra(EXTRA_LAUNCH_INTENT); if (launchIntent != null) { try { - startActivity(launchIntent); + startActivityAsUser(launchIntent, UserHandle.CURRENT); } catch (ActivityNotFoundException e) { } } diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java index 3e7a6b5..d63f84f 100755 --- a/src/com/android/nfc/NfcService.java +++ b/src/com/android/nfc/NfcService.java @@ -63,6 +63,7 @@ import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.provider.Settings; import android.util.Log; @@ -75,7 +76,7 @@ import java.util.HashSet; import java.util.List; import java.util.concurrent.ExecutionException; -public class NfcService extends Application implements DeviceHostListener { +public class NfcService implements DeviceHostListener { private static final String ACTION_MASTER_CLEAR_NOTIFICATION = "android.intent.action.MASTER_CLEAR_NOTIFICATION"; static final boolean DBG = false; @@ -101,8 +102,6 @@ public class NfcService extends Application implements DeviceHostListener { static final String PREF_FIRST_BOOT = "first_boot"; static final String PREF_AIRPLANE_OVERRIDE = "airplane_override"; - static final boolean PN544_QUIRK_DISCONNECT_BEFORE_RECONFIGURE = true; - static final int MSG_NDEF_TAG = 0; static final int MSG_CARD_EMULATION = 1; static final int MSG_LLCP_LINK_ACTIVATION = 2; @@ -114,6 +113,8 @@ public class NfcService extends Application implements DeviceHostListener { static final int MSG_SE_APDU_RECEIVED = 10; static final int MSG_SE_EMV_CARD_REMOVAL = 11; static final int MSG_SE_MIFARE_ACCESS = 12; + static final int MSG_SE_LISTEN_ACTIVATED = 13; + static final int MSG_SE_LISTEN_DEACTIVATED = 14; static final int TASK_ENABLE = 1; static final int TASK_DISABLE = 2; @@ -134,6 +135,15 @@ public class NfcService extends Application implements DeviceHostListener { /** minimum screen state that enables NFC polling (discovery) */ static final int POLLING_MODE = SCREEN_STATE_ON_UNLOCKED; + // Time to wait for NFC controller to initialize before watchdog + // goes off. This time is chosen large, because firmware download + // may be a part of initialization. + static final int INIT_WATCHDOG_MS = 90000; + + // Time to wait for routing to be applied before watchdog + // goes off + static final int ROUTING_WATCHDOG_MS = 10000; + // for use with playSound() public static final int SOUND_START = 0; public static final int SOUND_END = 1; @@ -160,18 +170,10 @@ public class NfcService extends Application implements DeviceHostListener { public static final String EXTRA_MIFARE_BLOCK = "com.android.nfc_extras.extra.MIFARE_BLOCK"; - //TODO: dont hardcode this - private static final byte[][] EE_WIPE_APDUS = { - {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x00}, - {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x07, (byte)0xa0, (byte)0x00, - (byte)0x00, (byte)0x04, (byte)0x76, (byte)0x20, (byte)0x10, (byte)0x00}, - {(byte)0x80, (byte)0xe2, (byte)0x01, (byte)0x03, (byte)0x00}, - {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x00}, - {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x07, (byte)0xa0, (byte)0x00, - (byte)0x00, (byte)0x04, (byte)0x76, (byte)0x30, (byte)0x30, (byte)0x00}, - {(byte)0x80, (byte)0xb4, (byte)0x00, (byte)0x00, (byte)0x00}, - {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x00}, - }; + public static final String ACTION_SE_LISTEN_ACTIVATED = + "com.android.nfc_extras.action.SE_LISTEN_ACTIVATED"; + public static final String ACTION_SE_LISTEN_DEACTIVATED = + "com.android.nfc_extras.action.SE_LISTEN_DEACTIVATED"; // NFC Execution Environment // fields below are protected by this @@ -184,6 +186,8 @@ public class NfcService extends Application implements DeviceHostListener { // fields below are used in multiple threads and protected by synchronized(this) final HashMap<Integer, Object> mObjectMap = new HashMap<Integer, Object>(); + // mSePackages holds packages that accessed the SE, but only for the owner user, + // as SE access is not granted for non-owner users. HashSet<String> mSePackages = new HashSet<String>(); int mScreenState; boolean mIsNdefPushEnabled; @@ -201,7 +205,9 @@ public class NfcService extends Application implements DeviceHostListener { private DeviceHost mDeviceHost; private SharedPreferences mPrefs; private SharedPreferences.Editor mPrefsEditor; - private PowerManager.WakeLock mWakeLock; + private PowerManager.WakeLock mRoutingWakeLock; + private PowerManager.WakeLock mEeWakeLock; + int mStartSound; int mEndSound; int mErrorSound; @@ -233,6 +239,9 @@ public class NfcService extends Application implements DeviceHostListener { throw new SecurityException(NfceeAccessControl.NFCEE_ACCESS_PATH + " denies NFCEE access to " + pkg); } + if (UserHandle.getCallingUserId() != UserHandle.USER_OWNER) { + throw new SecurityException("only the owner is allowed to call SE APIs"); + } } public static NfcService getInstance() { @@ -287,6 +296,17 @@ public class NfcService extends Application implements DeviceHostListener { } @Override + public void onSeListenActivated() { + sendMessage(NfcService.MSG_SE_LISTEN_ACTIVATED, null); + } + + @Override + public void onSeListenDeactivated() { + sendMessage(NfcService.MSG_SE_LISTEN_DEACTIVATED, null); + } + + + @Override public void onSeApduReceived(byte[] apdu) { sendMessage(NfcService.MSG_SE_APDU_RECEIVED, apdu); } @@ -301,10 +321,7 @@ public class NfcService extends Application implements DeviceHostListener { sendMessage(NfcService.MSG_SE_MIFARE_ACCESS, block); } - @Override - public void onCreate() { - super.onCreate(); - + public NfcService(Application nfcApplication) { mNfcTagService = new TagService(); mNfcAdapter = new NfcAdapterService(); mExtrasService = new NfcAdapterExtrasService(); @@ -313,48 +330,60 @@ public class NfcService extends Application implements DeviceHostListener { sService = this; - mContext = this; - mDeviceHost = new NativeNfcManager(this, this); + mContext = nfcApplication; + mDeviceHost = new NativeNfcManager(mContext, this); HandoverManager handoverManager = new HandoverManager(mContext); - mNfcDispatcher = new NfcDispatcher(this, handoverManager); - mP2pLinkManager = new P2pLinkManager(mContext, handoverManager); + mNfcDispatcher = new NfcDispatcher(mContext, handoverManager); + + mP2pLinkManager = new P2pLinkManager(mContext, handoverManager, + mDeviceHost.getDefaultLlcpMiu(), mDeviceHost.getDefaultLlcpRwSize()); mSecureElement = new NativeNfcSecureElement(mContext); mEeRoutingState = ROUTE_OFF; - mNfceeAccessControl = new NfceeAccessControl(this); + mNfceeAccessControl = new NfceeAccessControl(mContext); - mPrefs = getSharedPreferences(PREF, Context.MODE_PRIVATE); + mPrefs = mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE); mPrefsEditor = mPrefs.edit(); mState = NfcAdapter.STATE_OFF; mIsNdefPushEnabled = mPrefs.getBoolean(PREF_NDEF_PUSH_ON, NDEF_PUSH_ON_DEFAULT); - mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + + mRoutingWakeLock = mPowerManager.newWakeLock( + PowerManager.PARTIAL_WAKE_LOCK, "NfcService:mRoutingWakeLock"); + mEeWakeLock = mPowerManager.newWakeLock( + PowerManager.PARTIAL_WAKE_LOCK, "NfcService:mEeWakeLock"); - mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "NfcService"); - mKeyguard = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); + mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); mScreenState = checkScreenState(); ServiceManager.addService(SERVICE_NAME, mNfcAdapter); + // Intents only for owner + IntentFilter ownerFilter = new IntentFilter(NativeNfcManager.INTERNAL_TARGET_DESELECTED_ACTION); + ownerFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + ownerFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + ownerFilter.addAction(ACTION_MASTER_CLEAR_NOTIFICATION); + + mContext.registerReceiver(mOwnerReceiver, ownerFilter); + + ownerFilter = new IntentFilter(); + ownerFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + ownerFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + ownerFilter.addDataScheme("package"); + + mContext.registerReceiver(mOwnerReceiver, ownerFilter); + + // Intents for all users IntentFilter filter = new IntentFilter(NativeNfcManager.INTERNAL_TARGET_DESELECTED_ACTION); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); - filter.addAction(ACTION_MASTER_CLEAR_NOTIFICATION); filter.addAction(Intent.ACTION_USER_PRESENT); - filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); - filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); registerForAirplaneMode(filter); - registerReceiver(mReceiver, filter); - - filter = new IntentFilter(); - filter.addAction(Intent.ACTION_PACKAGE_ADDED); - filter.addAction(Intent.ACTION_PACKAGE_REMOVED); - filter.addDataScheme("package"); - - registerReceiver(mReceiver, filter); + mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, null); updatePackageCache(); @@ -365,9 +394,9 @@ public class NfcService extends Application implements DeviceHostListener { synchronized(this) { if (mSoundPool == null) { mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0); - mStartSound = mSoundPool.load(this, R.raw.start, 1); - mEndSound = mSoundPool.load(this, R.raw.end, 1); - mErrorSound = mSoundPool.load(this, R.raw.error, 1); + mStartSound = mSoundPool.load(mContext, R.raw.start, 1); + mEndSound = mSoundPool.load(mContext, R.raw.end, 1); + mErrorSound = mSoundPool.load(mContext, R.raw.error, 1); } } } @@ -399,8 +428,8 @@ public class NfcService extends Application implements DeviceHostListener { } void updatePackageCache() { - PackageManager pm = getPackageManager(); - List<PackageInfo> packages = pm.getInstalledPackages(0); + PackageManager pm = mContext.getPackageManager(); + List<PackageInfo> packages = pm.getInstalledPackages(0, UserHandle.USER_OWNER); synchronized (this) { mInstalledPackages = packages; } @@ -416,6 +445,37 @@ public class NfcService extends Application implements DeviceHostListener { } } + int doOpenSecureElementConnection() { + mEeWakeLock.acquire(); + try { + return mSecureElement.doOpenSecureElementConnection(); + } finally { + mEeWakeLock.release(); + } + } + + byte[] doTransceive(int handle, byte[] cmd) { + mEeWakeLock.acquire(); + try { + return doTransceiveNoLock(handle, cmd); + } finally { + mEeWakeLock.release(); + } + } + + byte[] doTransceiveNoLock(int handle, byte[] cmd) { + return mSecureElement.doTransceive(handle, cmd); + } + + void doDisconnect(int handle) { + mEeWakeLock.acquire(); + try { + mSecureElement.doDisconnect(handle); + } finally { + mEeWakeLock.release(); + } + } + /** * Manages tasks that involve turning on/off the NFC controller. * @@ -512,11 +572,21 @@ public class NfcService extends Application implements DeviceHostListener { Log.i(TAG, "Enabling NFC"); updateState(NfcAdapter.STATE_TURNING_ON); - - if (!mDeviceHost.initialize()) { - Log.w(TAG, "Error enabling NFC"); - updateState(NfcAdapter.STATE_OFF); - return false; + WatchDogThread watchDog = new WatchDogThread("enableInternal", INIT_WATCHDOG_MS); + watchDog.start(); + try { + mRoutingWakeLock.acquire(); + try { + if (!mDeviceHost.initialize()) { + Log.w(TAG, "Error enabling NFC"); + updateState(NfcAdapter.STATE_OFF); + return false; + } + } finally { + mRoutingWakeLock.release(); + } + } finally { + watchDog.cancel(); } synchronized(NfcService.this) { @@ -549,7 +619,7 @@ public class NfcService extends Application implements DeviceHostListener { * Implemented with a new thread (instead of a Handler or AsyncTask), * because the UI Thread and AsyncTask thread-pools can also get hung * when the NFC controller stops responding */ - WatchDogThread watchDog = new WatchDogThread(); + WatchDogThread watchDog = new WatchDogThread("disableInternal", ROUTING_WATCHDOG_MS); watchDog.start(); mP2pLinkManager.enableDisable(false, false); @@ -566,7 +636,6 @@ public class NfcService extends Application implements DeviceHostListener { // A convenient way to stop the watchdog properly consists of // disconnecting the tag. The polling loop shall be stopped before // to avoid the tag being discovered again. - applyRouting(true); maybeDisconnectTarget(); mNfcDispatcher.setForegroundDispatch(null, null, null); @@ -585,41 +654,64 @@ public class NfcService extends Application implements DeviceHostListener { void executeEeWipe() { // TODO: read SE reset list from /system/etc - byte[][]apdus = EE_WIPE_APDUS; + byte[][]apdus = mDeviceHost.getWipeApdus(); + + if (apdus == null) { + Log.d(TAG, "No wipe APDUs found"); + return; + } boolean tempEnable = mState == NfcAdapter.STATE_OFF; - if (tempEnable) { - if (!enableInternal()) { + // Hold a wake-lock over the entire wipe procedure + mEeWakeLock.acquire(); + try { + if (tempEnable && !enableInternal()) { Log.w(TAG, "Could not enable NFC to wipe NFC-EE"); return; } - } - Log.i(TAG, "Executing SE wipe"); - int handle = mSecureElement.doOpenSecureElementConnection(); - if (handle == 0) { - Log.w(TAG, "Could not open the secure element"); - if (tempEnable) { - disableInternal(); - } - return; - } - - mDeviceHost.setTimeout(TagTechnology.ISO_DEP, 10000); + try { + // NFC enabled + int handle = 0; + try { + Log.i(TAG, "Executing SE wipe"); + handle = doOpenSecureElementConnection(); + if (handle == 0) { + Log.w(TAG, "Could not open the secure element"); + return; + } + // TODO: remove this hack + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // Ignore + } - for (byte[] cmd : apdus) { - byte[] resp = mSecureElement.doTransceive(handle, cmd); - if (resp == null) { - Log.w(TAG, "Transceive failed, could not wipe NFC-EE"); - break; + mDeviceHost.setTimeout(TagTechnology.ISO_DEP, 10000); + try { + for (byte[] cmd : apdus) { + byte[] resp = doTransceiveNoLock(handle, cmd); + if (resp == null) { + Log.w(TAG, "Transceive failed, could not wipe NFC-EE"); + break; + } + } + } finally { + mDeviceHost.resetTimeouts(); + } + } finally { + if (handle != 0) { + doDisconnect(handle); + } + } + } finally { + if (tempEnable) { + disableInternal(); + } } + } finally { + mEeWakeLock.release(); } - - mDeviceHost.resetTimeouts(); - mSecureElement.doDisconnect(handle); - - if (tempEnable) { - disableInternal(); - } + Log.i(TAG, "SE wipe done"); } void updateState(int newState) { @@ -631,7 +723,7 @@ public class NfcService extends Application implements DeviceHostListener { Intent intent = new Intent(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED); intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.putExtra(NfcAdapter.EXTRA_ADAPTER_STATE, mState); - mContext.sendBroadcast(intent); + mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT); } } } @@ -663,12 +755,6 @@ public class NfcService extends Application implements DeviceHostListener { } } - @Override - public void onTerminate() { - super.onTerminate(); - // NFC application is persistent, it should not be destroyed by framework - Log.wtf(TAG, "NFC service is under attack!"); - } final class NfcAdapterService extends INfcAdapter.Stub { @Override @@ -870,10 +956,6 @@ public class NfcService extends Application implements DeviceHostListener { return ErrorCodes.ERROR_DISCONNECT; } - if (technology == TagTechnology.NFC_B) { - return ErrorCodes.ERROR_NOT_SUPPORTED; - } - // Note that on most tags, all technologies are behind a single // handle. This means that the connect at the lower levels // will do nothing, as the tag is already connected to that handle. @@ -1201,7 +1283,7 @@ public class NfcService extends Application implements DeviceHostListener { binder.unlinkToDeath(mOpenEe, 0); mDeviceHost.resetTimeouts(); - mSecureElement.doDisconnect(mOpenEe.handle); + doDisconnect(mOpenEe.handle); mOpenEe = null; applyRouting(true); @@ -1244,11 +1326,11 @@ public class NfcService extends Application implements DeviceHostListener { throw new IOException("NFC EE already open"); } - int handle = mSecureElement.doOpenSecureElementConnection(); + int handle = doOpenSecureElementConnection(); if (handle == 0) { throw new IOException("NFC EE failed to open"); } - mDeviceHost.setTimeout(TagTechnology.ISO_DEP, 10000); + mDeviceHost.setTimeout(TagTechnology.ISO_DEP, 30000); mOpenEe = new OpenSecureElement(getCallingPid(), handle, b); try { @@ -1259,7 +1341,7 @@ public class NfcService extends Application implements DeviceHostListener { // Add the calling package to the list of packages that have accessed // the secure element. - for (String packageName : getPackageManager().getPackagesForUid(getCallingUid())) { + for (String packageName : mContext.getPackageManager().getPackagesForUid(getCallingUid())) { mSePackages.add(packageName); } } @@ -1308,7 +1390,7 @@ public class NfcService extends Application implements DeviceHostListener { } } - return mSecureElement.doTransceive(mOpenEe.handle, data); + return doTransceive(mOpenEe.handle, data); } @Override @@ -1321,13 +1403,28 @@ public class NfcService extends Application implements DeviceHostListener { public void setCardEmulationRoute(String pkg, int route) throws RemoteException { NfcService.this.enforceNfceeAdminPerm(pkg); mEeRoutingState = route; - applyRouting(true); + ApplyRoutingTask applyRoutingTask = new ApplyRoutingTask(); + applyRoutingTask.execute(); + try { + // Block until route is set + applyRoutingTask.get(); + } catch (ExecutionException e) { + Log.e(TAG, "failed to set card emulation mode"); + } catch (InterruptedException e) { + Log.e(TAG, "failed to set card emulation mode"); + } } @Override public void authenticate(String pkg, byte[] token) throws RemoteException { NfcService.this.enforceNfceeAdminPerm(pkg); } + + @Override + public String getDriverName(String pkg) throws RemoteException { + NfcService.this.enforceNfceeAdminPerm(pkg); + return mDeviceHost.getName(); + } } /** resources kept while secure element is open */ @@ -1376,19 +1473,27 @@ public class NfcService extends Application implements DeviceHostListener { class WatchDogThread extends Thread { boolean mWatchDogCanceled = false; + final int mTimeout; + + public WatchDogThread(String threadName, int timeout) { + super(threadName); + mTimeout = timeout; + } + @Override public void run() { boolean slept = false; while (!slept) { try { - Thread.sleep(10000); + Thread.sleep(mTimeout); slept = true; } catch (InterruptedException e) { } } synchronized (this) { if (!mWatchDogCanceled) { // Trigger watch-dog - Log.e(TAG, "Watch dog triggered"); + Log.e(TAG, "Watchdog fired: name=" + getName() + " threadId=" + + getId() + " timeout=" + mTimeout); mDeviceHost.doAbort(); } } @@ -1407,12 +1512,12 @@ public class NfcService extends Application implements DeviceHostListener { // PN544 cannot be reconfigured while EE is open return; } - WatchDogThread watchDog = new WatchDogThread(); + WatchDogThread watchDog = new WatchDogThread("applyRouting", ROUTING_WATCHDOG_MS); try { watchDog.start(); - if (PN544_QUIRK_DISCONNECT_BEFORE_RECONFIGURE && mScreenState == SCREEN_STATE_OFF) { + if (mDeviceHost.enablePN544Quirks() && mScreenState == SCREEN_STATE_OFF) { /* TODO undo this after the LLCP stack is fixed. * Use a different sequence when turning the screen off to * workaround race conditions in pn544 libnfc. The race occurs @@ -1704,6 +1809,22 @@ public class NfcService extends Application implements DeviceHostListener { break; } + case MSG_SE_LISTEN_ACTIVATED: { + if (DBG) Log.d(TAG, "SE LISTEN MODE ACTIVATED"); + Intent listenModeActivated = new Intent(); + listenModeActivated.setAction(ACTION_SE_LISTEN_ACTIVATED); + sendSeBroadcast(listenModeActivated); + break; + } + + case MSG_SE_LISTEN_DEACTIVATED: { + if (DBG) Log.d(TAG, "SE LISTEN MODE DEACTIVATED"); + Intent listenModeDeactivated = new Intent(); + listenModeDeactivated.setAction(ACTION_SE_LISTEN_DEACTIVATED); + sendSeBroadcast(listenModeDeactivated); + break; + } + default: Log.e(TAG, "Unknown message received"); break; @@ -1808,52 +1929,22 @@ public class NfcService extends Application implements DeviceHostListener { } mScreenState = params[0].intValue(); - boolean needWakelock = mScreenState == SCREEN_STATE_OFF; - if (needWakelock) { - mWakeLock.acquire(); - } - applyRouting(false); - if (needWakelock) { - mWakeLock.release(); + mRoutingWakeLock.acquire(); + try { + applyRouting(false); + } finally { + mRoutingWakeLock.release(); } return null; } } } - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + private final BroadcastReceiver mOwnerReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (action.equals( - NativeNfcManager.INTERNAL_TARGET_DESELECTED_ACTION)) { - // Perform applyRouting() in AsyncTask to serialize blocking calls - new ApplyRoutingTask().execute(); - } else if (action.equals(Intent.ACTION_SCREEN_ON) - || action.equals(Intent.ACTION_SCREEN_OFF) - || action.equals(Intent.ACTION_USER_PRESENT)) { - // Perform applyRouting() in AsyncTask to serialize blocking calls - int screenState = SCREEN_STATE_OFF; - if (action.equals(Intent.ACTION_SCREEN_OFF)) { - screenState = SCREEN_STATE_OFF; - } else if (action.equals(Intent.ACTION_SCREEN_ON)) { - screenState = mKeyguard.isKeyguardLocked() ? - SCREEN_STATE_ON_LOCKED : SCREEN_STATE_ON_UNLOCKED; - } else if (action.equals(Intent.ACTION_USER_PRESENT)) { - screenState = SCREEN_STATE_ON_UNLOCKED; - } - new ApplyRoutingTask().execute(Integer.valueOf(screenState)); - } else if (action.equals(ACTION_MASTER_CLEAR_NOTIFICATION)) { - EnableDisableTask eeWipeTask = new EnableDisableTask(); - eeWipeTask.execute(TASK_EE_WIPE); - try { - eeWipeTask.get(); // blocks until EE wipe is complete - } catch (ExecutionException e) { - Log.w(TAG, "failed to wipe NFC-EE"); - } catch (InterruptedException e) { - Log.w(TAG, "failed to wipe NFC-EE"); - } - } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED) || + if (action.equals(Intent.ACTION_PACKAGE_REMOVED) || action.equals(Intent.ACTION_PACKAGE_ADDED) || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE) || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { @@ -1877,6 +1968,42 @@ public class NfcService extends Application implements DeviceHostListener { } } } + } else if (action.equals(ACTION_MASTER_CLEAR_NOTIFICATION)) { + EnableDisableTask eeWipeTask = new EnableDisableTask(); + eeWipeTask.execute(TASK_EE_WIPE); + try { + eeWipeTask.get(); // blocks until EE wipe is complete + } catch (ExecutionException e) { + Log.w(TAG, "failed to wipe NFC-EE"); + } catch (InterruptedException e) { + Log.w(TAG, "failed to wipe NFC-EE"); + } + } + } + }; + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals( + NativeNfcManager.INTERNAL_TARGET_DESELECTED_ACTION)) { + // Perform applyRouting() in AsyncTask to serialize blocking calls + new ApplyRoutingTask().execute(); + } else if (action.equals(Intent.ACTION_SCREEN_ON) + || action.equals(Intent.ACTION_SCREEN_OFF) + || action.equals(Intent.ACTION_USER_PRESENT)) { + // Perform applyRouting() in AsyncTask to serialize blocking calls + int screenState = SCREEN_STATE_OFF; + if (action.equals(Intent.ACTION_SCREEN_OFF)) { + screenState = SCREEN_STATE_OFF; + } else if (action.equals(Intent.ACTION_SCREEN_ON)) { + screenState = mKeyguard.isKeyguardLocked() ? + SCREEN_STATE_ON_LOCKED : SCREEN_STATE_ON_UNLOCKED; + } else if (action.equals(Intent.ACTION_USER_PRESENT)) { + screenState = SCREEN_STATE_ON_UNLOCKED; + } + new ApplyRoutingTask().execute(Integer.valueOf(screenState)); } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { boolean isAirplaneModeOn = intent.getBooleanExtra("state", false); // Query the airplane mode from Settings.System just to make sure that diff --git a/src/com/android/nfc/P2pLinkManager.java b/src/com/android/nfc/P2pLinkManager.java index 253ddaf..9b23f65 100755 --- a/src/com/android/nfc/P2pLinkManager.java +++ b/src/com/android/nfc/P2pLinkManager.java @@ -167,6 +167,9 @@ public class P2pLinkManager implements Handler.Callback, P2pEventListener.Callba final Handler mHandler; final HandoverManager mHandoverManager; + final int mDefaultMiu; + final int mDefaultRwSize; + // Locked on NdefP2pManager.this int mLinkState; int mSendState; // valid during LINK_STATE_UP or LINK_STATE_DEBOUNCE @@ -179,9 +182,10 @@ public class P2pLinkManager implements Handler.Callback, P2pEventListener.Callba SharedPreferences mPrefs; boolean mFirstBeam; - public P2pLinkManager(Context context, HandoverManager handoverManager) { + public P2pLinkManager(Context context, HandoverManager handoverManager, int defaultMiu, + int defaultRwSize) { mNdefPushServer = new NdefPushServer(NDEFPUSH_SAP, mNppCallback); - mDefaultSnepServer = new SnepServer(mDefaultSnepCallback); + mDefaultSnepServer = new SnepServer(mDefaultSnepCallback, defaultMiu, defaultRwSize); mHandoverServer = new HandoverServer(HANDOVER_SAP, handoverManager, mHandoverCallback); if (ECHOSERVER_ENABLED) { @@ -201,6 +205,8 @@ public class P2pLinkManager implements Handler.Callback, P2pEventListener.Callba mPrefs = context.getSharedPreferences(NfcService.PREF, Context.MODE_PRIVATE); mFirstBeam = mPrefs.getBoolean(NfcService.PREF_FIRST_BEAM, true); mHandoverManager = handoverManager; + mDefaultMiu = defaultMiu; + mDefaultRwSize = defaultRwSize; } /** @@ -417,11 +423,11 @@ public class P2pLinkManager implements Handler.Callback, P2pEventListener.Callba long time = SystemClock.elapsedRealtime(); - try { if (DBG) Log.d(TAG, "Sending ndef via SNEP"); - int snepResult = doSnepProtocol(mHandoverManager, m, uris); + int snepResult = doSnepProtocol(mHandoverManager, m, uris, + mDefaultMiu, mDefaultRwSize); switch (snepResult) { case SNEP_HANDOVER_UNSUPPORTED: @@ -461,8 +467,8 @@ public class P2pLinkManager implements Handler.Callback, P2pEventListener.Callba } static int doSnepProtocol(HandoverManager handoverManager, - NdefMessage msg, Uri[] uris) throws IOException { - SnepClient snepClient = new SnepClient(); + NdefMessage msg, Uri[] uris, int miu, int rwSize) throws IOException { + SnepClient snepClient = new SnepClient(miu, rwSize); try { snepClient.connect(); } catch (IOException e) { diff --git a/src/com/android/nfc/RegisteredComponentCache.java b/src/com/android/nfc/RegisteredComponentCache.java index 1bac283..5da2cd4 100644 --- a/src/com/android/nfc/RegisteredComponentCache.java +++ b/src/com/android/nfc/RegisteredComponentCache.java @@ -19,6 +19,7 @@ package com.android.nfc; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -29,6 +30,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.content.res.XmlResourceParser; +import android.os.UserHandle; import android.util.Log; import java.io.IOException; @@ -69,12 +71,16 @@ public class RegisteredComponentCache { intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); intentFilter.addDataScheme("package"); - mContext.registerReceiver(receiver, intentFilter); + mContext.registerReceiverAsUser(receiver, UserHandle.ALL, intentFilter, null, null); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); - mContext.registerReceiver(receiver, sdFilter); + mContext.registerReceiverAsUser(receiver, UserHandle.ALL, sdFilter, null, null); + // Generate a new list upon switching users as well + IntentFilter userFilter = new IntentFilter(); + userFilter.addAction(Intent.ACTION_USER_SWITCHED); + mContext.registerReceiverAsUser(receiver, UserHandle.ALL, userFilter, null, null); } public static class ComponentInfo { @@ -137,13 +143,21 @@ public class RegisteredComponentCache { } void generateComponentsList() { - PackageManager pm = mContext.getPackageManager(); + PackageManager pm; + try { + UserHandle currentUser = new UserHandle(ActivityManager.getCurrentUser()); + pm = mContext.createPackageContextAsUser("android", 0, + currentUser).getPackageManager(); + } catch (NameNotFoundException e) { + Log.e(TAG, "Could not create user package context"); + return; + } ArrayList<ComponentInfo> components = new ArrayList<ComponentInfo>(); - List<ResolveInfo> resolveInfos = pm.queryIntentActivities(new Intent(mAction), - PackageManager.GET_META_DATA); + List<ResolveInfo> resolveInfos = pm.queryIntentActivitiesAsUser(new Intent(mAction), + PackageManager.GET_META_DATA, ActivityManager.getCurrentUser()); for (ResolveInfo resolveInfo : resolveInfos) { try { - parseComponentInfo(resolveInfo, components); + parseComponentInfo(pm, resolveInfo, components); } catch (XmlPullParserException e) { Log.w(TAG, "Unable to load component info " + resolveInfo.toString(), e); } catch (IOException e) { @@ -158,10 +172,9 @@ public class RegisteredComponentCache { } } - void parseComponentInfo(ResolveInfo info, ArrayList<ComponentInfo> components) - throws XmlPullParserException, IOException { + void parseComponentInfo(PackageManager pm, ResolveInfo info, + ArrayList<ComponentInfo> components) throws XmlPullParserException, IOException { ActivityInfo ai = info.activityInfo; - PackageManager pm = mContext.getPackageManager(); XmlResourceParser parser = null; try { diff --git a/src/com/android/nfc/SendUi.java b/src/com/android/nfc/SendUi.java index 23602c9..5b5c234 100644 --- a/src/com/android/nfc/SendUi.java +++ b/src/com/android/nfc/SendUi.java @@ -183,7 +183,7 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener, // We're only allowed to use hardware acceleration if // isHighEndGfx() returns true - otherwise, we're too limited // on resources to do it. - mHardwareAccelerated = ActivityManager.isHighEndGfx(mDisplay); + mHardwareAccelerated = ActivityManager.isHighEndGfx(); int hwAccelerationFlags = mHardwareAccelerated ? WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED : 0; @@ -194,6 +194,8 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener, | hwAccelerationFlags | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, PixelFormat.OPAQUE); + mWindowLayoutParams.privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; mWindowLayoutParams.token = new Binder(); mFrameCounterAnimator = new TimeAnimator(); @@ -443,6 +445,9 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener, // Navbar has different sizes, depending on orientation final int navBarHeight = hasNavBar ? mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.navigation_bar_height) : 0; + final int navBarHeightLandscape = hasNavBar ? mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_height_landscape) : 0; + final int navBarWidth = hasNavBar ? mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.navigation_bar_width) : 0; @@ -483,13 +488,20 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener, int newTop = statusBarHeight; int newWidth = bitmap.getWidth(); int newHeight = bitmap.getHeight(); + float smallestWidth = (float)Math.min(newWidth, newHeight); + float smallestWidthDp = smallestWidth / (mDisplayMetrics.densityDpi / 160f); if (bitmap.getWidth() < bitmap.getHeight()) { // Portrait mode: status bar is at the top, navbar bottom, width unchanged newHeight = bitmap.getHeight() - statusBarHeight - navBarHeight; } else { - // Landscape mode: status bar is at the top, navbar right - newHeight = bitmap.getHeight() - statusBarHeight; - newWidth = bitmap.getWidth() - navBarWidth; + // Landscape mode: status bar is at the top + // Navbar: bottom on >599dp width devices, otherwise to the side + if (smallestWidthDp > 599) { + newHeight = bitmap.getHeight() - statusBarHeight - navBarHeightLandscape; + } else { + newHeight = bitmap.getHeight() - statusBarHeight; + newWidth = bitmap.getWidth() - navBarWidth; + } } bitmap = Bitmap.createBitmap(bitmap, newLeft, newTop, newWidth, newHeight); diff --git a/src/com/android/nfc/handover/BluetoothHeadsetHandover.java b/src/com/android/nfc/handover/BluetoothHeadsetHandover.java index 7974dfa..1377160 100644 --- a/src/com/android/nfc/handover/BluetoothHeadsetHandover.java +++ b/src/com/android/nfc/handover/BluetoothHeadsetHandover.java @@ -16,6 +16,7 @@ package com.android.nfc.handover; +import android.app.ActivityManager; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -28,6 +29,7 @@ import android.content.IntentFilter; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.UserHandle; import android.util.Log; import android.view.KeyEvent; import android.widget.Toast; @@ -44,9 +46,8 @@ import com.android.nfc.R; * designed to be re-used after the sequence has completed or timed out. * Subsequent NFC interactions should use new objects. * - * TODO: UI review */ -public class BluetoothHeadsetHandover { +public class BluetoothHeadsetHandover implements BluetoothProfile.ServiceListener { static final String TAG = HandoverManager.TAG; static final boolean DBG = HandoverManager.DBG; @@ -57,28 +58,33 @@ public class BluetoothHeadsetHandover { static final int STATE_INIT = 0; static final int STATE_TURNING_ON = 1; - static final int STATE_WAITING_FOR_BOND_CONFIRMATION = 2; - static final int STATE_BONDING = 3; - static final int STATE_CONNECTING = 4; - static final int STATE_DISCONNECTING = 5; - static final int STATE_COMPLETE = 6; + static final int STATE_WAITING_FOR_PROXIES = 2; + static final int STATE_INIT_COMPLETE = 3; + static final int STATE_WAITING_FOR_BOND_CONFIRMATION = 4; + static final int STATE_BONDING = 5; + static final int STATE_CONNECTING = 6; + static final int STATE_DISCONNECTING = 7; + static final int STATE_COMPLETE = 8; static final int RESULT_PENDING = 0; static final int RESULT_CONNECTED = 1; static final int RESULT_DISCONNECTED = 2; + static final int ACTION_INIT = 0; static final int ACTION_DISCONNECT = 1; static final int ACTION_CONNECT = 2; static final int MSG_TIMEOUT = 1; + static final int MSG_NEXT_STEP = 2; final Context mContext; final BluetoothDevice mDevice; final String mName; final HandoverPowerManager mHandoverPowerManager; - final BluetoothA2dp mA2dp; - final BluetoothHeadset mHeadset; final Callback mCallback; + final BluetoothAdapter mBluetoothAdapter; + + final Object mLock = new Object(); // only used on main thread int mAction; @@ -86,21 +92,24 @@ public class BluetoothHeadsetHandover { int mHfpResult; // used only in STATE_CONNECTING and STATE_DISCONNETING int mA2dpResult; // used only in STATE_CONNECTING and STATE_DISCONNETING + // protected by mLock + BluetoothA2dp mA2dp; + BluetoothHeadset mHeadset; + public interface Callback { public void onBluetoothHeadsetHandoverComplete(boolean connected); } public BluetoothHeadsetHandover(Context context, BluetoothDevice device, String name, - HandoverPowerManager powerManager, BluetoothA2dp a2dp, BluetoothHeadset headset, - Callback callback) { + HandoverPowerManager powerManager, Callback callback) { checkMainThread(); // mHandler must get get constructed on Main Thread for toasts to work mContext = context; mDevice = device; mName = name; mHandoverPowerManager = powerManager; - mA2dp = a2dp; - mHeadset = headset; mCallback = callback; + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + mState = STATE_INIT; } @@ -111,6 +120,7 @@ public class BluetoothHeadsetHandover { public void start() { checkMainThread(); if (mState != STATE_INIT) return; + if (mBluetoothAdapter == null) return; IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); @@ -122,15 +132,8 @@ public class BluetoothHeadsetHandover { mContext.registerReceiver(mReceiver, filter); - if (mA2dp.getConnectedDevices().contains(mDevice) || - mHeadset.getConnectedDevices().contains(mDevice)) { - Log.i(TAG, "ACTION_DISCONNECT addr=" + mDevice + " name=" + mName); - mAction = ACTION_DISCONNECT; - } else { - Log.i(TAG, "ACTION_CONNECT addr=" + mDevice + " name=" + mName); - mAction = ACTION_CONNECT; - } mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), TIMEOUT_MS); + mAction = ACTION_INIT; nextStep(); } @@ -138,35 +141,83 @@ public class BluetoothHeadsetHandover { * Called to execute next step in state machine */ void nextStep() { - if (mAction == ACTION_CONNECT) { + if (mAction == ACTION_INIT) { + nextStepInit(); + } else if (mAction == ACTION_CONNECT) { nextStepConnect(); } else { nextStepDisconnect(); } } - void nextStepDisconnect() { + /* + * Enables bluetooth and gets the profile proxies + */ + void nextStepInit() { switch (mState) { case STATE_INIT: - mState = STATE_DISCONNECTING; - if (mHeadset.getConnectionState(mDevice) != BluetoothProfile.STATE_DISCONNECTED) { - mHfpResult = RESULT_PENDING; - mHeadset.disconnect(mDevice); - } else { - mHfpResult = RESULT_DISCONNECTED; - } - if (mA2dp.getConnectionState(mDevice) != BluetoothProfile.STATE_DISCONNECTED) { - mA2dpResult = RESULT_PENDING; - mA2dp.disconnect(mDevice); - } else { - mA2dpResult = RESULT_DISCONNECTED; + if (!mHandoverPowerManager.isBluetoothEnabled()) { + if (mHandoverPowerManager.enableBluetooth()) { + // Bluetooth is being enabled + mState = STATE_TURNING_ON; + } else { + toast(mContext.getString(R.string.failed_to_enable_bt)); + complete(false); + } + break; } - if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { - toast(mContext.getString(R.string.disconnecting_headset ) + " " + - mName + "..."); + // fall-through + case STATE_TURNING_ON: + if (mA2dp == null || mHeadset == null) { + mState = STATE_WAITING_FOR_PROXIES; + if (!getProfileProxys()) { + complete(false); + } break; } // fall-through + case STATE_WAITING_FOR_PROXIES: + mState = STATE_INIT_COMPLETE; + // Check connected devices and see if we need to disconnect + synchronized(mLock) { + if (mA2dp.getConnectedDevices().contains(mDevice) || + mHeadset.getConnectedDevices().contains(mDevice)) { + Log.i(TAG, "ACTION_DISCONNECT addr=" + mDevice + " name=" + mName); + mAction = ACTION_DISCONNECT; + } else { + Log.i(TAG, "ACTION_CONNECT addr=" + mDevice + " name=" + mName); + mAction = ACTION_CONNECT; + } + } + nextStep(); + } + + } + + void nextStepDisconnect() { + switch (mState) { + case STATE_INIT_COMPLETE: + mState = STATE_DISCONNECTING; + synchronized (mLock) { + if (mHeadset.getConnectionState(mDevice) != BluetoothProfile.STATE_DISCONNECTED) { + mHfpResult = RESULT_PENDING; + mHeadset.disconnect(mDevice); + } else { + mHfpResult = RESULT_DISCONNECTED; + } + if (mA2dp.getConnectionState(mDevice) != BluetoothProfile.STATE_DISCONNECTED) { + mA2dpResult = RESULT_PENDING; + mA2dp.disconnect(mDevice); + } else { + mA2dpResult = RESULT_DISCONNECTED; + } + if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { + toast(mContext.getString(R.string.disconnecting_headset ) + " " + + mName + "..."); + break; + } + } + // fall-through case STATE_DISCONNECTING: if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { // still disconnecting @@ -178,26 +229,26 @@ public class BluetoothHeadsetHandover { complete(false); break; } + + } + + boolean getProfileProxys() { + if(!mBluetoothAdapter.getProfileProxy(mContext, this, BluetoothProfile.HEADSET)) + return false; + + if(!mBluetoothAdapter.getProfileProxy(mContext, this, BluetoothProfile.A2DP)) + return false; + + return true; } void nextStepConnect() { switch (mState) { - case STATE_INIT: - if (!mHandoverPowerManager.isBluetoothEnabled()) { - if (mHandoverPowerManager.enableBluetooth()) { - // Bluetooth is being enabled - mState = STATE_TURNING_ON; - } else { - toast(mContext.getString(R.string.failed_to_enable_bt)); - complete(false); - } - break; - } - // fall-through - case STATE_TURNING_ON: + case STATE_INIT_COMPLETE: if (mDevice.getBondState() != BluetoothDevice.BOND_BONDED) { requestPairConfirmation(); mState = STATE_WAITING_FOR_BOND_CONFIRMATION; + break; } // fall-through @@ -211,21 +262,23 @@ public class BluetoothHeadsetHandover { // Bluetooth Profile service will correctly serialize // HFP then A2DP connect mState = STATE_CONNECTING; - if (mHeadset.getConnectionState(mDevice) != BluetoothProfile.STATE_CONNECTED) { - mHfpResult = RESULT_PENDING; - mHeadset.connect(mDevice); - } else { - mHfpResult = RESULT_CONNECTED; - } - if (mA2dp.getConnectionState(mDevice) != BluetoothProfile.STATE_CONNECTED) { - mA2dpResult = RESULT_PENDING; - mA2dp.connect(mDevice); - } else { - mA2dpResult = RESULT_CONNECTED; - } - if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { - toast(mContext.getString(R.string.connecting_headset) + " " + mName + "..."); - break; + synchronized (mLock) { + if (mHeadset.getConnectionState(mDevice) != BluetoothProfile.STATE_CONNECTED) { + mHfpResult = RESULT_PENDING; + mHeadset.connect(mDevice); + } else { + mHfpResult = RESULT_CONNECTED; + } + if (mA2dp.getConnectionState(mDevice) != BluetoothProfile.STATE_CONNECTED) { + mA2dpResult = RESULT_PENDING; + mA2dp.connect(mDevice); + } else { + mA2dpResult = RESULT_CONNECTED; + } + if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { + toast(mContext.getString(R.string.connecting_headset) + " " + mName + "..."); + break; + } } // fall-through case STATE_CONNECTING: @@ -260,7 +313,7 @@ public class BluetoothHeadsetHandover { if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action) && mState == STATE_TURNING_ON) { int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); if (state == BluetoothAdapter.STATE_ON) { - nextStepConnect(); + nextStep(); } else if (state == BluetoothAdapter.STATE_OFF) { toast(mContext.getString(R.string.failed_to_enable_bt)); complete(false); @@ -313,6 +366,16 @@ public class BluetoothHeadsetHandover { mState = STATE_COMPLETE; mContext.unregisterReceiver(mReceiver); mHandler.removeMessages(MSG_TIMEOUT); + synchronized (mLock) { + if (mA2dp != null) { + mBluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, mA2dp); + } + if (mHeadset != null) { + mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadset); + } + mA2dp = null; + mHeadset = null; + } mCallback.onBluetoothHeadsetHandoverComplete(connected); } @@ -324,10 +387,10 @@ public class BluetoothHeadsetHandover { Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY)); - mContext.sendOrderedBroadcast(intent, null); + mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, null, null, 0, null, null); intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY)); - mContext.sendOrderedBroadcast(intent, null); + mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, null, null, 0, null, null); } void requestPairConfirmation() { @@ -336,7 +399,7 @@ public class BluetoothHeadsetHandover { dialogIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); - mContext.startActivity(dialogIntent); + mContext.startActivityAsUser(dialogIntent, new UserHandle(UserHandle.USER_CURRENT)); } final Handler mHandler = new Handler() { @@ -348,6 +411,9 @@ public class BluetoothHeadsetHandover { Log.i(TAG, "Timeout completing BT handover"); complete(false); break; + case MSG_NEXT_STEP: + nextStep(); + break; } } }; @@ -364,4 +430,29 @@ public class BluetoothHeadsetHandover { throw new IllegalThreadStateException("must be called on main thread"); } } + + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + synchronized (mLock) { + switch (profile) { + case BluetoothProfile.HEADSET: + mHeadset = (BluetoothHeadset) proxy; + if (mA2dp != null) { + mHandler.sendEmptyMessage(MSG_NEXT_STEP); + } + break; + case BluetoothProfile.A2DP: + mA2dp = (BluetoothA2dp) proxy; + if (mHeadset != null) { + mHandler.sendEmptyMessage(MSG_NEXT_STEP); + } + break; + } + } + } + + @Override + public void onServiceDisconnected(int profile) { + // We can ignore these + } } diff --git a/src/com/android/nfc/handover/HandoverManager.java b/src/com/android/nfc/handover/HandoverManager.java index f77f780..e7e807d 100644 --- a/src/com/android/nfc/handover/HandoverManager.java +++ b/src/com/android/nfc/handover/HandoverManager.java @@ -33,11 +33,8 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Notification.Builder; -import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHeadset; -import android.bluetooth.BluetoothProfile; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -52,6 +49,7 @@ import android.os.Environment; import android.os.Handler; import android.os.Message; import android.os.SystemClock; +import android.os.UserHandle; import android.util.Log; import android.util.Pair; @@ -62,8 +60,7 @@ import com.android.nfc.R; /** * Manages handover of NFC to other technologies. */ -public class HandoverManager implements BluetoothProfile.ServiceListener, - BluetoothHeadsetHandover.Callback { +public class HandoverManager implements BluetoothHeadsetHandover.Callback { static final String TAG = "NfcHandover"; static final boolean DBG = true; @@ -142,8 +139,6 @@ public class HandoverManager implements BluetoothProfile.ServiceListener, // Variables below synchronized on HandoverManager.this final HashMap<Pair<String, Boolean>, HandoverTransfer> mTransfers; - BluetoothHeadset mBluetoothHeadset; - BluetoothA2dp mBluetoothA2dp; BluetoothHeadsetHandover mBluetoothHeadsetHandover; boolean mBluetoothHeadsetConnected; @@ -390,7 +385,8 @@ public class HandoverManager implements BluetoothProfile.ServiceListener, notBuilder.setContentText(mContext.getString(R.string.beam_touch_to_view)); Intent viewIntent = buildViewIntent(); - PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, viewIntent, 0); + PendingIntent contentIntent = PendingIntent.getActivityAsUser( + mContext, 0, viewIntent, 0, null, UserHandle.CURRENT); notBuilder.setContentIntent(contentIntent); @@ -410,7 +406,8 @@ public class HandoverManager implements BluetoothProfile.ServiceListener, return; } - mNotificationManager.notify(mNotificationId, notBuilder.build()); + mNotificationManager.notifyAsUser(null, mNotificationId, notBuilder.build(), + UserHandle.CURRENT); } synchronized void updateStateAndNotification(int newState) { @@ -537,7 +534,7 @@ public class HandoverManager implements BluetoothProfile.ServiceListener, Uri uri = mediaUri != null ? mediaUri : Uri.parse(ContentResolver.SCHEME_FILE + "://" + filePath); viewIntent.setDataAndTypeAndNormalize(uri, mimeTypes.get(filePath)); - + viewIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); return viewIntent; } @@ -612,10 +609,6 @@ public class HandoverManager implements BluetoothProfile.ServiceListener, public HandoverManager(Context context) { mContext = context; mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - if (mBluetoothAdapter != null) { - mBluetoothAdapter.getProfileProxy(mContext, this, BluetoothProfile.HEADSET); - mBluetoothAdapter.getProfileProxy(mContext, this, BluetoothProfile.A2DP); - } mNotificationManager = (NotificationManager) mContext.getSystemService( Context.NOTIFICATION_SERVICE); @@ -792,9 +785,7 @@ public class HandoverManager implements BluetoothProfile.ServiceListener, if (!handover.valid) return true; synchronized (HandoverManager.this) { - if (mBluetoothAdapter == null || - mBluetoothA2dp == null || - mBluetoothHeadset == null) { + if (mBluetoothAdapter == null) { if (DBG) Log.d(TAG, "BT handover, but BT not available"); return true; } @@ -803,7 +794,7 @@ public class HandoverManager implements BluetoothProfile.ServiceListener, return true; } mBluetoothHeadsetHandover = new BluetoothHeadsetHandover(mContext, handover.device, - handover.name, mHandoverPowerManager, mBluetoothA2dp, mBluetoothHeadset, this); + handover.name, mHandoverPowerManager, this); mBluetoothHeadsetHandover.start(); } return true; @@ -983,34 +974,6 @@ public class HandoverManager implements BluetoothProfile.ServiceListener, } @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - synchronized (HandoverManager.this) { - switch (profile) { - case BluetoothProfile.HEADSET: - mBluetoothHeadset = (BluetoothHeadset) proxy; - break; - case BluetoothProfile.A2DP: - mBluetoothA2dp = (BluetoothA2dp) proxy; - break; - } - } - } - - @Override - public void onServiceDisconnected(int profile) { - synchronized (HandoverManager.this) { - switch (profile) { - case BluetoothProfile.HEADSET: - mBluetoothHeadset = null; - break; - case BluetoothProfile.A2DP: - mBluetoothA2dp = null; - break; - } - } - } - - @Override public void onBluetoothHeadsetHandoverComplete(boolean connected) { synchronized (HandoverManager.this) { mBluetoothHeadsetHandover = null; diff --git a/src/com/android/nfc/ndefpush/NdefPushServer.java b/src/com/android/nfc/ndefpush/NdefPushServer.java index ca58a67..bf92c17 100755 --- a/src/com/android/nfc/ndefpush/NdefPushServer.java +++ b/src/com/android/nfc/ndefpush/NdefPushServer.java @@ -117,28 +117,49 @@ public class NdefPushServer { /** Server class, used to listen for incoming connection request */ class ServerThread extends Thread { + // Variables below synchronized on NdefPushServer.this boolean mRunning = true; LlcpServerSocket mServerSocket; @Override public void run() { - while (mRunning) { + boolean threadRunning; + synchronized (NdefPushServer.this) { + threadRunning = mRunning; + } + while (threadRunning) { if (DBG) Log.d(TAG, "about create LLCP service socket"); try { - mServerSocket = mService.createLlcpServerSocket(mSap, SERVICE_NAME, - MIU, 1, 1024); + synchronized (NdefPushServer.this) { + mServerSocket = mService.createLlcpServerSocket(mSap, SERVICE_NAME, + MIU, 1, 1024); + } if (mServerSocket == null) { if (DBG) Log.d(TAG, "failed to create LLCP service socket"); return; } if (DBG) Log.d(TAG, "created LLCP service socket"); - while (mRunning) { + synchronized (NdefPushServer.this) { + threadRunning = mRunning; + } + + while (threadRunning) { + LlcpServerSocket serverSocket; + synchronized (NdefPushServer.this) { + serverSocket = mServerSocket; + } + if (serverSocket == null) return; + if (DBG) Log.d(TAG, "about to accept"); - LlcpSocket communicationSocket = mServerSocket.accept(); + LlcpSocket communicationSocket = serverSocket.accept(); if (DBG) Log.d(TAG, "accept returned " + communicationSocket); if (communicationSocket != null) { new ConnectionThread(communicationSocket).start(); } + + synchronized (NdefPushServer.this) { + threadRunning = mRunning; + } } if (DBG) Log.d(TAG, "stop running"); } catch (LlcpException e) { @@ -146,28 +167,36 @@ public class NdefPushServer { } catch (IOException e) { Log.e(TAG, "IO error", e); } finally { - if (mServerSocket != null) { - if (DBG) Log.d(TAG, "about to close"); - try { - mServerSocket.close(); - } catch (IOException e) { - // ignore + synchronized (NdefPushServer.this) { + if (mServerSocket != null) { + if (DBG) Log.d(TAG, "about to close"); + try { + mServerSocket.close(); + } catch (IOException e) { + // ignore + } + mServerSocket = null; } - mServerSocket = null; } } + + synchronized (NdefPushServer.this) { + threadRunning = mRunning; + } } } public void shutdown() { - mRunning = false; - if (mServerSocket != null) { - try { - mServerSocket.close(); - } catch (IOException e) { - // ignore + synchronized (NdefPushServer.this) { + mRunning = false; + if (mServerSocket != null) { + try { + mServerSocket.close(); + } catch (IOException e) { + // ignore + } + mServerSocket = null; } - mServerSocket = null; } } } diff --git a/src/com/android/nfc/snep/SnepClient.java b/src/com/android/nfc/snep/SnepClient.java index 8dca6ae..fae8143 100644 --- a/src/com/android/nfc/snep/SnepClient.java +++ b/src/com/android/nfc/snep/SnepClient.java @@ -29,7 +29,8 @@ public final class SnepClient { private static final String TAG = "SnepClient"; private static final boolean DBG = false; private static final int DEFAULT_ACCEPTABLE_LENGTH = 100*1024; - private static final int MIU = 128; + private static final int DEFAULT_MIU = 128; + private static final int DEFAULT_RWSIZE = 1; SnepMessenger mMessenger = null; private final Object mTransmissionLock = new Object(); @@ -38,6 +39,8 @@ public final class SnepClient { private int mState = DISCONNECTED; private final int mAcceptableLength; private final int mFragmentLength; + private final int mMiu; + private final int mRwSize; private static final int DISCONNECTED = 0; private static final int CONNECTING = 1; @@ -48,6 +51,8 @@ public final class SnepClient { mPort = SnepServer.DEFAULT_PORT; mAcceptableLength = DEFAULT_ACCEPTABLE_LENGTH; mFragmentLength = -1; + mMiu = DEFAULT_MIU; + mRwSize = DEFAULT_RWSIZE; } public SnepClient(String serviceName) { @@ -55,6 +60,17 @@ public final class SnepClient { mPort = -1; mAcceptableLength = DEFAULT_ACCEPTABLE_LENGTH; mFragmentLength = -1; + mMiu = DEFAULT_MIU; + mRwSize = DEFAULT_RWSIZE; + } + + public SnepClient(int miu, int rwSize) { + mServiceName = SnepServer.DEFAULT_SERVICE_NAME; + mPort = SnepServer.DEFAULT_PORT; + mAcceptableLength = DEFAULT_ACCEPTABLE_LENGTH; + mFragmentLength = -1; + mMiu = miu; + mRwSize = rwSize; } SnepClient(String serviceName, int fragmentLength) { @@ -62,6 +78,8 @@ public final class SnepClient { mPort = -1; mAcceptableLength = DEFAULT_ACCEPTABLE_LENGTH; mFragmentLength = fragmentLength; + mMiu = DEFAULT_MIU; + mRwSize = DEFAULT_RWSIZE; } SnepClient(String serviceName, int acceptableLength, int fragmentLength) { @@ -69,6 +87,8 @@ public final class SnepClient { mPort = -1; mAcceptableLength = acceptableLength; mFragmentLength = fragmentLength; + mMiu = DEFAULT_MIU; + mRwSize = DEFAULT_RWSIZE; } public void put(NdefMessage msg) throws IOException { @@ -122,7 +142,7 @@ public final class SnepClient { try { if (DBG) Log.d(TAG, "about to create socket"); // Connect to the snep server on the remote side - socket = NfcService.getInstance().createLlcpSocket(0, MIU, 1, 1024); + socket = NfcService.getInstance().createLlcpSocket(0, mMiu, mRwSize, 1024); if (socket == null) { throw new IOException("Could not connect to socket."); } diff --git a/src/com/android/nfc/snep/SnepServer.java b/src/com/android/nfc/snep/SnepServer.java index 84bb673..aa7da48 100644 --- a/src/com/android/nfc/snep/SnepServer.java +++ b/src/com/android/nfc/snep/SnepServer.java @@ -34,9 +34,10 @@ import java.io.IOException; public final class SnepServer { private static final String TAG = "SnepServer"; private static final boolean DBG = false; + private static final int DEFAULT_MIU = 248; + private static final int DEFAULT_RW_SIZE = 1; public static final int DEFAULT_PORT = 4; - private static final int MIU = 248; public static final String DEFAULT_SERVICE_NAME = "urn:nfc:sn:snep"; @@ -44,6 +45,8 @@ public final class SnepServer { final String mServiceName; final int mServiceSap; final int mFragmentLength; + final int mMiu; + final int mRwSize; /** Protected by 'this', null when stopped, non-null when running */ ServerThread mServerThread = null; @@ -59,6 +62,8 @@ public final class SnepServer { mServiceName = DEFAULT_SERVICE_NAME; mServiceSap = DEFAULT_PORT; mFragmentLength = -1; + mMiu = DEFAULT_MIU; + mRwSize = DEFAULT_RW_SIZE; } public SnepServer(String serviceName, int serviceSap, Callback callback) { @@ -66,6 +71,17 @@ public final class SnepServer { mServiceName = serviceName; mServiceSap = serviceSap; mFragmentLength = -1; + mMiu = DEFAULT_MIU; + mRwSize = DEFAULT_RW_SIZE; + } + + public SnepServer(Callback callback, int miu, int rwSize) { + mCallback = callback; + mServiceName = DEFAULT_SERVICE_NAME; + mServiceSap = DEFAULT_PORT; + mFragmentLength = -1; + mMiu = miu; + mRwSize = rwSize; } SnepServer(String serviceName, int serviceSap, int fragmentLength, Callback callback) { @@ -73,6 +89,8 @@ public final class SnepServer { mServiceName = serviceName; mServiceSap = serviceSap; mFragmentLength = fragmentLength; + mMiu = DEFAULT_MIU; + mRwSize = DEFAULT_RW_SIZE; } /** Connection class, used to handle incoming connections */ @@ -168,7 +186,7 @@ public final class SnepServer { try { synchronized (SnepServer.this) { mServerSocket = NfcService.getInstance().createLlcpServerSocket(mServiceSap, - mServiceName, MIU, 1, 1024); + mServiceName, mMiu, mRwSize, 1024); } if (mServerSocket == null) { if (DBG) Log.d(TAG, "failed to create LLCP service socket"); |