In order to debug a problem, I wrote the following applet. This applet simply registers itself for multiple events and then when an event triggers, it set correponding bits (based on the received event type) in a variable named receivedEvents
. Finally we can receive the value of this variable using a SELECT APDU command.
package f0r.god.sake;
import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.ISOException;
import sim.toolkit.EnvelopeHandler;
import sim.toolkit.ProactiveHandler;
import sim.toolkit.ToolkitConstants;
import sim.toolkit.ToolkitException;
import sim.toolkit.ToolkitInterface;
import sim.toolkit.ToolkitRegistry;
public class SampleSTKApplet extends Applet implements ToolkitInterface, ToolkitConstants {
private ToolkitRegistry toolkitRegistry;
private byte receivedEvents = (byte) 0;
private byte timerId;
private static byte[] TIMER_VALUE = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x01};
private byte menuItemId;
static byte[] menuItemText = new byte[] { 'C', 'l', 'i', 'c', 'k', ' ', 'H', 'e', 'r', 'e'};
private SampleSTKApplet() {
toolkitRegistry = ToolkitRegistry.getEntry();
menuItemId = toolkitRegistry.initMenuEntry(menuItemText, (short)0, (short)menuItemText.length,
PRO_CMD_SELECT_ITEM, false, (byte)0, (short)0);
toolkitRegistry.setEvent(EVENT_PROFILE_DOWNLOAD);
toolkitRegistry.requestPollInterval((short)15);
timerId = toolkitRegistry.allocateTimer();
}
public static void install(byte[] bArray, short bOffset, byte bLength) {
SampleSTKApplet applet = new SampleSTKApplet();
applet.register();
}
public void process(APDU apdu) throws ISOException {
if (selectingApplet())
ISOException.throwIt((short)((short)0x9000 | receivedEvents));
}
public void processToolkit(byte event) throws ToolkitException {
EnvelopeHandler envHdlr = EnvelopeHandler.getTheHandler();
ProactiveHandler proHdlr = ProactiveHandler.getTheHandler();
switch(event){
case EVENT_MENU_SELECTION:
receivedEvents = (byte)(receivedEvents | 0x01); // 0b00000001
byte selectedItemId = envHdlr.getItemIdentifier();
if (selectedItemId == menuItemId) {
proHdlr.init((byte) PRO_CMD_TIMER_MANAGEMENT, (byte) 0, DEV_ID_ME);
proHdlr.appendTLV((byte) (TAG_TIMER_IDENTIFIER | TAG_SET_CR), timerId);
proHdlr.appendTLV((byte) (TAG_TIMER_VALUE | TAG_SET_CR), TIMER_VALUE, (short) 0,
(short) TIMER_VALUE.length);
proHdlr.send();
}
break;
case EVENT_TIMER_EXPIRATION:
receivedEvents = (byte)(receivedEvents | 0x02); // 0b00000010
break;
case EVENT_PROFILE_DOWNLOAD:
receivedEvents = (byte)(receivedEvents | 0x04); // 0b00000100
break;
case EVENT_STATUS_COMMAND:
receivedEvents = (byte)(receivedEvents | 0x08); // 0b00001000
break;
default:
receivedEvents = (byte)(receivedEvents | 0x10); // 0b00010000
}
}
}
I compiled it and installed it successfuly on my SIM card.
Then:
EVENT_TIMER_EXPIRATION
, EVENT_STATUS_COMMAND
and EVENT_PROFILE_DOWNLOAD
events.Now as I think that all the events have been triggered, I expect SW = 0x900F (=0x9000 | 0b00001111)
or SW = 0x901F (= 0x9000 | 0b00011111)
in response of SELECT APDU command. But that's not what I receive:
ebr@him:~/SomePath$ apdu_sender -a 00a4040006112233445566 -d
>>> 00A40400 06 112233445566
<<< 9003
As you see above, the Status Word to SELECT APDU command in 0x9003 (= 0x9000 | 0b00000011)
and that means the STK triggered EVENT_MENU_SELECTION
and EVENT_TIMER_EXPIRATION
only.
Why other event did not triggered?
1st Update 2022-10-13: Simulate ME!
I just put the SIM in a card reader and simulate the role of ME using TERMNINAL PROFILE
, FETCH
and TERMINAL REPONSE
APDU commands to investigate my SIM reaction:
>>> A0100000 08 FFFFFFFFFFFFFFFF // Simulated Terminal Profile APDU command
<<< 9129 // Card have 0x29 bytes to send
>>> A0120000 29 // Fetch 0x29 bytes
<<< D027810301250082028182850C53544B2053657276696365738F0B80436C69636B2048657265180124 9000
/*
D0 27
81 03 012500 <--- 0x25 = Set Up Menu
82 02 8182
85 0C 53544B205365727669636573
8F 0B 80436C69636B2048657265
18 01 24
*/
>>> A0140000 0C 810301030002028281030100 // Simulated Terminal Response = Command Performed Successfully.
<<< 910F // Card have 0x0F more bytes to send
>>> A0120000 0F // Fetch 0x0F bytes
<<< D00D8103010500820281829902090A 9000
/*
D0 0D
81 03 010500 <--- 0x05 = Set Up Event List
82 02 8182
99 02 090A <-- Event List: Data Available, Channel Status
*/
>>> A0140000 0C 810301030002028281030100 // Simulated Terminal Response = Command Performed Successfully.
<<< 910F // Card have 0x0F more bytes to send
>>> A0120000 0F // Fetch 0x0F bytes
<<< D00D8103010300820281828402010F 9000
/*
D0 0D
81 03 010300 <--- 0x03 = Poll Interval
82 02 8182
84 02 010F <--- Duration: 15 seconds
*/
>>> A0140000 10 8103010300020282810301008402010F // Simulated Terminal Response = Command Performed Successfully.
<<< 9000 // No more proactive commands
And try to read the receivedEvents
again:
ebr@him:~/SomePath$ apdu_sender -a 00a4040006112233445566 -d
>>> 00A40400 06 112233445566
<<< 9000 <-- Corrected in update! No event received by the applet!
2nd Update 2022-10-14: Type Fixed
I have changed Status Word bytes of the last command above from 9003 to 9000. It was a typo. The applet doesn't triggered by any event in the simulated ME test!
(Transcript of discussion in comments)
Move the getter for the EnvelopeHandler
to the EVENT_MENU_SELECTION
as it might throw for events without the ENVELOPE
APDU command:
public void processToolkit(byte event) throws ToolkitException {
ProactiveHandler proHdlr = ProactiveHandler.getTheHandler();
switch(event){
case EVENT_MENU_SELECTION: {
EnvelopeHandler envHdlr = EnvelopeHandler.getTheHandler();
receivedEvents = (byte)(receivedEvents | 0x01); // 0b00000001
byte selectedItemId = envHdlr.getItemIdentifier();
if (selectedItemId == menuItemId) {
proHdlr.init((byte) PRO_CMD_TIMER_MANAGEMENT, (byte) 0, DEV_ID_ME);
proHdlr.appendTLV((byte) (TAG_TIMER_IDENTIFIER | TAG_SET_CR), timerId);
proHdlr.appendTLV((byte) (TAG_TIMER_VALUE | TAG_SET_CR), TIMER_VALUE, (short) 0,
(short) TIMER_VALUE.length);
proHdlr.send();
}
}
break;
...the rest of the code...
Good luck with your project!