| |
| /* |
| * Copyright (C) 2013 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 the listen-mode routing table. |
| */ |
| |
| #include <cutils/log.h> |
| #include <ScopedLocalRef.h> |
| #include "config.h" |
| #include "JavaClassConstants.h" |
| #include "RoutingManager.h" |
| |
| RoutingManager::RoutingManager () |
| { |
| } |
| |
| RoutingManager::~RoutingManager () |
| { |
| NFA_EeDeregister (nfaEeCallback); |
| } |
| |
| bool RoutingManager::initialize (nfc_jni_native_data* native) |
| { |
| static const char fn [] = "RoutingManager::initialize()"; |
| unsigned long num = 0; |
| mNativeData = native; |
| |
| tNFA_STATUS nfaStat; |
| { |
| 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 (); |
| } |
| |
| // Get the "default" route |
| if (GetNumValue("DEFAULT_ISODEP_ROUTE", &num, sizeof(num))) |
| mDefaultEe = num; |
| else |
| mDefaultEe = 0x00; |
| |
| ALOGD("%s: default route is 0x%02X", fn, mDefaultEe); |
| setDefaultRouting(); |
| return true; |
| } |
| |
| RoutingManager& RoutingManager::getInstance () |
| { |
| static RoutingManager manager; |
| return manager; |
| } |
| |
| void RoutingManager::setDefaultRouting() |
| { |
| tNFA_STATUS nfaStat; |
| SyncEventGuard guard (mRoutingEvent); |
| // Default routing for NFC-A technology |
| nfaStat = NFA_EeSetDefaultTechRouting (mDefaultEe, 0x01, 0, 0); |
| if (nfaStat == NFA_STATUS_OK) |
| mRoutingEvent.wait (); |
| else |
| ALOGE ("Fail to set default tech routing"); |
| |
| // Default routing for IsoDep protocol |
| nfaStat = NFA_EeSetDefaultProtoRouting(mDefaultEe, NFA_PROTOCOL_MASK_ISO_DEP, 0, 0); |
| if (nfaStat == NFA_STATUS_OK) |
| mRoutingEvent.wait (); |
| else |
| ALOGE ("Fail to set default proto routing"); |
| |
| // Tell the UICC to only listen on Nfc-A |
| nfaStat = NFA_CeConfigureUiccListenTech (mDefaultEe, 0x01); |
| if (nfaStat != NFA_STATUS_OK) |
| ALOGE ("Failed to configure UICC listen technologies"); |
| |
| // Tell the host-routing to only listen on Nfc-A |
| nfaStat = NFA_CeSetIsoDepListenTech(0x01); |
| if (nfaStat != NFA_STATUS_OK) |
| ALOGE ("Failed to configure CE IsoDep technologies"); |
| |
| // Register a wild-card for AIDs routed to the host |
| nfaStat = NFA_CeRegisterAidOnDH (NULL, 0, stackCallback); |
| if (nfaStat != NFA_STATUS_OK) |
| ALOGE("Failed to register wildcard AID for DH"); |
| |
| // Commit the routing configuration |
| nfaStat = NFA_EeUpdateNow(); |
| if (nfaStat != NFA_STATUS_OK) |
| ALOGE("Failed to commit routing configuration"); |
| } |
| |
| bool RoutingManager::addAidRouting(const UINT8* aid, UINT8 aidLen, int route) |
| { |
| static const char fn [] = "RoutingManager::addAidRouting"; |
| ALOGD ("%s: enter", fn); |
| tNFA_STATUS nfaStat = NFA_EeAddAidRouting(route, aidLen, (UINT8*) aid, 0x01); |
| if (nfaStat == NFA_STATUS_OK) |
| { |
| ALOGD ("%s: routed AID", fn); |
| return true; |
| } else |
| { |
| ALOGE ("%s: failed to route AID", fn); |
| return false; |
| } |
| } |
| |
| bool RoutingManager::removeAidRouting(const UINT8* aid, UINT8 aidLen) |
| { |
| static const char fn [] = "RoutingManager::removeAidRouting"; |
| ALOGD ("%s: enter", fn); |
| tNFA_STATUS nfaStat = NFA_EeRemoveAidRouting(aidLen, (UINT8*) aid); |
| if (nfaStat == NFA_STATUS_OK) |
| { |
| ALOGD ("%s: removed AID", fn); |
| return true; |
| } else |
| { |
| ALOGE ("%s: failed to remove AID", fn); |
| return false; |
| } |
| } |
| |
| bool RoutingManager::commitRouting() |
| { |
| tNFA_STATUS nfaStat = NFA_EeUpdateNow(); |
| return (nfaStat == NFA_STATUS_OK); |
| } |
| |
| void RoutingManager::notifyActivated () |
| { |
| JNIEnv* e = NULL; |
| ScopedAttach attach(mNativeData->vm, &e); |
| if (e == NULL) |
| { |
| ALOGE ("jni env is null"); |
| return; |
| } |
| |
| e->CallVoidMethod (mNativeData->manager, android::gCachedNfcManagerNotifyHostEmuActivated); |
| if (e->ExceptionCheck()) |
| { |
| e->ExceptionClear(); |
| ALOGE ("fail notify"); |
| } |
| } |
| |
| void RoutingManager::notifyDeactivated () |
| { |
| SecureElement::getInstance().notifyListenModeState (false); |
| |
| JNIEnv* e = NULL; |
| ScopedAttach attach(mNativeData->vm, &e); |
| if (e == NULL) |
| { |
| ALOGE ("jni env is null"); |
| return; |
| } |
| |
| e->CallVoidMethod (mNativeData->manager, android::gCachedNfcManagerNotifyHostEmuDeactivated); |
| if (e->ExceptionCheck()) |
| { |
| e->ExceptionClear(); |
| ALOGE ("fail notify"); |
| } |
| } |
| |
| void RoutingManager::handleData (const UINT8* data, UINT8 dataLen) |
| { |
| if (dataLen <= 0) |
| { |
| ALOGE("no data"); |
| return; |
| } |
| |
| JNIEnv* e = NULL; |
| ScopedAttach attach(mNativeData->vm, &e); |
| if (e == NULL) |
| { |
| ALOGE ("jni env is null"); |
| return; |
| } |
| |
| ScopedLocalRef<jobject> dataJavaArray(e, e->NewByteArray(dataLen)); |
| if (dataJavaArray.get() == NULL) |
| { |
| ALOGE ("fail allocate array"); |
| return; |
| } |
| |
| e->SetByteArrayRegion ((jbyteArray)dataJavaArray.get(), 0, dataLen, (jbyte *)data); |
| if (e->ExceptionCheck()) |
| { |
| e->ExceptionClear(); |
| ALOGE ("fail fill array"); |
| return; |
| } |
| |
| e->CallVoidMethod (mNativeData->manager, android::gCachedNfcManagerNotifyHostEmuData, dataJavaArray.get()); |
| if (e->ExceptionCheck()) |
| { |
| e->ExceptionClear(); |
| ALOGE ("fail notify"); |
| } |
| } |
| |
| void RoutingManager::stackCallback (UINT8 event, tNFA_CONN_EVT_DATA* eventData) |
| { |
| static const char fn [] = "RoutingManager::stackCallback"; |
| ALOGD("%s: event=0x%X", fn, event); |
| RoutingManager& routingManager = RoutingManager::getInstance(); |
| |
| 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); |
| } |
| 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); |
| } |
| break; |
| |
| case NFA_CE_ACTIVATED_EVT: |
| { |
| routingManager.notifyActivated(); |
| } |
| break; |
| case NFA_DEACTIVATED_EVT: |
| case NFA_CE_DEACTIVATED_EVT: |
| { |
| routingManager.notifyDeactivated(); |
| } |
| 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); |
| getInstance().handleData(ce_data.p_data, ce_data.len); |
| } |
| break; |
| } |
| |
| } |
| /******************************************************************************* |
| ** |
| ** Function: nfaEeCallback |
| ** |
| ** Description: Receive execution environment-related events from stack. |
| ** event: Event code. |
| ** eventData: Event data. |
| ** |
| ** Returns: None |
| ** |
| *******************************************************************************/ |
| void RoutingManager::nfaEeCallback (tNFA_EE_EVT event, tNFA_EE_CBACK_DATA* eventData) |
| { |
| static const char fn [] = "RoutingManager::nfaEeCallback"; |
| |
| SecureElement& se = SecureElement::getInstance(); |
| RoutingManager& routingManager = RoutingManager::getInstance(); |
| |
| switch (event) |
| { |
| case NFA_EE_REGISTER_EVT: |
| { |
| SyncEventGuard guard (routingManager.mEeRegisterEvent); |
| ALOGD ("%s: NFA_EE_REGISTER_EVT; status=%u", fn, eventData->ee_register); |
| routingManager.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, se.mActiveEeHandle); |
| se.notifyModeSet(eventData->mode_set.ee_handle, eventData->mode_set.status); |
| } |
| break; |
| |
| case NFA_EE_SET_TECH_CFG_EVT: |
| { |
| ALOGD ("%s: NFA_EE_SET_TECH_CFG_EVT; status=0x%X", fn, eventData->status); |
| SyncEventGuard guard(routingManager.mRoutingEvent); |
| routingManager.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(routingManager.mRoutingEvent); |
| routingManager.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) ) |
| { |
| se.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); |
| 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); |
| } |
| break; |
| |
| case NFA_EE_REMOVE_AID_EVT: |
| { |
| ALOGD ("%s: NFA_EE_REMOVE_AID_EVT status=%u", fn, eventData->status); |
| } |
| 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); |
| } |
| break; |
| |
| default: |
| ALOGE ("%s: unknown event=%u ????", fn, event); |
| break; |
| } |
| } |