/*
* Copyright 2015 Licel Corporation.
*
* 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.licel.jcardsim.framework.service;
import javacard.framework.APDU;
import javacard.framework.ISOException;
import javacard.framework.JCSystem;
import javacard.framework.ISO7816;
import javacard.framework.Util;
import com.licel.jcardsim.base.SimulatorSystem;
import javacard.framework.service.Dispatcher;
import javacard.framework.service.Service;
import javacard.framework.service.ServiceException;
public class DispatcherImpl extends Dispatcher {
private short _maxServices;
private Service[] _services;
private byte[] _phases;
public DispatcherImpl(short maxServices) throws ServiceException {
super(maxServices);
_services = new Service[maxServices];
SimulatorSystem.instance().setJavaOwner(_services, this);
_phases = new byte[maxServices];
SimulatorSystem.instance().setJavaOwner(_phases, this);
_maxServices = maxServices;
}
@Override
public void addService(Service service, byte phase) throws ServiceException {
if (phase <= PROCESS_NONE || phase > PROCESS_OUTPUT_DATA || service == null)
ServiceException.throwIt(ServiceException.ILLEGAL_PARAM);
short i = (short) 0;
short index = _maxServices;
while (i < _maxServices) {
if (_services[i] == null && index == _maxServices)
index = i;
if (_services[i] == service && _phases[i] == phase) {
return;
}
i++;
}
if (index == _maxServices)
ServiceException.throwIt(ServiceException.DISPATCH_TABLE_FULL);
final boolean doTrans = (JCSystem.getTransactionDepth() == (byte) 0);
if (doTrans)
JCSystem.beginTransaction();
_services[index] = service;
_phases[index] = phase;
if (doTrans)
JCSystem.commitTransaction();
}
@Override
public void removeService(Service service, byte phase)
throws ServiceException {
if (phase <= PROCESS_NONE || phase > PROCESS_OUTPUT_DATA
|| service == null)
ServiceException.throwIt(ServiceException.ILLEGAL_PARAM);
short i = (short) 0;
while (i < _maxServices) {
if (_services[i] == service && _phases[i] == phase)
break;
i++;
}
if (i != _maxServices) {
final boolean doTrans = (JCSystem.getTransactionDepth() == (byte) 0);
if (doTrans)
JCSystem.beginTransaction();
_services[i] = null;
_phases[i] = (byte) 0;
if (doTrans)
JCSystem.commitTransaction();
}
}
@Override
public Exception dispatch(APDU command, byte phase) throws ServiceException {
if (phase <= PROCESS_NONE || phase > PROCESS_OUTPUT_DATA)
ServiceException.throwIt(ServiceException.ILLEGAL_PARAM);
Exception result = null;
byte phases = phase;
try {
while (phases <= PROCESS_OUTPUT_DATA) {
dispatchPhase(command, phases);
phases++;
}
} catch (Exception e) {
result = e;
}
return result;
}
private void dispatchPhase(APDU command, byte phase) {
short service = (short) 0;
while (service < _maxServices) {
if (_services[service] == null || _phases[service] != phase)
break;
if (phase == PROCESS_INPUT_DATA) {
if (_services[service].processDataIn(command))
return;
} else {
if (phase == PROCESS_COMMAND) {
if (_services[service].processCommand(command))
return;
} else {
if (command.getCurrentState() == APDU.STATE_OUTGOING) {
if (_services[service].processDataOut(command)) {
short len = (short) (command.getBuffer()[ISO7816.OFFSET_LC] & 0xff);
command.setOutgoingLength(len);
command.sendBytes(ISO7816.OFFSET_CDATA, len);
ISOException.throwIt(Util.makeShort(command
.getBuffer()[ISO7816.OFFSET_P1], command
.getBuffer()[ISO7816.OFFSET_P2]));
}
} else {
return;
}
}
}
service++;
}
}
@Override
public void process(APDU command) throws ISOException {
Exception e = dispatch(command, PROCESS_INPUT_DATA);
if (e instanceof ISOException) {
if (((ISOException) e).getReason() != ISO7816.SW_NO_ERROR)
throw (ISOException) e;
}
if (e == null)
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}
}