FileDocCategorySizeDatePackage
Protocol.javaAPI DocphoneME MR2 API (J2ME)36507Wed May 02 18:00:38 BST 2007com.sun.midp.io.j2me.jcrmi

Protocol

public class Protocol extends Object implements com.sun.cldc.io.ConnectionBaseInterface, javax.microedition.jcrmi.JavaCardRMIConnection, javax.microedition.io.StreamConnection
JCRMI connection to card application.

Fields Summary
private static com.sun.midp.security.SecurityToken
classSecurityToken
This class has a different security domain than the MIDlet suite
private static final int
APDUBufferSize
Size of APDU buffer.
private Remote
initialReference
Stub object for initial remote reference.
private Reference
internalReference
Reference object for initial remote reference.
private byte[]
APDUBuffer
Remote reference uses this buffer to prepare INVOKE APDU command.
private int
offset
Current offset in APDUBuffer buffer for write methods.
private byte[]
response
Response APDU data.
private int
r_offset
Current offset in response buffer for read methods.
private com.sun.midp.crypto.MessageDigest
SHA
SHA-1 message digest object used by this connection.
private com.sun.midp.io.j2me.apdu.Handle
h
The current APDU connection handle.
private boolean
connectionOpen
A flag to indicate if connection is open or not.
private com.sun.satsa.acl.JCRMIPermissions
verifier
This object verifies access rights of MIDlet.
private static final byte
NormalTag
Constant for JCRMI protocol.
private static final byte
ExactExceptionTag
Constant for JCRMI protocol.
private static final byte
ExceptionSubclassTag
Constant for JCRMI protocol.
private static final byte
ErrorTag
Constant for JCRMI protocol.
Constructors Summary
Methods Summary
public shortchangePin(int pinID)
A call to changePin method pops up a UI that requests the user for an old or existing PIN value and the new PIN value to to change the value of the PIN. The pinID field indicates which PIN is to be changed. The user can either cancel the request or continue. If the user enters the PIN values and chooses to continue the implementation is responsible for presenting the the old and new values of the PIN to the card.

param
pinID the type of PIN the implementation is suppose to prompt the user to change.
return
PINENTRY_CANCELLED if the user cancelled the PIN entry request or the value returned by the remote method.
exception
java.rmi.RemoteException is thrown if the PIN could not be communicated to the card or an exception is thrown by the card in response to the PIN entry.
exception
SecurityException is thrown if the J2ME application does not have appropriate rights to ask for changing the PIN value.

        return doEnterPin(pinID, 0, ACLPermissions.CMD_CHANGE);
    
public voidclose()
Closes the connection.

throws
IOException If an I/O error occurs

        synchronized (APDUBuffer) {
            if (connectionOpen) {
                connectionOpen = false;
                APDUManager.closeConnection(h);
            }
        }
    
private java.rmi.RemotecreateStub()
Creates stub using remote reference descriptor in response buffer at r_offset offset.

