diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2017-07-09 07:33:07 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2017-07-09 07:33:07 +0000 |
commit | 084ad222eaf0fd53ecd4c4efaf79b8afd31d841d (patch) | |
tree | 459b84aa4539c6bdc5271452a4cb3af6175631cc | |
parent | ed38674449cf922e3fa4808e1a2c43106790e599 (diff) | |
parent | 8ee2015a9c4639d465a390af93df3fdcd983cac8 (diff) | |
download | android_packages_apps_Nfc-084ad222eaf0fd53ecd4c4efaf79b8afd31d841d.tar.gz android_packages_apps_Nfc-084ad222eaf0fd53ecd4c4efaf79b8afd31d841d.tar.bz2 android_packages_apps_Nfc-084ad222eaf0fd53ecd4c4efaf79b8afd31d841d.zip |
release-request-bf3460d4-15ec-4b2b-816a-b106ef372c4b-for-git_oc-dr1-release-4164433 snap-temp-L62100000080728136
Change-Id: If8148f85ae083524dfdc1953de4f887f833af6c7
-rwxr-xr-x | nci/jni/NativeNfcManager.cpp | 156 | ||||
-rw-r--r-- | nci/jni/NativeNfcTag.cpp | 4 | ||||
-rwxr-xr-x | nci/jni/NfcJniUtil.h | 2 | ||||
-rwxr-xr-x | nci/jni/NfcTag.cpp | 16 | ||||
-rwxr-xr-x | nci/jni/RoutingManager.cpp | 10 | ||||
-rwxr-xr-x | nci/jni/RoutingManager.h | 2 | ||||
-rwxr-xr-x | nci/src/com/android/nfc/dhimpl/NativeNfcManager.java | 8 | ||||
-rw-r--r-- | src/com/android/nfc/DeviceHost.java | 6 | ||||
-rwxr-xr-x | src/com/android/nfc/NfcService.java | 78 | ||||
-rw-r--r-- | src/com/android/nfc/ScreenStateHelper.java | 26 | ||||
-rw-r--r-- | src/com/android/nfc/cardemulation/AidRoutingManager.java | 75 | ||||
-rw-r--r-- | src/com/android/nfc/cardemulation/EnabledNfcFServices.java | 3 | ||||
-rw-r--r-- | src/com/android/nfc/cardemulation/RegisteredAidCache.java | 344 | ||||
-rw-r--r-- | src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java | 6 | ||||
-rw-r--r-- | src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java | 9 |
15 files changed, 611 insertions, 134 deletions
diff --git a/nci/jni/NativeNfcManager.cpp b/nci/jni/NativeNfcManager.cpp index 146b61e6..b79954e6 100755 --- a/nci/jni/NativeNfcManager.cpp +++ b/nci/jni/NativeNfcManager.cpp @@ -75,6 +75,7 @@ namespace android *****************************************************************************/ bool gActivated = false; SyncEvent gDeactivatedEvent; +SyncEvent sNfaSetPowerSubState; namespace android { @@ -130,7 +131,7 @@ static jint sLfT3tMax = 0; #define DEFAULT_TECH_MASK (NFA_TECHNOLOGY_MASK_A \ | NFA_TECHNOLOGY_MASK_B \ | NFA_TECHNOLOGY_MASK_F \ - | NFA_TECHNOLOGY_MASK_ISO15693 \ + | NFA_TECHNOLOGY_MASK_V \ | NFA_TECHNOLOGY_MASK_B_PRIME \ | NFA_TECHNOLOGY_MASK_A_ACTIVE \ | NFA_TECHNOLOGY_MASK_F_ACTIVE \ @@ -145,10 +146,12 @@ static bool isListenMode(tNFA_ACTIVATED& activated); static void enableDisableLptd (bool enable); static tNFA_STATUS stopPolling_rfDiscoveryDisabled(); static tNFA_STATUS startPolling_rfDiscoveryDisabled(tNFA_TECHNOLOGY_MASK tech_mask); +static void nfcManager_doSetScreenState(JNIEnv* e, jobject o, jint screen_state_mask); static uint16_t sCurrentConfigLen; static uint8_t sConfig[256]; - +static int prevScreenState = NFA_SCREEN_STATE_OFF_LOCKED; +static int NFA_SCREEN_POLLING_TAG_MASK = 0x10; ///////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////// @@ -329,16 +332,19 @@ static void nfaConnectionCallback (uint8_t connEvent, tNFA_CONN_EVT_DATA* eventD break; } sP2pActive = true; - ALOGV("%s: NFA_ACTIVATED_EVT; is p2p", __func__); - // Disable RF field events in case of p2p - uint8_t nfa_disable_rf_events[] = { 0x00 }; - ALOGV("%s: Disabling RF field events", __func__); - status = NFA_SetConfig(NCI_PARAM_ID_RF_FIELD_INFO, sizeof(nfa_disable_rf_events), - &nfa_disable_rf_events[0]); - if (status == NFA_STATUS_OK) { - ALOGV("%s: Disabled RF field events", __func__); - } else { - ALOGE("%s: Failed to disable RF field events", __func__); + ALOGD("%s: NFA_ACTIVATED_EVT; is p2p", __func__); + if (NFC_GetNCIVersion() == NCI_VERSION_1_0) + { + // Disable RF field events in case of p2p + uint8_t nfa_disable_rf_events[] = { 0x00 }; + ALOGD ("%s: Disabling RF field events", __func__); + 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", __func__); + } else { + ALOGE ("%s: Failed to disable RF field events", __func__); + } } } else if (pn544InteropIsBusy() == false) @@ -391,19 +397,22 @@ static void nfaConnectionCallback (uint8_t connEvent, tNFA_CONN_EVT_DATA* eventD } else if (sP2pActive) { sP2pActive = false; // Make sure RF field events are re-enabled - ALOGV("%s: NFA_DEACTIVATED_EVT; is p2p", __func__); - // Disable RF field events in case of p2p - uint8_t nfa_enable_rf_events[] = { 0x01 }; - - if (!sIsDisabling && sIsNfaEnabled) + ALOGD("%s: NFA_DEACTIVATED_EVT; is p2p", __func__); + if (NFC_GetNCIVersion() == NCI_VERSION_1_0) { - ALOGV("%s: Enabling RF field events", __func__); - status = NFA_SetConfig(NCI_PARAM_ID_RF_FIELD_INFO, sizeof(nfa_enable_rf_events), - &nfa_enable_rf_events[0]); - if (status == NFA_STATUS_OK) { - ALOGV("%s: Enabled RF field events", __func__); - } else { - ALOGE("%s: Failed to enable RF field events", __func__); + // Disable RF field events in case of p2p + uint8_t nfa_enable_rf_events[] = { 0x01 }; + + if (!sIsDisabling && sIsNfaEnabled) + { + ALOGD ("%s: Enabling RF field events", __func__); + 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", __func__); + } else { + ALOGE ("%s: Failed to enable RF field events", __func__); + } } } } @@ -743,6 +752,14 @@ void nfaDeviceManagementCallback (uint8_t dmEvent, tNFA_DM_CBACK_DATA* eventData PowerSwitch::getInstance ().deviceManagementCallback (dmEvent, eventData); break; + case NFA_DM_SET_POWER_SUB_STATE_EVT: + { + ALOGD("%s: NFA_DM_SET_POWER_SUB_STATE_EVT; status=0x%X", + __FUNCTION__, eventData->power_sub_state.status); + SyncEventGuard guard (sNfaSetPowerSubState); + sNfaSetPowerSubState.notifyOne(); + } + break; default: ALOGV("%s: unhandled event", __func__); break; @@ -776,18 +793,19 @@ static jboolean nfcManager_sendRawFrame (JNIEnv* e, jobject, jbyteArray data) ** ** Description: Route an AID to an EE ** e: JVM environment. -** o: Java object. +** aid: aid to be added to routing table. +** route: aid route location. i.e. DH/eSE/UICC +** aidInfo: prefix or suffix aid. ** -** Returns: True if ok. +** Returns: True if aid is accpted by NFA Layer. ** *******************************************************************************/ -static jboolean nfcManager_routeAid (JNIEnv* e, jobject, jbyteArray aid, jint route) +static jboolean nfcManager_routeAid (JNIEnv* e, jobject, jbyteArray aid, jint route, jint aidInfo) { ScopedByteArrayRO bytes(e, aid); uint8_t* buf = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(&bytes[0])); size_t bufLen = bytes.size(); - bool result = RoutingManager::getInstance().addAidRouting(buf, bufLen, route); - return result; + return RoutingManager::getInstance().addAidRouting(buf, bufLen, route, aidInfo); } /******************************************************************************* @@ -1634,7 +1652,79 @@ static jstring nfcManager_doDump(JNIEnv* e, jobject) return e->NewStringUTF(buffer); } +static jint nfcManager_doGetNciVersion(JNIEnv* , jobject) +{ + return NFC_GetNCIVersion(); +} +static void nfcManager_doSetScreenState (JNIEnv* e, jobject o, jint screen_state_mask) +{ + tNFA_STATUS status = NFA_STATUS_OK; + uint8_t state = (screen_state_mask & NFA_SCREEN_STATE_MASK); + uint8_t discovry_param = NFA_LISTEN_DH_NFCEE_ENABLE_MASK | NFA_POLLING_DH_ENABLE_MASK; + + ALOGD ("%s: state = %d discovry_param = %d", __FUNCTION__, state, discovry_param); + + if (sIsDisabling || !sIsNfaEnabled ||(NFC_GetNCIVersion() != NCI_VERSION_2_0)) + return; + if (prevScreenState == NFA_SCREEN_STATE_OFF_LOCKED || prevScreenState == NFA_SCREEN_STATE_OFF_UNLOCKED) + { + SyncEventGuard guard (sNfaSetPowerSubState); + status = NFA_SetPowerSubStateForScreenState(state); + if (status != NFA_STATUS_OK) + { + ALOGE ("%s: fail enable SetScreenState; error=0x%X", __FUNCTION__, status); + return; + } + else + { + sNfaSetPowerSubState.wait(); + } + } + + if (state == NFA_SCREEN_STATE_OFF_LOCKED || state == NFA_SCREEN_STATE_OFF_UNLOCKED) + { + // disable both poll and listen on DH 0x02 + discovry_param = NFA_POLLING_DH_DISABLE_MASK | NFA_LISTEN_DH_NFCEE_DISABLE_MASK; + } + + if (state == NFA_SCREEN_STATE_ON_LOCKED) + { + // disable poll and enable listen on DH 0x00 + discovry_param = (screen_state_mask & NFA_SCREEN_POLLING_TAG_MASK) ? + (NFA_LISTEN_DH_NFCEE_ENABLE_MASK | NFA_POLLING_DH_ENABLE_MASK): + (NFA_POLLING_DH_DISABLE_MASK | NFA_LISTEN_DH_NFCEE_ENABLE_MASK); + } + + if (state == NFA_SCREEN_STATE_ON_UNLOCKED) + { + // enable both poll and listen on DH 0x01 + discovry_param = NFA_LISTEN_DH_NFCEE_ENABLE_MASK | NFA_POLLING_DH_ENABLE_MASK; + } + + SyncEventGuard guard (sNfaSetConfigEvent); + status = NFA_SetConfig(NFC_PMID_CON_DISCOVERY_PARAM, NCI_PARAM_LEN_CON_DISCOVERY_PARAM, &discovry_param); + if (status == NFA_STATUS_OK) + { + sNfaSetConfigEvent.wait (); + } else { + ALOGE ("%s: Failed to update CON_DISCOVER_PARAM", __FUNCTION__); + return; + } + + if (prevScreenState == NFA_SCREEN_STATE_ON_LOCKED || prevScreenState == NFA_SCREEN_STATE_ON_UNLOCKED) + { + SyncEventGuard guard (sNfaSetPowerSubState); + status = NFA_SetPowerSubStateForScreenState(state); + if (status != NFA_STATUS_OK) + { + ALOGE ("%s: fail enable SetScreenState; error=0x%X", __FUNCTION__, status); + } else { + sNfaSetPowerSubState.wait(); + } + } + prevScreenState = state; +} /******************************************************************************* ** ** Function: nfcManager_doSetP2pInitiatorModes @@ -1722,7 +1812,7 @@ static JNINativeMethod gMethods[] = {"sendRawFrame", "([B)Z", (void*) nfcManager_sendRawFrame}, - {"routeAid", "([BI)Z", + {"routeAid", "([BII)Z", (void*) nfcManager_routeAid}, {"unrouteAid", "([B)Z", @@ -1785,11 +1875,17 @@ static JNINativeMethod gMethods[] = {"doEnableScreenOffSuspend", "()V", (void *)nfcManager_doEnableScreenOffSuspend}, + {"doSetScreenState", "(I)V", + (void*)nfcManager_doSetScreenState}, + {"doDisableScreenOffSuspend", "()V", (void *)nfcManager_doDisableScreenOffSuspend}, {"doDump", "()Ljava/lang/String;", (void *)nfcManager_doDump}, + + {"getNciVersion","()I", + (void *)nfcManager_doGetNciVersion}, }; diff --git a/nci/jni/NativeNfcTag.cpp b/nci/jni/NativeNfcTag.cpp index 6361bc96..ece0ce5b 100644 --- a/nci/jni/NativeNfcTag.cpp +++ b/nci/jni/NativeNfcTag.cpp @@ -1102,7 +1102,7 @@ static jint nativeNfcTag_doGetNdefType (JNIEnv*, jobject, jint libnfcType, jint } else { - /* NFA_PROTOCOL_ISO15693, NFA_PROTOCOL_INVALID and others */ + /* NFA_PROTOCOL_T5T, NFA_PROTOCOL_INVALID and others */ ndefType = NDEF_UNKNOWN_TYPE; } ALOGV("%s: exit; ndef type=%d", __func__, ndefType); @@ -1455,7 +1455,7 @@ static jboolean nativeNfcTag_doIsNdefFormatable (JNIEnv* e, { jboolean isFormattable = JNI_FALSE; tNFC_PROTOCOL protocol = NfcTag::getInstance().getProtocol(); - if (NFA_PROTOCOL_T1T == protocol || NFA_PROTOCOL_ISO15693 == protocol + if (NFA_PROTOCOL_T1T == protocol || NFA_PROTOCOL_T5T == protocol || NFA_PROTOCOL_MIFARE == protocol) { isFormattable = JNI_TRUE; diff --git a/nci/jni/NfcJniUtil.h b/nci/jni/NfcJniUtil.h index c52e93ef..f5368c90 100755 --- a/nci/jni/NfcJniUtil.h +++ b/nci/jni/NfcJniUtil.h @@ -78,7 +78,7 @@ #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_V 5 #define TARGET_TYPE_NDEF 6 #define TARGET_TYPE_NDEF_FORMATABLE 7 #define TARGET_TYPE_MIFARE_CLASSIC 8 diff --git a/nci/jni/NfcTag.cpp b/nci/jni/NfcTag.cpp index 3246f6fa..0b8ec85e 100755 --- a/nci/jni/NfcTag.cpp +++ b/nci/jni/NfcTag.cpp @@ -384,10 +384,10 @@ void NfcTag::discoverTechnologies (tNFA_ACTIVATED& activationData) memcpy (&(mTechParams[mNumTechList]), &(rfDetail.rf_tech_param), sizeof(rfDetail.rf_tech_param)); } } - else if (NFC_PROTOCOL_15693 == rfDetail.protocol) + else if (NFC_PROTOCOL_T5T == rfDetail.protocol) { //is TagTechnology.NFC_V by Java API - mTechList [mNumTechList] = TARGET_TYPE_ISO15693; + mTechList [mNumTechList] = TARGET_TYPE_V; } else if (NFC_PROTOCOL_KOVIO == rfDetail.protocol) { @@ -510,10 +510,10 @@ void NfcTag::discoverTechnologies (tNFA_DISC_RESULT& discoveryData) } } } - else if (NFC_PROTOCOL_15693 == discovery_ntf.protocol) + else if (NFC_PROTOCOL_T5T == discovery_ntf.protocol) { //is TagTechnology.NFC_V by Java API - mTechList [mNumTechList] = TARGET_TYPE_ISO15693; + mTechList [mNumTechList] = TARGET_TYPE_V; } else if (NFC_PROTOCOL_MIFARE == discovery_ntf.protocol) { @@ -784,7 +784,7 @@ void NfcTag::fillNativeNfcTagMembers3 (JNIEnv* e, jclass tag_cls, jobject tag, t pollBytes.reset(e->NewByteArray(len)); e->SetByteArrayRegion(pollBytes.get(), 0, len, (jbyte*) result); } - else if (NFC_DISCOVERY_TYPE_POLL_ISO15693 == mTechParams [i].mode + else if (NFC_DISCOVERY_TYPE_POLL_V == mTechParams [i].mode || NFC_DISCOVERY_TYPE_LISTEN_ISO15693 == mTechParams [i].mode) { ALOGV("%s: tech iso 15693", fn); @@ -916,7 +916,7 @@ void NfcTag::fillNativeNfcTagMembers4 (JNIEnv* e, jclass tag_cls, jobject tag, t actBytes.reset(e->NewByteArray(0)); } } //case NFC_PROTOCOL_ISO_DEP: //t4t - else if (NFC_PROTOCOL_15693 == mTechLibNfcTypes[i]) + else if (NFC_PROTOCOL_T5T == mTechLibNfcTypes[i]) { ALOGV("%s: tech iso 15693", fn); //iso 15693 response flags: 1 octet @@ -1004,7 +1004,7 @@ void NfcTag::fillNativeNfcTagMembers5 (JNIEnv* e, jclass tag_cls, jobject tag, t (jbyte*) &mTechParams [0].param.pf.nfcid2); ALOGV("%s: tech F", fn); } - else if (NFC_DISCOVERY_TYPE_POLL_ISO15693 == mTechParams [0].mode + else if (NFC_DISCOVERY_TYPE_POLL_V == mTechParams [0].mode || NFC_DISCOVERY_TYPE_LISTEN_ISO15693 == mTechParams [0].mode) { ALOGV("%s: tech iso 15693", fn); @@ -1487,7 +1487,7 @@ void NfcTag::resetAllTransceiveTimeouts () mTechnologyTimeoutsTable [TARGET_TYPE_ISO14443_3B] = 1000; //NfcB mTechnologyTimeoutsTable [TARGET_TYPE_ISO14443_4] = 618; //ISO-DEP mTechnologyTimeoutsTable [TARGET_TYPE_FELICA] = 255; //Felica - mTechnologyTimeoutsTable [TARGET_TYPE_ISO15693] = 1000;//NfcV + mTechnologyTimeoutsTable [TARGET_TYPE_V] = 1000;//NfcV mTechnologyTimeoutsTable [TARGET_TYPE_NDEF] = 1000; mTechnologyTimeoutsTable [TARGET_TYPE_NDEF_FORMATABLE] = 1000; mTechnologyTimeoutsTable [TARGET_TYPE_MIFARE_CLASSIC] = 618; //MifareClassic diff --git a/nci/jni/RoutingManager.cpp b/nci/jni/RoutingManager.cpp index 101dc0c0..bf6489c3 100755 --- a/nci/jni/RoutingManager.cpp +++ b/nci/jni/RoutingManager.cpp @@ -326,11 +326,11 @@ void RoutingManager::disableRoutingToHost() } } -bool RoutingManager::addAidRouting(const uint8_t* aid, uint8_t aidLen, int route) +bool RoutingManager::addAidRouting(const uint8_t* aid, uint8_t aidLen, int route, int aidInfo) { static const char fn [] = "RoutingManager::addAidRouting"; ALOGV("%s: enter", fn); - tNFA_STATUS nfaStat = NFA_EeAddAidRouting(route, aidLen, (uint8_t*) aid, 0x01); + tNFA_STATUS nfaStat = NFA_EeAddAidRouting(route, aidLen, (uint8_t*) aid, 0x01, aidInfo); if (nfaStat == NFA_STATUS_OK) { ALOGV("%s: routed AID", fn); @@ -688,7 +688,7 @@ int RoutingManager::registerT3tIdentifier(uint8_t* t3tId, uint8_t t3tIdLen) ALOGV("%s: Start to register NFC-F system on DH", fn); - if (t3tIdLen != (2 + NCI_RF_F_UID_LEN)) + if (t3tIdLen != (2 + NCI_RF_F_UID_LEN + NCI_T3T_PMM_LEN)) { ALOGE("%s: Invalid length of T3T Identifier", fn); return NFA_HANDLE_INVALID; @@ -699,11 +699,13 @@ int RoutingManager::registerT3tIdentifier(uint8_t* t3tId, uint8_t t3tIdLen) int systemCode; uint8_t nfcid2[NCI_RF_F_UID_LEN]; + uint8_t t3tPmm[NCI_T3T_PMM_LEN]; systemCode = (((int)t3tId[0] << 8) | ((int)t3tId[1] << 0)); memcpy(nfcid2, t3tId + 2, NCI_RF_F_UID_LEN); + memcpy(t3tPmm, t3tId + 10, NCI_T3T_PMM_LEN); - tNFA_STATUS nfaStat = NFA_CeRegisterFelicaSystemCodeOnDH (systemCode, nfcid2, nfcFCeCallback); + tNFA_STATUS nfaStat = NFA_CeRegisterFelicaSystemCodeOnDH (systemCode, nfcid2, t3tPmm, nfcFCeCallback); if (nfaStat == NFA_STATUS_OK) { mRoutingEvent.wait (); diff --git a/nci/jni/RoutingManager.h b/nci/jni/RoutingManager.h index ee9211fc..62d569fe 100755 --- a/nci/jni/RoutingManager.h +++ b/nci/jni/RoutingManager.h @@ -35,7 +35,7 @@ public: bool initialize(nfc_jni_native_data* native); void enableRoutingToHost(); void disableRoutingToHost(); - bool addAidRouting(const uint8_t* aid, uint8_t aidLen, int route); + bool addAidRouting(const uint8_t* aid, uint8_t aidLen, int route, int aidInfo); bool removeAidRouting(const uint8_t* aid, uint8_t aidLen); bool commitRouting(); int registerT3tIdentifier(uint8_t* t3tId, uint8_t t3tIdLen); diff --git a/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java index ecd8e6d3..35f2cb33 100755 --- a/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java +++ b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java @@ -95,7 +95,7 @@ public class NativeNfcManager implements DeviceHost { public native boolean sendRawFrame(byte[] data); @Override - public native boolean routeAid(byte[] aid, int route); + public native boolean routeAid(byte[] aid, int route, int aidInfo); @Override public native boolean unrouteAid(byte[] aid); @@ -143,6 +143,12 @@ public class NativeNfcManager implements DeviceHost { @Override public native int getLfT3tMax(); + @Override + public native void doSetScreenState(int screen_state_mask); + + @Override + public native int getNciVersion(); + private native void doEnableDiscovery(int techMask, boolean enableLowPowerPolling, boolean enableReaderMode, diff --git a/src/com/android/nfc/DeviceHost.java b/src/com/android/nfc/DeviceHost.java index 621478c8..8b622206 100644 --- a/src/com/android/nfc/DeviceHost.java +++ b/src/com/android/nfc/DeviceHost.java @@ -183,7 +183,7 @@ public interface DeviceHost { public boolean sendRawFrame(byte[] data); - public boolean routeAid(byte[] aid, int route); + public boolean routeAid(byte[] aid, int route, int aidInfo); public boolean unrouteAid(byte[] aid); @@ -237,4 +237,8 @@ public interface DeviceHost { boolean enableScreenOffSuspend(); boolean disableScreenOffSuspend(); + + public void doSetScreenState(int screen_state_mask); + + public int getNciVersion(); } diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java index 4eff56ec..d52ca834 100755 --- a/src/com/android/nfc/NfcService.java +++ b/src/com/android/nfc/NfcService.java @@ -131,6 +131,7 @@ public class NfcService implements DeviceHostListener { static final int MSG_DEREGISTER_T3T_IDENTIFIER = 13; static final int MSG_TAG_DEBOUNCE = 14; static final int MSG_UPDATE_STATS = 15; + static final int MSG_APPLY_SCREEN_STATE = 16; // Update stats every 4 hours static final long STATS_UPDATE_INTERVAL_MS = 4 * 60 * 60 * 1000; @@ -144,7 +145,7 @@ public class NfcService implements DeviceHostListener { static final int NFC_POLL_A = 0x01; static final int NFC_POLL_B = 0x02; static final int NFC_POLL_F = 0x04; - static final int NFC_POLL_ISO15693 = 0x08; + static final int NFC_POLL_V = 0x08; static final int NFC_POLL_B_PRIME = 0x10; static final int NFC_POLL_KOVIO = 0x20; @@ -178,6 +179,10 @@ public class NfcService implements DeviceHostListener { public static final int SOUND_END = 1; public static final int SOUND_ERROR = 2; + public static final int NCI_VERSION_2_0 = 0x20; + + public static final int NCI_VERSION_1_0 = 0x10; + public static final String ACTION_LLCP_UP = "com.android.nfc.action.LLCP_UP"; @@ -189,6 +194,7 @@ public class NfcService implements DeviceHostListener { private final UserManager mUserManager; + private static int nci_version = NCI_VERSION_1_0; // NFC Execution Environment // fields below are protected by this private final ReaderModeDeathRecipient mReaderModeDeathRecipient = @@ -384,7 +390,7 @@ public class NfcService implements DeviceHostListener { } else if (liveCaseTechList[i].equals("TypeF")) { mLiveCaseTechnology |= NFC_POLL_F; } else if (liveCaseTechList[i].equals("TypeV")) { - mLiveCaseTechnology |= NFC_POLL_ISO15693; + mLiveCaseTechnology |= NFC_POLL_V; } } } catch (NotFoundException e) { @@ -620,6 +626,8 @@ public class NfcService implements DeviceHostListener { mCardEmulationManager.onNfcEnabled(); } + nci_version = getNciVersion(); + synchronized (NfcService.this) { mObjectMap.clear(); mP2pLinkManager.enableDisable(mIsNdefPushEnabled, true); @@ -628,6 +636,15 @@ public class NfcService implements DeviceHostListener { initSoundPool(); + mScreenState = mScreenStateHelper.checkScreenState(); + int screen_state_mask = (mNfcUnlockManager.isLockscreenPollingEnabled()) ? + (ScreenStateHelper.SCREEN_POLLING_TAG_MASK | mScreenState) : mScreenState; + + if(mNfcUnlockManager.isLockscreenPollingEnabled()) + applyRouting(false); + + mDeviceHost.doSetScreenState(screen_state_mask); + /* Start polling loop */ applyRouting(true); @@ -1115,7 +1132,7 @@ public class NfcService implements DeviceHostListener { techCodeToMask.put(TagTechnology.NFC_A, NfcService.NFC_POLL_A); techCodeToMask.put(TagTechnology.NFC_B, NfcService.NFC_POLL_B); - techCodeToMask.put(TagTechnology.NFC_V, NfcService.NFC_POLL_ISO15693); + techCodeToMask.put(TagTechnology.NFC_V, NfcService.NFC_POLL_V); techCodeToMask.put(TagTechnology.NFC_F, NfcService.NFC_POLL_F); techCodeToMask.put(TagTechnology.NFC_BARCODE, NfcService.NFC_POLL_KOVIO); @@ -1596,7 +1613,7 @@ public class NfcService implements DeviceHostListener { if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_F) != 0) techMask |= NFC_POLL_F; if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_V) != 0) - techMask |= NFC_POLL_ISO15693; + techMask |= NFC_POLL_V; if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_BARCODE) != 0) techMask |= NFC_POLL_KOVIO; @@ -1736,11 +1753,12 @@ public class NfcService implements DeviceHostListener { sendMessage(MSG_MOCK_NDEF, msg); } - public void routeAids(String aid, int route) { + public void routeAids(String aid, int route, int aidInfo) { Message msg = mHandler.obtainMessage(); msg.what = MSG_ROUTE_AID; msg.arg1 = route; msg.obj = aid; + msg.arg2 = aidInfo; mHandler.sendMessage(msg); } @@ -1748,11 +1766,15 @@ public class NfcService implements DeviceHostListener { sendMessage(MSG_UNROUTE_AID, aid); } - private byte[] getT3tIdentifierBytes(String systemCode, String nfcId2) { - ByteBuffer buffer = ByteBuffer.allocate(2 + 8); + public int getNciVersion() { + return mDeviceHost.getNciVersion(); + } + + private byte[] getT3tIdentifierBytes(String systemCode, String nfcId2, String t3tPmm) { + ByteBuffer buffer = ByteBuffer.allocate(2 + 8 + 8); /* systemcode + nfcid2 + t3tpmm */ buffer.put(hexStringToBytes(systemCode)); buffer.put(hexStringToBytes(nfcId2)); - + buffer.put(hexStringToBytes(t3tPmm)); byte[] t3tIdBytes = new byte[buffer.position()]; buffer.position(0); buffer.get(t3tIdBytes); @@ -1760,17 +1782,17 @@ public class NfcService implements DeviceHostListener { return t3tIdBytes; } - public void registerT3tIdentifier(String systemCode, String nfcId2) { + public void registerT3tIdentifier(String systemCode, String nfcId2, String t3tPmm) { Log.d(TAG, "request to register LF_T3T_IDENTIFIER"); - byte[] t3tIdentifier = getT3tIdentifierBytes(systemCode, nfcId2); + byte[] t3tIdentifier = getT3tIdentifierBytes(systemCode, nfcId2, t3tPmm); sendMessage(MSG_REGISTER_T3T_IDENTIFIER, t3tIdentifier); } - public void deregisterT3tIdentifier(String systemCode, String nfcId2) { + public void deregisterT3tIdentifier(String systemCode, String nfcId2, String t3tPmm) { Log.d(TAG, "request to deregister LF_T3T_IDENTIFIER"); - byte[] t3tIdentifier = getT3tIdentifierBytes(systemCode, nfcId2); + byte[] t3tIdentifier = getT3tIdentifierBytes(systemCode, nfcId2, t3tPmm); sendMessage(MSG_DEREGISTER_T3T_IDENTIFIER, t3tIdentifier); } @@ -1804,8 +1826,9 @@ public class NfcService implements DeviceHostListener { switch (msg.what) { case MSG_ROUTE_AID: { int route = msg.arg1; + int aidInfo = msg.arg2; String aid = (String) msg.obj; - mDeviceHost.routeAid(hexStringToBytes(aid), route); + mDeviceHost.routeAid(hexStringToBytes(aid), route, aidInfo); // Restart polling config break; } @@ -2041,6 +2064,22 @@ public class NfcService implements DeviceHostListener { removeMessages(MSG_UPDATE_STATS); sendEmptyMessageDelayed(MSG_UPDATE_STATS, STATS_UPDATE_INTERVAL_MS); break; + + case MSG_APPLY_SCREEN_STATE: + mScreenState = (Integer)msg.obj; + + if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED) { + applyRouting(false); + } + int screen_state_mask = (mNfcUnlockManager.isLockscreenPollingEnabled()) ? + (ScreenStateHelper.SCREEN_POLLING_TAG_MASK | mScreenState) : mScreenState; + + if (mNfcUnlockManager.isLockscreenPollingEnabled()) + applyRouting(false); + + mDeviceHost.doSetScreenState(screen_state_mask); + break; + default: Log.e(TAG, "Unknown message received"); break; @@ -2203,9 +2242,12 @@ public class NfcService implements DeviceHostListener { || action.equals(Intent.ACTION_SCREEN_OFF) || action.equals(Intent.ACTION_USER_PRESENT)) { // Perform applyRouting() in AsyncTask to serialize blocking calls - int screenState = ScreenStateHelper.SCREEN_STATE_OFF; + int screenState = mScreenStateHelper.checkScreenState(); if (action.equals(Intent.ACTION_SCREEN_OFF)) { - screenState = ScreenStateHelper.SCREEN_STATE_OFF; + if (mScreenState != ScreenStateHelper.SCREEN_STATE_OFF_LOCKED) { + screenState = mKeyguard.isKeyguardLocked() ? + ScreenStateHelper.SCREEN_STATE_OFF_LOCKED : ScreenStateHelper.SCREEN_STATE_OFF_UNLOCKED; + } } else if (action.equals(Intent.ACTION_SCREEN_ON)) { screenState = mKeyguard.isKeyguardLocked() ? ScreenStateHelper.SCREEN_STATE_ON_LOCKED @@ -2213,8 +2255,10 @@ public class NfcService implements DeviceHostListener { } else if (action.equals(Intent.ACTION_USER_PRESENT)) { screenState = ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED; } - - new ApplyRoutingTask().execute(Integer.valueOf(screenState)); + if (nci_version != NCI_VERSION_2_0) { + new ApplyRoutingTask().execute(Integer.valueOf(screenState)); + } + sendMessage(NfcService.MSG_APPLY_SCREEN_STATE, screenState); } else if (action.equals(Intent.ACTION_USER_SWITCHED)) { int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); synchronized (this) { diff --git a/src/com/android/nfc/ScreenStateHelper.java b/src/com/android/nfc/ScreenStateHelper.java index b044c287..92ba1687 100644 --- a/src/com/android/nfc/ScreenStateHelper.java +++ b/src/com/android/nfc/ScreenStateHelper.java @@ -9,10 +9,16 @@ import android.os.PowerManager; */ class ScreenStateHelper { - static final int SCREEN_STATE_UNKNOWN = 0; - static final int SCREEN_STATE_OFF = 1; - static final int SCREEN_STATE_ON_LOCKED = 2; - static final int SCREEN_STATE_ON_UNLOCKED = 3; + static final int SCREEN_STATE_UNKNOWN = 0x00; + static final int SCREEN_STATE_OFF_UNLOCKED = 0x01; + static final int SCREEN_STATE_OFF_LOCKED = 0x02; + static final int SCREEN_STATE_ON_LOCKED = 0x04; + static final int SCREEN_STATE_ON_UNLOCKED = 0x08; + + //Polling mask + static final int SCREEN_POLLING_TAG_MASK = 0x10; + static final int SCREEN_POLLING_P2P_MASK = 0x20; + static final int SCREEN_POLLING_READER_MASK = 0x40; private final PowerManager mPowerManager; private final KeyguardManager mKeyguardManager; @@ -26,7 +32,11 @@ class ScreenStateHelper { int checkScreenState() { //TODO: fix deprecated api if (!mPowerManager.isScreenOn()) { - return SCREEN_STATE_OFF; + if(mKeyguardManager.isKeyguardLocked()) { + return SCREEN_STATE_OFF_LOCKED; + } else { + return SCREEN_STATE_OFF_UNLOCKED; + } } else if (mKeyguardManager.isKeyguardLocked()) { return SCREEN_STATE_ON_LOCKED; } else { @@ -39,12 +49,14 @@ class ScreenStateHelper { */ static String screenStateToString(int screenState) { switch (screenState) { - case SCREEN_STATE_OFF: - return "OFF"; + case SCREEN_STATE_OFF_LOCKED: + return "OFF_LOCKED"; case SCREEN_STATE_ON_LOCKED: return "ON_LOCKED"; case SCREEN_STATE_ON_UNLOCKED: return "ON_UNLOCKED"; + case SCREEN_STATE_OFF_UNLOCKED: + return "OFF_UNLOCKED"; default: return "UNKNOWN"; } diff --git a/src/com/android/nfc/cardemulation/AidRoutingManager.java b/src/com/android/nfc/cardemulation/AidRoutingManager.java index 96225b71..b21b9e79 100644 --- a/src/com/android/nfc/cardemulation/AidRoutingManager.java +++ b/src/com/android/nfc/cardemulation/AidRoutingManager.java @@ -28,6 +28,7 @@ import java.util.Map; import java.util.Set; public class AidRoutingManager { + static final String TAG = "AidRoutingManager"; static final boolean DBG = false; @@ -40,7 +41,8 @@ public class AidRoutingManager { static final int AID_MATCHING_EXACT_OR_PREFIX = 0x01; // Every routing table entry is matched as a prefix static final int AID_MATCHING_PREFIX_ONLY = 0x02; - + // Every routing table entry can be matched either exact or prefix or subset only + static final int AID_MATCHING_EXACT_OR_SUBSET_OR_PREFIX = 0x03; // This is the default IsoDep protocol route; it means // that for any AID that needs to be routed to this // destination, we won't need to add a rule to the routing @@ -72,6 +74,11 @@ public class AidRoutingManager { private native int doGetDefaultOffHostRouteDestination(); private native int doGetAidMatchingMode(); + final class AidEntry { + boolean isOnHost; + int aidInfo; + } + public AidRoutingManager() { mDefaultRoute = doGetDefaultRouteDestination(); if (DBG) Log.d(TAG, "mDefaultRoute=0x" + Integer.toHexString(mDefaultRoute)); @@ -83,7 +90,12 @@ public class AidRoutingManager { public boolean supportsAidPrefixRouting() { return mAidMatchingSupport == AID_MATCHING_EXACT_OR_PREFIX || - mAidMatchingSupport == AID_MATCHING_PREFIX_ONLY; + mAidMatchingSupport == AID_MATCHING_PREFIX_ONLY || + mAidMatchingSupport == AID_MATCHING_EXACT_OR_SUBSET_OR_PREFIX; + } + + public boolean supportsAidSubsetRouting() { + return mAidMatchingSupport == AID_MATCHING_EXACT_OR_SUBSET_OR_PREFIX; } void clearNfcRoutingTableLocked() { @@ -97,9 +109,23 @@ public class AidRoutingManager { if (DBG) Log.d(TAG, "Unrouting prefix AID " + aid); // Cut off '*' since controller anyway treats all AIDs as a prefix aid = aid.substring(0, aid.length() - 1); - } else if (mAidMatchingSupport == AID_MATCHING_EXACT_OR_PREFIX) { + } else if (mAidMatchingSupport == AID_MATCHING_EXACT_OR_PREFIX || + mAidMatchingSupport == AID_MATCHING_EXACT_OR_SUBSET_OR_PREFIX) { + aid = aid.substring(0, aid.length() - 1); if (DBG) Log.d(TAG, "Unrouting prefix AID " + aid); } + } else if (aid.endsWith("#")) { + if (mAidMatchingSupport == AID_MATCHING_EXACT_ONLY) { + Log.e(TAG, "Device does not support subset AIDs but AID [" + aid + + "] is registered"); + } else if (mAidMatchingSupport == AID_MATCHING_PREFIX_ONLY || + mAidMatchingSupport == AID_MATCHING_EXACT_OR_PREFIX) { + Log.e(TAG, "Device does not support subset AIDs but AID [" + aid + + "] is registered"); + } else if (mAidMatchingSupport == AID_MATCHING_EXACT_OR_SUBSET_OR_PREFIX) { + if (DBG) Log.d(TAG, "Unrouting subset AID " + aid); + aid = aid.substring(0, aid.length() - 1); + } } else { if (DBG) Log.d(TAG, "Unrouting exact AID " + aid); } @@ -108,17 +134,20 @@ public class AidRoutingManager { } } - public boolean configureRouting(HashMap<String, Boolean> aidMap) { + public boolean configureRouting(HashMap<String, AidEntry> aidMap) { SparseArray<Set<String>> aidRoutingTable = new SparseArray<Set<String>>(aidMap.size()); HashMap<String, Integer> routeForAid = new HashMap<String, Integer>(aidMap.size()); + HashMap<String, Integer> infoForAid = new HashMap<String, Integer>(aidMap.size()); // Then, populate internal data structures first - for (Map.Entry<String, Boolean> aidEntry : aidMap.entrySet()) { - int route = aidEntry.getValue() ? ROUTE_HOST : mDefaultOffHostRoute; + for (Map.Entry<String, AidEntry> aidEntry : aidMap.entrySet()) { + int route = aidEntry.getValue().isOnHost ? ROUTE_HOST : mDefaultOffHostRoute; + int aidType = aidEntry.getValue().aidInfo; String aid = aidEntry.getKey(); Set<String> entries = aidRoutingTable.get(route, new HashSet<String>()); entries.add(aid); aidRoutingTable.put(route, entries); routeForAid.put(aid, route); + infoForAid.put(aid, aidType); } synchronized (mLock) { @@ -155,11 +184,11 @@ public class AidRoutingManager { String aid = aidEntry.getKey(); int route = aidEntry.getValue(); if (defaultRouteAid.startsWith(aid) && route != mDefaultRoute) { - if (DBG) - Log.d(TAG, "Adding AID " + defaultRouteAid + " for default " + - "route, because a conflicting shorter AID will be " + - "added to the routing table"); - NfcService.getInstance().routeAids(defaultRouteAid, mDefaultRoute); + if (DBG) Log.d(TAG, "Adding AID " + defaultRouteAid + " for default " + + "route, because a conflicting shorter AID will be " + + "added to the routing table"); + NfcService.getInstance().routeAids(defaultRouteAid, mDefaultRoute, + infoForAid.get(defaultRouteAid)); } } } @@ -180,16 +209,32 @@ public class AidRoutingManager { + Integer.toString(route)); // Cut off '*' since controller anyway treats all AIDs as a prefix NfcService.getInstance().routeAids(aid.substring(0, - aid.length() - 1), route); - } else if (mAidMatchingSupport == AID_MATCHING_EXACT_OR_PREFIX) { + aid.length() - 1), route, infoForAid.get(aid)); + } else if (mAidMatchingSupport == AID_MATCHING_EXACT_OR_PREFIX || + mAidMatchingSupport == AID_MATCHING_EXACT_OR_SUBSET_OR_PREFIX) { if (DBG) Log.d(TAG, "Routing prefix AID " + aid + " to route " + Integer.toString(route)); - NfcService.getInstance().routeAids(aid, route); + NfcService.getInstance().routeAids(aid.substring(0,aid.length() - 1), + route, infoForAid.get(aid)); + } + } else if (aid.endsWith("#")) { + if (mAidMatchingSupport == AID_MATCHING_EXACT_ONLY) { + Log.e(TAG, "Device does not support subset AIDs but AID [" + aid + + "] is registered"); + } else if (mAidMatchingSupport == AID_MATCHING_PREFIX_ONLY || + mAidMatchingSupport == AID_MATCHING_EXACT_OR_PREFIX) { + Log.e(TAG, "Device does not support subset AIDs but AID [" + aid + + "] is registered"); + } else if (mAidMatchingSupport == AID_MATCHING_EXACT_OR_SUBSET_OR_PREFIX) { + if (DBG) Log.d(TAG, "Routing subset AID " + aid + " to route " + + Integer.toString(route)); + NfcService.getInstance().routeAids(aid.substring(0,aid.length() - 1), + route, infoForAid.get(aid)); } } else { if (DBG) Log.d(TAG, "Routing exact AID " + aid + " to route " + Integer.toString(route)); - NfcService.getInstance().routeAids(aid, route); + NfcService.getInstance().routeAids(aid, route, infoForAid.get(aid)); } } } diff --git a/src/com/android/nfc/cardemulation/EnabledNfcFServices.java b/src/com/android/nfc/cardemulation/EnabledNfcFServices.java index 1d2acb58..c06ca26b 100644 --- a/src/com/android/nfc/cardemulation/EnabledNfcFServices.java +++ b/src/com/android/nfc/cardemulation/EnabledNfcFServices.java @@ -117,7 +117,8 @@ public class EnabledNfcFServices implements com.android.nfc.ForegroundUtils.Call return false; } else { if (serviceInfo.getSystemCode().equalsIgnoreCase("NULL") || - serviceInfo.getNfcid2().equalsIgnoreCase("NULL")) { + serviceInfo.getNfcid2().equalsIgnoreCase("NULL") || + serviceInfo.getT3tPmm().equalsIgnoreCase("NULL")) { return false; } } diff --git a/src/com/android/nfc/cardemulation/RegisteredAidCache.java b/src/com/android/nfc/cardemulation/RegisteredAidCache.java index 0ac04b71..402d7936 100644 --- a/src/com/android/nfc/cardemulation/RegisteredAidCache.java +++ b/src/com/android/nfc/cardemulation/RegisteredAidCache.java @@ -24,7 +24,7 @@ import android.nfc.cardemulation.CardEmulation; import android.util.Log; import com.google.android.collect.Maps; - +import java.util.Collections; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -42,14 +42,17 @@ public class RegisteredAidCache { static final boolean DBG = false; + static final int AID_ROUTE_QUAL_SUBSET = 0x20; + static final int AID_ROUTE_QUAL_PREFIX = 0x10; + // mAidServices maps AIDs to services that have registered them. // It's a TreeMap in order to be able to quickly select subsets // of AIDs that conflict with each other. final TreeMap<String, ArrayList<ServiceAidInfo>> mAidServices = new TreeMap<String, ArrayList<ServiceAidInfo>>(); - // mAidCache is a lookup table for quickly mapping an exact or prefix AID to one or - // more handling services. It differs from mAidServices in the sense that it + // mAidCache is a lookup table for quickly mapping an exact or prefix or subset AID + // to one or more handling services. It differs from mAidServices in the sense that it // has already accounted for defaults, and hence its return value // is authoritative for the current set of services and defaults. // It is only valid for the current user. @@ -100,7 +103,7 @@ public class RegisteredAidCache { ApduServiceInfo defaultService = null; String category = null; boolean mustRoute = true; // Whether this AID should be routed at all - + ReslovedPrefixConflictAid prefixInfo = null; @Override public String toString() { return "AidResolveInfo{" + @@ -124,6 +127,7 @@ public class RegisteredAidCache { boolean mNfcEnabled = false; boolean mSupportsPrefixes = false; + boolean mSupportsSubset = false; public RegisteredAidCache(Context context) { mContext = context; @@ -131,9 +135,13 @@ public class RegisteredAidCache { mPreferredPaymentService = null; mPreferredForegroundService = null; mSupportsPrefixes = mRoutingManager.supportsAidPrefixRouting(); + mSupportsSubset = mRoutingManager.supportsAidSubsetRouting(); if (mSupportsPrefixes) { if (DBG) Log.d(TAG, "Controller supports AID prefix routing"); } + if (mSupportsSubset) { + if (DBG) Log.d(TAG, "Controller supports AID subset routing"); + } } public AidResolveInfo resolveAid(String aid) { @@ -144,12 +152,13 @@ public class RegisteredAidCache { return EMPTY_RESOLVE_INFO; } AidResolveInfo resolveInfo = new AidResolveInfo(); - if (mSupportsPrefixes) { - // Our AID cache may contain prefixes which also match this AID, - // so we must find all potential prefixes and merge the ResolveInfo + if (mSupportsPrefixes || mSupportsSubset) { + // Our AID cache may contain prefixes/subset which also match this AID, + // so we must find all potential prefixes or suffixes and merge the ResolveInfo // of those prefixes plus any exact match in a single result. String shortestAidMatch = aid.substring(0, 10); // Minimum AID length is 5 bytes - String longestAidMatch = aid + "*"; // Longest potential matching AID + String longestAidMatch = String.format("%-32s", aid).replace(' ', 'F'); + if (DBG) Log.d(TAG, "Finding AID registrations in range [" + shortestAidMatch + " - " + longestAidMatch + "]"); @@ -159,9 +168,11 @@ public class RegisteredAidCache { resolveInfo.category = CardEmulation.CATEGORY_OTHER; for (Map.Entry<String, AidResolveInfo> entry : matchingAids.entrySet()) { boolean isPrefix = isPrefix(entry.getKey()); - String entryAid = isPrefix ? entry.getKey().substring(0, - entry.getKey().length() - 1) : entry.getKey(); // Cut off '*' if prefix - if (entryAid.equalsIgnoreCase(aid) || (isPrefix && aid.startsWith(entryAid))) { + boolean isSubset = isSubset(entry.getKey()); + String entryAid = (isPrefix || isSubset) ? entry.getKey().substring(0, + entry.getKey().length() - 1):entry.getKey(); // Cut off '*' if prefix + if (entryAid.equalsIgnoreCase(aid) || (isPrefix && aid.startsWith(entryAid)) + || (isSubset && entryAid.startsWith(aid))) { if (DBG) Log.d(TAG, "resolveAid: AID " + entry.getKey() + " matches."); AidResolveInfo entryResolveInfo = entry.getValue(); if (entryResolveInfo.defaultService != null) { @@ -192,6 +203,10 @@ public class RegisteredAidCache { return mSupportsPrefixes; } + public boolean supportsAidSubsetRegistration() { + return mSupportsSubset; + } + public boolean isDefaultServiceForAid(int userId, ComponentName service, String aid) { AidResolveInfo resolveInfo = resolveAid(aid); if (resolveInfo == null || resolveInfo.services == null || @@ -305,27 +320,33 @@ public class RegisteredAidCache { return defaultServiceInfo; } - AidResolveInfo resolvePrefixAidConflictLocked(ArrayList<ServiceAidInfo> prefixServices, + AidResolveInfo resolveAidConflictLocked(ArrayList<ServiceAidInfo> aidServices, ArrayList<ServiceAidInfo> conflictingServices) { - // Find defaults among the prefix services themselves - DefaultServiceInfo prefixDefaultInfo = findDefaultServices(prefixServices); + // Find defaults among the root AID services themselves + DefaultServiceInfo aidDefaultInfo = findDefaultServices(aidServices); // Find any defaults among the children DefaultServiceInfo conflictingDefaultInfo = findDefaultServices(conflictingServices); - - // Three conditions under which the prefix root AID gets to be the default - // 1. A service registering the prefix root AID is the current foreground preferred - // 2. A service registering the prefix root AID is the current tap & pay default AND + AidResolveInfo resolveinfo; + // Three conditions under which the root AID gets to be the default + // 1. A service registering the root AID is the current foreground preferred + // 2. A service registering the root AID is the current tap & pay default AND // no child is the current foreground preferred - // 3. There is only one service for the prefix root AID, and there are no children - if (prefixDefaultInfo.foregroundDefault != null) { + // 3. There is only one service for the root AID, and there are no children + if (aidDefaultInfo.foregroundDefault != null) { if (DBG) Log.d(TAG, "Prefix AID service " + - prefixDefaultInfo.foregroundDefault.service.getComponent() + " has foreground" + + aidDefaultInfo.foregroundDefault.service.getComponent() + " has foreground" + " preference, ignoring conflicting AIDs."); // Foreground default trumps any conflicting services, treat as normal AID conflict // and ignore children - return resolveAidConflictLocked(prefixServices, true); - } else if (prefixDefaultInfo.paymentDefault != null) { + resolveinfo = resolveAidConflictLocked(aidServices, true); + //If the AID is subsetAID check for prefix in same service. + if (isSubset(aidServices.get(0).aid)) { + resolveinfo.prefixInfo = findPrefixConflictForSubsetAid(aidServices.get(0).aid , + new ArrayList<ApduServiceInfo>(){{add(resolveinfo.defaultService);}},true); + } + return resolveinfo; + } else if (aidDefaultInfo.paymentDefault != null) { // Check if any of the conflicting services is foreground default if (conflictingDefaultInfo.foregroundDefault != null) { // Conflicting AID registration is in foreground, trumps prefix tap&pay default @@ -335,9 +356,15 @@ public class RegisteredAidCache { } else { // Prefix service is tap&pay default, treat as normal AID conflict for just prefix if (DBG) Log.d(TAG, "Prefix AID service " + - prefixDefaultInfo.paymentDefault.service.getComponent() + " is payment" + + aidDefaultInfo.paymentDefault.service.getComponent() + " is payment" + " default, ignoring conflicting AIDs."); - return resolveAidConflictLocked(prefixServices, true); + resolveinfo = resolveAidConflictLocked(aidServices, true); + //If the AID is subsetAID check for prefix in same service. + if (isSubset(aidServices.get(0).aid)) { + resolveinfo.prefixInfo = findPrefixConflictForSubsetAid(aidServices.get(0).aid , + new ArrayList<ApduServiceInfo>(){{add(resolveinfo.defaultService);}},true); + } + return resolveinfo; } } else { if (conflictingDefaultInfo.foregroundDefault != null || @@ -349,7 +376,19 @@ public class RegisteredAidCache { // No children that are preferred; add all services of the root // make single service default if no children are present if (DBG) Log.d(TAG, "No service has preference, adding all."); - return resolveAidConflictLocked(prefixServices, conflictingServices.isEmpty()); + resolveinfo = resolveAidConflictLocked(aidServices, conflictingServices.isEmpty()); + //If the AID is subsetAID check for conflicting prefix in all + //conflciting services and root services. + if (isSubset(aidServices.get(0).aid)) { + ArrayList <ApduServiceInfo> apduServiceList = new ArrayList <ApduServiceInfo>(); + for (ServiceAidInfo serviceInfo : conflictingServices) + apduServiceList.add(serviceInfo.service); + for (ServiceAidInfo serviceInfo : aidServices) + apduServiceList.add(serviceInfo.service); + resolveinfo.prefixInfo = + findPrefixConflictForSubsetAid(aidServices.get(0).aid ,apduServiceList,false); + } + return resolveinfo; } } } @@ -360,6 +399,8 @@ public class RegisteredAidCache { for (ApduServiceInfo service : services) { if (DBG) Log.d(TAG, "generateServiceMap component: " + service.getComponent()); List<String> prefixAids = service.getPrefixAids(); + List<String> subSetAids = service.getSubsetAids(); + for (String aid : service.getAids()) { if (!CardEmulation.isValidAid(aid)) { Log.e(TAG, "Aid " + aid + " is not valid."); @@ -368,7 +409,7 @@ public class RegisteredAidCache { if (aid.endsWith("*") && !supportsAidPrefixRegistration()) { Log.e(TAG, "Prefix AID " + aid + " ignored on device that doesn't support it."); continue; - } else if (supportsAidPrefixRegistration() && prefixAids.size() > 0 && !isPrefix(aid)) { + } else if (supportsAidPrefixRegistration() && prefixAids.size() > 0 && isExact(aid)) { // Check if we already have an overlapping prefix registered for this AID boolean foundPrefix = false; for (String prefixAid : prefixAids) { @@ -383,7 +424,26 @@ public class RegisteredAidCache { if (foundPrefix) { continue; } + } else if (aid.endsWith("#") && !supportsAidSubsetRegistration()) { + Log.e(TAG, "Subset AID " + aid + " ignored on device that doesn't support it."); + continue; + } else if (supportsAidSubsetRegistration() && subSetAids.size() > 0 && isExact(aid)) { + // Check if we already have an overlapping subset registered for this AID + boolean foundSubset = false; + for (String subsetAid : subSetAids) { + String plainSubset = subsetAid.substring(0, subsetAid.length() - 1); + if (plainSubset.startsWith(aid)) { + Log.e(TAG, "Ignoring exact AID " + aid + " because subset AID " + plainSubset + + " is already registered"); + foundSubset = true; + break; + } + } + if (foundSubset) { + continue; + } } + ServiceAidInfo serviceAidInfo = new ServiceAidInfo(); serviceAidInfo.aid = aid.toUpperCase(); serviceAidInfo.service = service; @@ -403,18 +463,67 @@ public class RegisteredAidCache { } } + static boolean isExact(String aid) { + return (!((aid.endsWith("*") || (aid.endsWith("#"))))); + } + static boolean isPrefix(String aid) { return aid.endsWith("*"); } - final class PrefixConflicts { + static boolean isSubset(String aid) { + return aid.endsWith("#"); + } + + final class ReslovedPrefixConflictAid { + String prefixAid = null; + boolean matchingSubset = false; + } + + final class AidConflicts { NavigableMap<String, ArrayList<ServiceAidInfo>> conflictMap; final ArrayList<ServiceAidInfo> services = new ArrayList<ServiceAidInfo>(); final HashSet<String> aids = new HashSet<String>(); } - PrefixConflicts findConflictsForPrefixLocked(String prefixAid) { - PrefixConflicts prefixConflicts = new PrefixConflicts(); + ReslovedPrefixConflictAid findPrefixConflictForSubsetAid(String subsetAid , + ArrayList<ApduServiceInfo> prefixServices, boolean priorityRootAid){ + ArrayList<String> prefixAids = new ArrayList<String>(); + String minPrefix = null; + //This functions checks whether there is a prefix AID matching to subset AID + //Because both the subset AID and matching smaller perfix are to be added to routing table. + //1.Finds the prefix matching AID in the services sent. + //2.Find the smallest prefix among matching prefix and add it only if it is not same as susbet AID. + //3..If the subset AID and prefix AID are same add only one AID with both prefix , subset bits set. + // Cut off "#" + String plainSubsetAid = subsetAid.substring(0, subsetAid.length() - 1); + for (ApduServiceInfo service : prefixServices) { + for (String prefixAid : service.getPrefixAids()) { + // Cut off "#" + String plainPrefix= prefixAid.substring(0, prefixAid.length() - 1); + if( plainSubsetAid.startsWith(plainPrefix)) { + if (priorityRootAid) { + if (CardEmulation.CATEGORY_PAYMENT.equals(service.getCategoryForAid(prefixAid)) || + (service.getComponent().equals(mPreferredForegroundService))) + prefixAids.add(prefixAid); + } else { + prefixAids.add(prefixAid); + } + } + } + } + if (prefixAids.size() > 0) + minPrefix = Collections.min(prefixAids); + ReslovedPrefixConflictAid resolvedPrefix = new ReslovedPrefixConflictAid(); + resolvedPrefix.prefixAid = minPrefix; + if ((minPrefix != null ) && + plainSubsetAid.equalsIgnoreCase(minPrefix.substring(0, minPrefix.length() - 1))) + resolvedPrefix.matchingSubset = true; + return resolvedPrefix; + } + + AidConflicts findConflictsForPrefixLocked(String prefixAid) { + AidConflicts prefixConflicts = new AidConflicts(); String plainAid = prefixAid.substring(0, prefixAid.length() - 1); // Cut off "*" String lastAidWithPrefix = String.format("%-32s", plainAid).replace(' ', 'F'); if (DBG) Log.d(TAG, "Finding AIDs in range [" + plainAid + " - " + @@ -434,11 +543,46 @@ public class RegisteredAidCache { return prefixConflicts; } + AidConflicts findConflictsForSubsetAidLocked(String subsetAid) { + AidConflicts subsetConflicts = new AidConflicts(); + // Cut off "@" + String lastPlainAid = subsetAid.substring(0, subsetAid.length() - 1); + // Cut off "@" + String plainSubsetAid = subsetAid.substring(0, subsetAid.length() - 1); + String firstAid = subsetAid.substring(0, 10); + if (DBG) Log.d(TAG, "Finding AIDs in range [" + firstAid + " - " + + lastPlainAid + "]"); + subsetConflicts.conflictMap = new TreeMap(); + for (Map.Entry<String, ArrayList<ServiceAidInfo>> entry : + mAidServices.entrySet()) { + String aid = entry.getKey(); + String plainAid = aid; + if (isSubset(aid) || isPrefix(aid)) + plainAid = aid.substring(0, aid.length() - 1); + if (plainSubsetAid.startsWith(plainAid)) + subsetConflicts.conflictMap.put(entry.getKey(),entry.getValue()); + } + for (Map.Entry<String, ArrayList<ServiceAidInfo>> entry : + subsetConflicts.conflictMap.entrySet()) { + if (!entry.getKey().equalsIgnoreCase(subsetAid)) { + if (DBG) + Log.d(TAG, "AID " + entry.getKey() + " conflicts with subset AID; " + + " adding handling services for conflict resolution."); + subsetConflicts.services.addAll(entry.getValue()); + subsetConflicts.aids.add(entry.getKey()); + } + } + return subsetConflicts; + } + void generateAidCacheLocked() { mAidCache.clear(); // Get all exact and prefix AIDs in an ordered list - PriorityQueue<String> aidsToResolve = new PriorityQueue<String>(mAidServices.keySet()); + final TreeMap<String, AidResolveInfo> aidCache = new TreeMap<String, AidResolveInfo>(); + //aidCache is temproary cache for geenrating the first prefix based lookup table. + PriorityQueue<String> aidsToResolve = new PriorityQueue<String>(mAidServices.keySet()); + aidCache.clear(); while (!aidsToResolve.isEmpty()) { final ArrayList<String> resolvedAids = new ArrayList<String>(); @@ -448,6 +592,7 @@ public class RegisteredAidCache { // A special case is if another service registered the same AID as a prefix, in // which case we want to start with that AID, since it conflicts with this one + // All exact and suffix and prefix AID must be checked for conflicting cases if (aidsToResolve.contains(aidToResolve + "*")) { aidToResolve = aidToResolve + "*"; } @@ -462,17 +607,28 @@ public class RegisteredAidCache { mAidServices.get(aidToResolve)); // Find all conflicting children services - PrefixConflicts prefixConflicts = findConflictsForPrefixLocked(aidToResolve); + AidConflicts prefixConflicts = findConflictsForPrefixLocked(aidToResolve); // Resolve conflicts - AidResolveInfo resolveInfo = resolvePrefixAidConflictLocked(prefixServices, + AidResolveInfo resolveInfo = resolveAidConflictLocked(prefixServices, prefixConflicts.services); - mAidCache.put(aidToResolve, resolveInfo); + aidCache.put(aidToResolve, resolveInfo); resolvedAids.add(aidToResolve); if (resolveInfo.defaultService != null) { // This prefix is the default; therefore, AIDs of all conflicting children // will no longer be evaluated. resolvedAids.addAll(prefixConflicts.aids); + for (String aid : resolveInfo.defaultService.getSubsetAids()) { + if (prefixConflicts.aids.contains(aid)) { + if ((CardEmulation.CATEGORY_PAYMENT.equals(resolveInfo.defaultService.getCategoryForAid(aid))) || + (resolveInfo.defaultService.getComponent().equals(mPreferredForegroundService))) { + AidResolveInfo childResolveInfo = resolveAidConflictLocked(mAidServices.get(aid), false); + aidCache.put(aid,childResolveInfo); + Log.d(TAG, "AID " + aid+ " shared with prefix; " + + "adding subset ."); + } + } + } } else if (resolveInfo.services.size() > 0) { // This means we don't have a default for this prefix and all its // conflicting children. So, for all conflicting AIDs, just add @@ -491,7 +647,7 @@ public class RegisteredAidCache { // Since these are all "children" of the prefix, they don't need // to be routed, since the prefix will already get routed to the host childResolveInfo.mustRoute = false; - mAidCache.put(entry.getKey(),childResolveInfo); + aidCache.put(entry.getKey(),childResolveInfo); resolvedAids.add(entry.getKey()); foundChildService |= !childResolveInfo.services.isEmpty(); } @@ -512,7 +668,7 @@ public class RegisteredAidCache { if (DBG) Log.d(TAG, "Exact AID, resolving."); final ArrayList<ServiceAidInfo> conflictingServiceInfos = new ArrayList<ServiceAidInfo>(mAidServices.get(aidToResolve)); - mAidCache.put(aidToResolve, resolveAidConflictLocked(conflictingServiceInfos, true)); + aidCache.put(aidToResolve, resolveAidConflictLocked(conflictingServiceInfos, true)); resolvedAids.add(aidToResolve); } @@ -521,6 +677,103 @@ public class RegisteredAidCache { aidsToResolve.removeAll(resolvedAids); resolvedAids.clear(); } + PriorityQueue<String> reversedQueue = new PriorityQueue<String>(1, Collections.reverseOrder()); + reversedQueue.addAll(aidCache.keySet()); + while (!reversedQueue.isEmpty()) { + final ArrayList<String> resolvedAids = new ArrayList<String>(); + + String aidToResolve = reversedQueue.peek(); + if (isPrefix(aidToResolve)) { + String matchingSubset = aidToResolve.substring(0,aidToResolve.length()-1 ) + "#"; + if (DBG) Log.d(TAG, "matching subset"+matchingSubset); + if (reversedQueue.contains(matchingSubset)) + aidToResolve = aidToResolve.substring(0,aidToResolve.length()-1) + "#"; + } + if (isSubset(aidToResolve)) { + if (DBG) Log.d(TAG, "subset resolving aidToResolve "+aidToResolve); + final ArrayList<ServiceAidInfo> subsetServices = new ArrayList<ServiceAidInfo>( + mAidServices.get(aidToResolve)); + + // Find all conflicting children services + AidConflicts aidConflicts = findConflictsForSubsetAidLocked(aidToResolve); + + // Resolve conflicts + AidResolveInfo resolveInfo = resolveAidConflictLocked(subsetServices, + aidConflicts.services); + mAidCache.put(aidToResolve, resolveInfo); + resolvedAids.add(aidToResolve); + if (resolveInfo.defaultService != null) { + // This subset is the default; therefore, AIDs of all conflicting children + // will no longer be evaluated.Check for any prefix matching in the same service + if (resolveInfo.prefixInfo != null && resolveInfo.prefixInfo.prefixAid != null && + !resolveInfo.prefixInfo.matchingSubset) { + if (DBG) + Log.d(TAG, "AID default " + resolveInfo.prefixInfo.prefixAid + + " prefix AID shared with dsubset root; " + + " adding prefix aid"); + AidResolveInfo childResolveInfo = resolveAidConflictLocked( + mAidServices.get(resolveInfo.prefixInfo.prefixAid), false); + mAidCache.put(resolveInfo.prefixInfo.prefixAid, childResolveInfo); + } + resolvedAids.addAll(aidConflicts.aids); + } else if (resolveInfo.services.size() > 0) { + // This means we don't have a default for this subset and all its + // conflicting children. So, for all conflicting AIDs, just add + // all handling services without setting a default + boolean foundChildService = false; + for (Map.Entry<String, ArrayList<ServiceAidInfo>> entry : + aidConflicts.conflictMap.entrySet()) { + // We need to add shortest prefix among them. + if (!entry.getKey().equalsIgnoreCase(aidToResolve)) { + if (DBG) + Log.d(TAG, "AID " + entry.getKey() + " shared with subset root; " + + " adding all handling services."); + AidResolveInfo childResolveInfo = resolveAidConflictLocked( + entry.getValue(), false); + // Special case: in this case all children AIDs must be routed to the + // host, so we can ask the user which service is preferred. + // Since these are all "children" of the subset, they don't need + // to be routed, since the subset will already get routed to the host + childResolveInfo.mustRoute = false; + mAidCache.put(entry.getKey(),childResolveInfo); + resolvedAids.add(entry.getKey()); + foundChildService |= !childResolveInfo.services.isEmpty(); + } + } + if(resolveInfo.prefixInfo != null && + resolveInfo.prefixInfo.prefixAid != null && + !resolveInfo.prefixInfo.matchingSubset) { + AidResolveInfo childResolveInfo = resolveAidConflictLocked( + mAidServices.get(resolveInfo.prefixInfo.prefixAid), false); + mAidCache.put(resolveInfo.prefixInfo.prefixAid, childResolveInfo); + if (DBG) + Log.d(TAG, "AID " + resolveInfo.prefixInfo.prefixAid + + " prefix AID shared with subset root; " + + " adding prefix aid"); + } + // Special case: if in the end we didn't add any children services, + // and the subset has only one service, make that default + if (!foundChildService && resolveInfo.services.size() == 1) { + resolveInfo.defaultService = resolveInfo.services.get(0); + } + } else { + // This subset is not handled at all; we will evaluate + // the children separately in next passes. + } + } else { + // Exact AID and no other conflicting AID registrations present. This is + // true because reversedQueue is lexicographically ordered in revrese, and + // so by necessity all other AIDs are different than this AID or shorter. + if (DBG) Log.d(TAG, "Exact or Prefix AID."+aidToResolve); + mAidCache.put(aidToResolve, aidCache.get(aidToResolve)); + resolvedAids.add(aidToResolve); + } + + // Remove the AIDs we resolved from the list of AIDs to resolve + if (DBG) Log.d(TAG, "AIDs: " + resolvedAids + " were resolved."); + reversedQueue.removeAll(resolvedAids); + resolvedAids.clear(); + } updateRoutingLocked(); } @@ -530,7 +783,7 @@ public class RegisteredAidCache { if (DBG) Log.d(TAG, "Not updating routing table because NFC is off."); return; } - final HashMap<String, Boolean> routingEntries = Maps.newHashMap(); + final HashMap<String, AidRoutingManager.AidEntry> routingEntries = Maps.newHashMap(); // For each AID, find interested services for (Map.Entry<String, AidResolveInfo> aidEntry: mAidCache.entrySet()) { @@ -540,19 +793,30 @@ public class RegisteredAidCache { if (DBG) Log.d(TAG, "Not routing AID " + aid + " on request."); continue; } + AidRoutingManager.AidEntry aidType = mRoutingManager.new AidEntry(); + if (aid.endsWith("#")) { + aidType.aidInfo |= AID_ROUTE_QUAL_SUBSET; + } + if(aid.endsWith("*") || (resolveInfo.prefixInfo != null && + resolveInfo.prefixInfo.matchingSubset)) { + aidType.aidInfo |= AID_ROUTE_QUAL_PREFIX; + } if (resolveInfo.services.size() == 0) { // No interested services } else if (resolveInfo.defaultService != null) { // There is a default service set, route to where that service resides - // either on the host (HCE) or on an SE. - routingEntries.put(aid, resolveInfo.defaultService.isOnHost()); + aidType.isOnHost = resolveInfo.defaultService.isOnHost(); + routingEntries.put(aid, aidType); } else if (resolveInfo.services.size() == 1) { // Only one service, but not the default, must route to host // to ask the user to choose one. - routingEntries.put(aid, true); + aidType.isOnHost = true; + routingEntries.put(aid, aidType); } else if (resolveInfo.services.size() > 1) { // Multiple services, need to route to host to ask - routingEntries.put(aid, true); + aidType.isOnHost = true; + routingEntries.put(aid, aidType); } } mRoutingManager.configureRouting(routingEntries); diff --git a/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java b/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java index de7ef8f4..6391a51f 100644 --- a/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java +++ b/src/com/android/nfc/cardemulation/RegisteredT3tIdentifiersCache.java @@ -46,10 +46,12 @@ public class RegisteredT3tIdentifiersCache { final class T3tIdentifier { public final String systemCode; public final String nfcid2; + public final String t3tPmm; - T3tIdentifier(String systemCode, String nfcid2) { + T3tIdentifier(String systemCode, String nfcid2, String t3tPmm) { this.systemCode = systemCode; this.nfcid2 = nfcid2; + this.t3tPmm = t3tPmm; } @Override @@ -138,7 +140,7 @@ public class RegisteredT3tIdentifiersCache { Map.Entry<String, NfcFServiceInfo> entry = (Map.Entry<String, NfcFServiceInfo>) it.next(); t3tIdentifiers.add(new T3tIdentifier( - entry.getValue().getSystemCode(), entry.getValue().getNfcid2())); + entry.getValue().getSystemCode(), entry.getValue().getNfcid2(), entry.getValue().getT3tPmm())); } mRoutingManager.configureRouting(t3tIdentifiers); } diff --git a/src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java b/src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java index 70195255..a2d2a777 100644 --- a/src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java +++ b/src/com/android/nfc/cardemulation/SystemCodeRoutingManager.java @@ -59,25 +59,26 @@ public class SystemCodeRoutingManager { for (T3tIdentifier t3tIdentifier : toBeRemoved) { if (DBG) Log.d(TAG, "deregisterNfcFSystemCodeonDh:"); NfcService.getInstance().deregisterT3tIdentifier( - t3tIdentifier.systemCode, t3tIdentifier.nfcid2); + t3tIdentifier.systemCode, t3tIdentifier.nfcid2, t3tIdentifier.t3tPmm); } for (T3tIdentifier t3tIdentifier : toBeAdded) { if (DBG) Log.d(TAG, "registerNfcFSystemCodeonDh:"); NfcService.getInstance().registerT3tIdentifier( - t3tIdentifier.systemCode, t3tIdentifier.nfcid2); + t3tIdentifier.systemCode, t3tIdentifier.nfcid2 , t3tIdentifier.t3tPmm); } if (DBG) { Log.d(TAG, "(Before) mConfiguredT3tIdentifiers: size=" + mConfiguredT3tIdentifiers.size()); for (T3tIdentifier t3tIdentifier : mConfiguredT3tIdentifiers) { Log.d(TAG, " " + t3tIdentifier.systemCode + - "/" + t3tIdentifier.nfcid2); + "/" + t3tIdentifier.t3tPmm); } Log.d(TAG, "(After) mConfiguredT3tIdentifiers: size=" + t3tIdentifiers.size()); for (T3tIdentifier t3tIdentifier : t3tIdentifiers) { Log.d(TAG, " " + t3tIdentifier.systemCode + - "/" + t3tIdentifier.nfcid2); + "/" + t3tIdentifier.nfcid2 + + "/" + t3tIdentifier.t3tPmm); } } mConfiguredT3tIdentifiers = t3tIdentifiers; |