();
Integer channelIdCounter = 0;
Omapi omapi = this;
/**
* Create a new OMAPI client instance.
* @param context application environment context
* @param callback instance of OmapiCallback that implements the OMAPI callback methods
*/
Omapi(Context context, OmapiCallback callback) {
seService = new SEService(context, new syncExec(), mListener);
assert(callback != null);
this.callback = callback;
Log.d("OMAPI", "Connecting SE-Service...\n");
}
private final OnConnectedListener mListener = new OnConnectedListener() {
@Override
public void onConnected() {
Log.d("OMAPI", "SE-Service connection successful!\n");
callback.omapiConnected(omapi);
}
};
private class syncExec implements Executor {
public void execute(Runnable r) {
r.run();
}
}
//Ensure SE-Service is present and connected
private void ensureSeService() throws Exception {
if (seService == null)
throw new Exception("cannot get channel, SE-Service not present!");
if (!seService.isConnected())
throw new Exception("cannot get channel, SE-Service not connected!");
}
//Get a session for a specified reader. In case the specified reader has no session open yet,
//create a new one. Sessions stay open throughout the whole lifetime of this object.
private Session getOrCreateSession(String readerName) throws Exception {
Session session;
Reader[] readers = seService.getReaders();
for (Reader reader : readers) {
if (reader.getName().equals(readerName) && reader.isSecureElementPresent()) {
if (sessions.containsKey(readerName)) {
session = sessions.get(readerName);
assert (session != null);
} else {
session = reader.openSession();
sessions.put(readerName, session);
}
return session;
}
}
throw new Exception("reader " + readerName + " not found!\n");
}
/**
* Scan for useable readers.
* The result is a Map that contains the available readers as key value pairs, where the
* key is the reader name and the value the ATR of the card. Empty readers will be ignored.
* @return Map that contains reader names and card ATR values
*/
public Map scan() {
try {
Map usable_readers = new HashMap();
ensureSeService();
Reader[] readers = seService.getReaders();
if (readers.length == 0)
throw new Exception("no reader available!");
for (Reader reader : readers) {
if (reader.isSecureElementPresent()) {
Session session = reader.openSession();
byte[] atr = session.getATR();
if (atr != null) {
Log.d("OMAPI", "found reader: " + reader.getName() + " (ATR=" + Utils.b2h(atr) + ")\n");
usable_readers.put(reader.getName(), atr);
} else {
Log.d("OMAPI", "found reader: " + reader.getName() + " (no ATR, unresponsive?)\n");
}
session.close();
} else {
Log.d("OMAPI","found reader: " + reader.getName() + " (empty)\n");
}
}
return usable_readers;
} catch (Exception e) {
Log.e("OMAPI",e.getMessage() + "\n");
return null;
}
}
/**
* Perform an APDU transaction on the card.
* @param readerName string that contains the reader name (e.g. "SIM1")
* @return array of bytes that contains the ATR (from card) or, throws Exception on error
*/
public byte[] getAtr(String readerName) throws Exception {
try {
ensureSeService();
Session session = getOrCreateSession(readerName);
byte[] atr = session.getATR();
if (atr != null) {
Log.d("OMAPI", "ATR: " + Utils.b2h(atr) + "\n");
return atr;
}
throw new Exception( "ATR request failed!\n");
} catch (Exception e) {
Log.e("OMAPI", e.getMessage() + "\n");
throw e;
}
}
/**
* Open a channel on the specified reader to the specified AID.
* @param readerName string that contains the reader name (e.g. "SIM1")
* @param aid array of bytes that contains the AID of the application to access
* @return OMAPI Channel number on success, throws Exception on error
*/
public int open(String readerName, byte[] aid) throws Exception {
try {
ensureSeService();
Session session = getOrCreateSession(readerName);
Channel channel = session.openLogicalChannel(aid);
if (channel == null)
throw new Exception(String.format("could not open channel for AID (%s) on reader: %s!\n",
Utils.b2h(aid), readerName));
Log.d("OMAPI", String.format("sucessfully opend channel for AID (%s) on reader: %s\n",
Utils.b2h(aid), readerName));
channelIdCounter++;
channels.put(channelIdCounter, channel);
return channelIdCounter;
} catch (Exception e) {
Log.e("OMAPI",e.getMessage() + "\n");
throw e;
}
}
/**
* Perform an APDU transaction on the card.
* @param channelId id-number of the OMAPI Channel
* @param apdu array of bytes that contains the command APDU (to card)
* @return array of bytes that contains the response APDU (from card) or throws Exception on error
*/
public byte[] transact(int channelId, byte[] apdu) throws Exception {
try {
ensureSeService();
if (!channels.containsKey(channelId))
throw new Exception(String.format("no channel open under channelId = %d", channelId));
Channel channel = channels.get(channelId);
Log.d("OMAPI","APDU TX: " + Utils.b2h(apdu) + "\n");
byte[] response = channel.transmit(apdu);
if (response == null)
throw new Exception("unresponsive card!");
Log.d("OMAPI","APDU RX: " + Utils.b2h(response) + "\n");
return response;
} catch (Exception e) {
Log.e("OMAPI",e.getMessage() + "\n");
throw e;
}
}
/**
* Close an OMAPI channel.
* @param channelId id-number of the OMAPI Channel
*/
public void close(int channelId) {
try {
ensureSeService();
if (!channels.containsKey(channelId))
throw new Exception(String.format("no channel open under channelId = %d", channelId));
Channel channel = channels.get(channelId);
Log.d("OMAPI", String.format("closing reader channel %d ...\n", channelId));
channel.close();
Log.d("OMAPI", String.format("channel %d closed.\n", channelId));
channels.remove(channel);
} catch (Exception e) {
Log.e("OMAPI",e.getMessage() + "\n");
}
}
/**
* Shutdown OMAPI SE-Service. This shuts down the SE-Service. There is no method to recover
* from the shutdown state again. The APU user must create a new instance.
*/
public void shutdown() {
/* Inform callback handler that the SE-Service is going to disconnect. */
callback.omapiDisconnected();
/* Make sure all channels and sessions are orderly closed. */
for (Channel channel : channels.values())
channel.close();
for (Session session : sessions.values())
session.close();
sessions = new HashMap();
channels = new HashMap();
/* Shutdown SE-Service */
seService.shutdown();
}
}