return
new stub object
throws
java.rmi.RemoteException if an error occurs


        short objectID = getShort();

        if (objectID == (short) 0xffff) {
            return null;                    // null reference returned
        }

        // hash modifier
        String hashModifier = getString();

        // list of interfaces

        int count = getByte();

        Class stubClass = null;
        String className = null;
        String packageName = null;
        Class[] classes = new Class[count];

        for (int i = 0; i < count; i++) {

            String name = getString();
            if (name != null) {
                packageName = name.replace('/", '.");
            }

            name = packageName + "." + getString();

            try {
                classes[i] = Class.forName(name);
            } catch (ClassNotFoundException e) {
                throw new RemoteException("Class not found", e);
            }

            if (stubClass == null ||
                    stubClass.isAssignableFrom(classes[i])) {
                stubClass = classes[i];
                className = name;
                continue;
            }
        }

        for (int i = 0; i < count; i++) {
            if (! classes[i].isAssignableFrom(stubClass)) {
                throw new RemoteException(
                        "Incorrect hierarchy in descriptor");
            }
        }

        RemoteStub stub;
        try {
            stub = (RemoteStub)
                    (Class.forName(className + "_Stub").newInstance());
        } catch (ClassNotFoundException cnfe) {
            throw new RemoteException("Can't find stub class", cnfe);
        } catch (IllegalAccessException iae) {
            throw new RemoteException("Access to stub class denied", iae);
        } catch (InstantiationException ie) {
            throw new RemoteException("Can't create stub object", ie);
        }
        Reference r = new Reference(this, objectID, hashModifier, className);
        if (internalReference == null) {
            internalReference = r;
        }
        stub.setRef(r);

        return (Remote) stub;
    
public shortdisablePin(int pinID)
A call to disablePin method pops up a UI that requests the user to enter the value for the PIN that is to be disabled. The pinID field indicates which PIN is to be disabled. The user can either cancel the request or continue. If the user enters the PIN and chooses to continue the implementation is responsible for presenting the PIN value to the card to disable PIN.

param
pinID the type of PIN the implementation is required to prompt the user to enter.
return
PINENTRY_CANCELLED if the user cancelled the PIN entry request or the value returned by the remote method.
exception
java.rmi.RemoteException is thrown if the PIN could not be communicated to the card or an exception is thrown by the card in response to the PIN entry.
exception
SecurityException is thrown if the J2ME application does not have appropriate rights to ask for disabling the PIN.

        return doEnterPin(pinID, 0, ACLPermissions.CMD_DISABLE);
    
private shortdoEnterPin(int pinID, int uPinID, int action)
Performs PIN entry operation.

param
pinID PIN identifier.
param
uPinID unblocking PIN identifier.
param
action PIN operation identifier.
return
PINENTRY_CANCELLED if the user cancelled the PIN entry request or the value returned by the remote method.
exception
java.rmi.RemoteException is thrown if the PIN could not be communicated to the card or an exception is thrown by the card in response to the PIN entry.
exception
SecurityException is thrown if the J2ME application does not have appropriate rights to ask for unblocking the PIN.


        if (! connectionOpen) {
            throw new RemoteException("Connection is closed.");
        }

        String method = null;
        method = verifier.preparePIN(pinID, uPinID, action,
                                    internalReference.getClassName());
        Object[] pins = verifier.enterPIN(classSecurityToken, action);

        if (pins == null) {
            return PINENTRY_CANCELLED;
        }

        try {
            Short r = (Short) invoke(internalReference, method, pins);
            return r.shortValue();
        } catch (Exception e) {
            throw new RemoteException("" + e);
        }
    
public shortenablePin(int pinID)
A call to enablePin method pops up a UI that requests the user to enter the value for the PIN that is to be enabled. The pinID field indicates which PIN is to be enabled. The user can either cancel the request or continue. If the user enters the PIN and chooses to continue the implementation is responsible for presenting the PIN value to the card for enabling the PIN.

param
pinID the type of PIN the implementation is required to prompt the user to enter.
return
PINENTRY_CANCELLED if the user cancelled the PIN entry request or the value returned by the remote method.
exception
java.rmi.RemoteException is thrown if the PIN could not be communicated to the card or an exception is thrown by the card in response to the PIN entry.
exception
SecurityException is thrown if the J2ME application does not have appropriate rights to ask for enabling the PIN.

        return doEnterPin(pinID, 0, ACLPermissions.CMD_ENABLE);
    
public shortenterPin(int pinID)
A call to enterPin method pops up a UI that requests the PIN from the user. The pinID field indicates which PIN must be requested from the user. The user can either cancel the request or continue. If the user enters the PIN and chooses to continue, The implementation is responsible for presenting the PIN entered by the user to the card for verification.

param
pinID the type of PIN the implementation is suppose to prompt the user to enter.
return
PINENTRY_CANCELLED if the user cancelled the PIN entry request or the value returned by the remote method.
exception
java.rmi.RemoteException is thrown if the PIN could not be communicated to the card or an exception is thrown by the card in response to the PIN entry.
exception
SecurityException is thrown if the J2ME application does not have appropriate rights to ask for PIN verification.

        return doEnterPin(pinID, 0, ACLPermissions.CMD_VERIFY);
    
private intgetByte()
Reads one byte from response APDU, zero-extends it to type int, and returns the result.

return
the unsigned 8-bit value

    	return response[r_offset++] & 0xff;
    
intgetCardSessionId()
Returns the card session identifier for this connection.

return
the card session identifier

        return h.cardSessionId;
    
public java.rmi.RemotegetInitialReference()
Returns the stub object for an initial remote reference.

return
the initial remote reference

        return initialReference;
    
private intgetInt()
Reads int value from response APDU.

return
int value

        return (getByte() << 24) |
               (getByte() << 16) |
               (getByte() << 8) |
                getByte();
    
private shortgetShort()
Reads short value from response APDU

return
short value

    	return (short) (getByte() << 8 | getByte());
    
private java.lang.StringgetString()
Reads String value from response APDU.

return
String value


        int len = getByte();

        if (len == 0) {
            return null;
        }

        String S;
        try {
            S = new String(response, r_offset, len, Utils.utf8);
        } catch (UnsupportedEncodingException e) {
            throw new RemoteException("UTF-8 encoding is not supported");
        }
        r_offset += len;
        return S;
    
java.lang.Objectinvoke(Reference ref, java.lang.String method, java.lang.Object[] params)
Invokes a remote method. The remote method invoked on the card can throw an exception to signal that an unexpected condition has been detected.

If the exception thrown on the card is an exception defined in the Java Card 2.2 API, then the same exception is thrown to the stub method. The client can access the reason code associated with Java Card-specific exceptions using the standard getReason() method.

If the exception thrown on the card is a subclass of an exception defined in the Java Card 2.2 API, then the closest exception defined in the API (along with the reason code, if applicable) is thrown to the stub method. The detail message string of the exception object may indicate that exception subclass was thrown on the card.

Apart from the exceptions thrown by the remote method itself, errors during communication, marshalling, protocol handling, unmarshalling, stub object instantiation, and so on, related to the JCRMI method invocation, results in a RemoteException being thrown to the stub method.

param
ref handle for remote object
param
method simple (not fully qualified) name of the method followed by the method descriptor. Representation of a method descriptor is the same as that described in The Java Virtual Machine Specification (§ 4.3.3)
param
params the parameter list
return
result of remote method invocation
exception
java.lang.Exception if any exception occurs during the remote method invocation


                                                                                                                                                                                                                                                                             
          
              

        if (! connectionOpen) {
            throw new RemoteException("Connection is closed.");
        }

        verifier.checkPermission(ref.getClassName(), method);

        synchronized (APDUBuffer) {

            try {
                marshal(ref, method, params);
            } catch (RuntimeException ai) {
                throw new RemoteException("Error marshalling parameters");
            }

            try {
                response = APDUManager.exchangeAPDU(h, APDUBuffer);
            } catch (IOException e) {
                throw new RemoteException("IO error", e);
            }

            r_offset = response.length - 2;

            if (getShort() !=  (short) 0x9000) {
                throw new RemoteException("Incorrect status word");
            }

            r_offset = 0;

            byte tag = (byte) getByte();

            Exception ex = null;
            Object result = null;

            try {
                if (tag == ExactExceptionTag || tag == ExceptionSubclassTag) {
                    ex = parseException(tag);
                } else
                if (tag == ErrorTag) {
                    ex = parseError();
                } else
                if (tag == NormalTag) {
                    result = parseResult(method);
                } else {
                    throw new RemoteException("Incorrect tag value: " + tag);
                }

                getShort();     // status word

            } catch (RuntimeException e) {
                throw new RemoteException("Incorrect response structure");
            }

            if (ex != null) {
                throw ex;
            }

            return result;
        }
    
booleanisOpened()
Returns the flag that indicates if connection is open.

return
true if the connection is open

        return connectionOpen;
    
private voidmarshal(Reference ref, java.lang.String method, java.lang.Object[] params)
Prepares INVOKE APDU.

param
ref handle for remote object
param
method simple (not fully qualified) name of the method followed by the method descriptor. Representation of a method descriptor is the same as that described in The Java Virtual Machine Specification (§ 4.3.3)
param
params the parameter list
throws
RemoteException if an error occurs


        offset = 5;

        putShort(ref.getObjectID());

        String hashString = method;
        String hashModifier = ref.getHashModifier();

        if (hashModifier != null) {
            hashString = hashModifier + hashString;
        }

        byte[] buf = Utils.stringToBytes(hashString);

        SHA.reset();
        SHA.update(buf, 0, buf.length);
        try {
            SHA.digest(APDUBuffer, offset, SHA.getDigestLength());
        } catch (GeneralSecurityException e) {
            throw new RemoteException("SHA1 error");
        }

        offset += 2;

        if (params != null) {
            for (int i = 0; i < params.length; i++) {

                Object obj = params[i];

                if (obj == null) {
                    putByte(0xff);
                    continue;
                }

                if (obj instanceof Byte) {
                    putByte(((Byte)obj).byteValue());
                    continue;
                }

                if (obj instanceof Boolean) {
                    putByte(((Boolean)obj).booleanValue() ? 1 : 0);
                    continue;
                }

                if (obj instanceof Short) {
                    putShort(((Short)obj).shortValue());
                    continue;
                }

                if (obj instanceof Integer) {
                    putInt(((Integer)obj).intValue());
                    continue;
                }

                if (obj instanceof byte[]) {
                    byte[] param = (byte[]) obj;
                    putByte(param.length);
                    for (int k = 0; k < param.length; k++) {
                        putByte(param[k]);
                    }
                    continue;
                }

                if (obj instanceof boolean[]) {
                    boolean[] param = (boolean[]) obj;
                    putByte(param.length);
                    for (int k = 0; k < param.length; k++) {
                        putByte(param[k] ? 1 : 0);
                    }
                    continue;
                }

                if (obj instanceof short[]) {
                    short[] param = (short[]) obj;
                    putByte(param.length);
                    for (int k = 0; k < param.length; k++) {
                        putShort(param[k]);
                    }
                    continue;
                }

                if (obj instanceof int[]) {
                    int[] param = (int[]) obj;
                    putByte(param.length);
                    for (int k = 0; k < param.length; k++) {
                        putInt(param[k]);
                    }
                    continue;
                }

                throw new RemoteException("Incorrect parameter type");
            }
        }
        APDUBuffer[4] = (byte) (offset - 5);

        putByte(255);
    
public java.io.DataInputStreamopenDataInputStream()
Open and return a data input stream for a connection. This method always throw IllegalArgumentException.

return
An input stream
exception
IllegalArgumentException is thrown for all requests

        throw new IllegalArgumentException("Not supported");
    
public java.io.DataOutputStreamopenDataOutputStream()
Open and return a data output stream for a connection. This method always throw IllegalArgumentException.

return
An output stream
exception
IllegalArgumentException is thrown for all requests

        throw new IllegalArgumentException("Not supported");
    
public java.io.InputStreamopenInputStream()
Open and return an input stream for a connection. This method always throw IllegalArgumentException.

return
An input stream
exception
IllegalArgumentException is thrown for all requests

        throw new IllegalArgumentException("Not supported");
    
public java.io.OutputStreamopenOutputStream()
Open and return an output stream for a connection. This method always throw IllegalArgumentException.

return
An output stream
exception
IllegalArgumentException is thrown for all requests

        throw new IllegalArgumentException("Not supported");
    
public javax.microedition.io.ConnectionopenPrim(java.lang.String name, int mode, boolean timeouts)
Connector uses this method to initialize the connection object. This method establishes APDU connection with card application, obtains FCI information and creates stub for initial remote reference.

param
name the URL for the connection without protocol name
param
mode the access mode (Ignored)
param
timeouts a flag to indicate that the caller wants timeout exceptions. Ignored
return
this connection
throws
IOException if the connection can not be initialized
throws
RemoteException if initial remote reference object can not be created
throws
SecurityException if access is restricted by ACL


                                                                                                                 
           
              

        MIDletSuite midletSuite =
            MIDletStateHandler.getMidletStateHandler().getMIDletSuite();

        try {
            midletSuite.checkForPermission(Permissions.JCRMI_CONNECTION, null);
        } catch (InterruptedException ie) {
            throw new InterruptedIOException(
                "Interrupted while trying to ask the user permission");
        }

        int slotInfo;
        // parse URL string for slot number and AID
        try {
            slotInfo = parseURL(name);
        } catch (NullPointerException npe) {
            throw new IllegalArgumentException("Invalid URL");
        } catch (IndexOutOfBoundsException iobe) {
            throw new IllegalArgumentException("Invalid URL");
        } catch (IllegalArgumentException iae) {
            throw new IllegalArgumentException("Invalid URL");
        }

        APDUManager.checkSlotNumber(slotInfo);
        // get card application selected
         APDUManager.initACL(slotInfo, classSecurityToken);
         verifier = AccessControlManager
                .getJCRMIPermissions(slotInfo,
                APDUBuffer,
                ((MIDletSuiteImpl)midletSuite)
                .getInstallInfo().getCA());

         h = APDUManager.selectApplication(APDUBuffer, slotInfo,
                                              classSecurityToken);

        connectionOpen = true;

        // parse FCI
        byte[] FCI = h.getFCI();

        byte invokeINS;

        try {
            // fci_tag or application_data_tag
            if (FCI[0] != 0x6f || FCI[2] != 0x6e) {
                throw new RemoteException("Incorrect FCI format");
            }

            int index = 4;

            // skip unnecessary tag/value pairs
            while (FCI[index] != 0x5E) {     // jc_rmi_data_tag
                index += 2 + (FCI[index] & 0xff);
            }

            index += 4;

            invokeINS = FCI[index++];

            if (FCI[index++] != (byte) 0x81) {       // normal_tag
                throw new RemoteException("Incorrect FCI format");
            }

            response = FCI;
            r_offset = index;

            initialReference = createStub();

            if (initialReference == null) {
                throw new RemoteException();
            }
        } catch (RemoteException e) {
            close();
            throw e;
        } catch (Throwable e) {
            close();
            throw new RemoteException("Can't create initial reference");
        }


        offset = 0;
        putByte(0x80 | h.channel);
        putByte(invokeINS);
        putShort(0x0202);

        try {
            SHA = MessageDigest.getInstance("SHA-1");
        } catch (GeneralSecurityException e) {
            // Ignore this exception
        }

        return this;
    
private java.rmi.RemoteExceptionparseError()
Parses response containing data about error.

return
the exception to be thrown to stub
throws
RemoteException if error code is unknown


        short code = getShort();

        switch (code) {
            case 1: return new RemoteException("Object not exported");
            case 2: return new RemoteException("Method not found");
            case 3: return new RemoteException("Signature mismatch");
            case 4: return new RemoteException("Out of parameter resources");
            case 5: return new RemoteException("Out of response resources");
            case 6: return new RemoteException(
                        "Protocol error reported by the card");
            default:
                throw new RemoteException("Error reported by the card: " +
                                            code);
        }
    
private java.lang.ExceptionparseException(byte tag)
Parses response containing data about exception.

param
tag type tag of exception
return
the exception to be thrown to stub
throws
RemoteException if an error occurs during parsing



        String message = (tag == ExactExceptionTag) ?
                            "Exception is thrown on card" :
                            "Exception subclass is thrown on card";

        byte type = (byte) getByte();
        short reason = getShort();

        switch (type) {
            case 0x00:
                return new Exception(message + ": java.lang.Throwable");
            case 0x01:
                return new ArithmeticException(message);
            case 0x02:
                return new ArrayIndexOutOfBoundsException(message);
            case 0x03:
                return new ArrayStoreException(message);
            case 0x04:
                return new ClassCastException(message);
            case 0x05:
                return new Exception(message);
            case 0x06:
                return new IndexOutOfBoundsException(message);
            case 0x07:
                return new NegativeArraySizeException(message);
            case 0x08:
                return new NullPointerException(message);
            case 0x09:
                return new RuntimeException(message);
            case 0x0A:
                return new SecurityException(message);
            case 0x0B:
                return new IOException(message);
            case 0x0C:
                return new RemoteException(message);
            case 0x20:
                return new APDUException(reason);
            case 0x21:
                return new CardException(reason);
            case 0x22:
                return new CardRuntimeException(reason);
            case 0x23:
                return new ISOException(reason);
            case 0x24:
                return new PINException(reason);
            case 0x25:
                return new SystemException(reason);
            case 0x26:
                return new TransactionException(reason);
            case 0x27:
                return new UserException(reason);
            case 0x30:
                return new javacard.security.CryptoException(reason);
            case 0x40:
                return new ServiceException(reason);
            default:
                throw new RemoteException(
                        "Unknown exception is thrown on card");
        }
    
private java.lang.ObjectparseResult(java.lang.String method)
Parses the normal JCRMI response.

param
method method name and signature
return
the value returned by method
throws
RemoteException if an error occurs during parsing


        int index = method.indexOf(')") + 1;

        if (index != -1) {
            switch (method.charAt(index)) {
                case 'V": return null;
                case 'B": return new Byte((byte) getByte());
                case 'Z": return new Boolean(getByte() == 1 ? true : false);
                case 'S": return new Short(getShort());
                case 'I": return new Integer(getInt());
                case 'L": return createStub();
                case '[": {

                    int len = getByte();

                    if (len == 255)
                        return null;

                    switch (method.charAt(index + 1)) {
                        case 'B": {
                            byte[] data = new byte[len];

                            for (int i = 0; i < len; i++) {
                                data[i] = (byte) getByte();
                            }
                            return data;
                        }
                        case 'Z": {
                            boolean[] data = new boolean[len];

                            for (int i = 0; i < len; i++) {
                                data[i] = (getByte() == 1 ? true : false);
                            }
                            return data;
                        }
                        case 'S": {
                            short[] data = new short[len];

                            for (int i = 0; i < len; i++) {
                                data[i] = getShort();
                            }
                            return data;
                        }
                        case 'I": {
                            int[] data = new int[len];

                            for (int i = 0; i < len; i++) {
                                data[i] = getInt();
                            }
                            return data;
                        }
                    }
                }
            }
        }
        throw new RemoteException("Incorrect method signature");
    
