/*
* 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;
}
}