private intparseURL(java.lang.String URL)
Parses the URL to get the slot number and AID. Prepares SELECT APDU in APDUBuffer.

param
URL contains the URL from which the slot information is to be extracted
return
slot number for this connection


        int slotIndex = URL.indexOf(":") + 1;
        int AIDIndex = URL.indexOf(";AID=");

        int slotInfo = (AIDIndex == slotIndex) ? 0 :
               Integer.parseInt(URL.substring(slotIndex, AIDIndex), 16);

        // prepare selection APDU

        offset = 0;
// IMPL_NOTE: if the card does not support JC2.2 you must use: putInt(0x00a40400); 
        putInt(0x00a40410);  // selection APDU header
        offset++;           // length

        // parse for AID
        int AIDLength = APDUManager.parseDottedBytes(
                URL.substring(AIDIndex + 5), APDUBuffer, offset);

        if (AIDLength < 5 || AIDLength > 16)
            throw new IllegalArgumentException();

        APDUBuffer[4] = (byte) AIDLength;
        offset += AIDLength;
        putByte(255);

        return slotInfo;
    
private voidputByte(int param)
Writes param value into APDUBuffer.

param
param the value to be written

        APDUBuffer[offset++] = (byte) param;
    
private voidputInt(int param)
Writes param value into APDUBuffer.

param
param the value to be written

        putByte(param >> 24);
        putByte(param >> 16);
        putByte(param >> 8);
        putByte(param);
    
private voidputShort(int param)
Writes param value into APDUBuffer.

param
param the value to be written

        putByte(param >> 8);
        putByte(param);
    
public shortunblockPin(int blockedPinID, int unblockingPinId)
This is a high-level method that lets the J2ME application ask the user to enter the value for an unblocking PIN, and the new value for the blocked PIN and send these to the card. A call to unblockPin method pops up a UI that requests the user to enter the value for the unblocking PIN and the new value for the blocked PIN. The unblockingPinID field indicates which unblocking PIN is to be used to unblock the blocked PIN which is indicated by the field blockedPinId. The unblockingPinID field indicates which PIN is to be unblocked. The user can either cancel the request or continue. If the user enters the PIN values and chooses to continue, the implementation is responsible for presenting the PIN values to the card for unblocking the blocked PIN. If padding is required for either of the PIN values, the implementation is responsible for providing appropriate padding.

param
blockedPinID the Id of PIN that is to be unblocked.
param
unblockingPinId the Id of unblocking PIN.
return
PINENTRY_CANCELLED if the user cancelled the PIN entry request or the value returned by the remote method.
exception
java.rmi.RemoteException is thrown if the PIN could not be communicated to the card or an exception is thrown by the card in response to the PIN entry.
exception
SecurityException is thrown if the J2ME application does not have appropriate rights to ask for unblocking the PIN.

        return doEnterPin(blockedPinID, unblockingPinId,
                ACLPermissions.CMD_UNBLOCK);