FileDocCategorySizeDatePackage
Protocol.javaAPI DocphoneME MR2 API (J2ME)34909Wed May 02 18:00:32 BST 2007com.sun.midp.io.j2me.sms

Protocol

public class Protocol extends com.sun.midp.io.j2me.ProtocolBase
SMS message connection implementation. Protocol itself is not instantiated. Instead, the application calls Connector.open with an SMS URL string and obtains a {@link javax.wireless.messaging.MessageConnection MessageConnection} object. It is an instance of MessageConnection that is instantiated. The Generic Connection Framework mechanism in CLDC will return a Protocol object, which is the implementation of MessageConnection. The Protocol object represents a connection to a low-level transport mechanism.

Optional packages, such as Protocol, cannot reside in small devices. The Generic Connection Framework allows an application to reach the optional packages and classes indirectly. For example, an application can be written with a string that is used to open a connection. Inside the implementation of Connector, the string is mapped to a particular implementation: Protocol, in this case. This allows the implementation to be optional even though the interface, MessageConnection, is required.

Closing the connection frees an instance of MessageConnection.

The Protocol class contains methods to open and close the connection to the low-level transport mechanism. The messages passed on the transport mechanism are defined by the {@link MessageObject MessageObject} class. Connections can be made in either client mode or server mode.

  • Client mode connections are for sending messages only. They are created by passing a string identifying a destination address to the Connector.open() method.
  • Server mode connections are for receiving and sending messages. They are created by passing a string that identifies a port, or equivalent, on the local host to the Connector.open() method.
The class also contains methods to send, receive, and construct Message objects.

This class declares that it implements StreamConnection so it can intercept calls to Connector.open*Stream() to throw an IllegalArgumentException.

Fields Summary
private final int
connectionMode
Connection mode.
protected static Vector
openconnections
Currently opened connections.
protected String
url
Name of current connection.
private int
m_iport
Local handle for port number.
protected static int
open_count
Count of simultaneous opened conenctions.
int[]
restrictedPorts
Ports barred from use int the specification.
protected static final int
GSM_TEXT
DCS: GSM Alphabet
protected static final int
GSM_BINARY
DCS: Binary
protected static final int
GSM_UCS2
DCS: Unicode UCS-2
Constructors Summary
public Protocol()
Creates a message connection protocol handler.


           
      
        super();
        ADDRESS_PREFIX = "sms://";
    
Methods Summary
protected voidcheckReceivePermission()
Checks internal setting of receive permission. Called from receive and setMessageListener methods.

exception
InterruptedIOException if permission dialog was preempted

	/* Check if we have permission to recieve. */
	if (!readPermission) {
	    try {
		midletSuite.checkForPermission(Permissions.SMS_RECEIVE,
						"sms:receive");
		readPermission = true;
	    } catch (InterruptedException ie) {
		throw new InterruptedIOException("Interrupted while trying " +
						 "to ask the user permission");
	    }
	}
    
public voidclose()
Closes the connection. Resets the connection open flag to false. Subsequent operations on a closed connection should throw an appropriate exception.

exception
IOException if an I/O error occurs

        /*
         * Set m_iport to 0, in order to quit out of the while loop
         * in the receiver thread.
         */
	int save_iport = m_iport;

	m_iport = 0;

        synchronized (closeLock) {
            if (open) {
                /*
                 * Reset open flag early to prevent receive0 executed by
                 * concurrent thread to operate on partially closed
                 * connection
                 */
                open = false;

                close0(save_iport, connHandle, 1);

                setMessageListener(null);

                /*
                 * Reset handle and other params to default
                 * values. Multiple calls to close() are allowed
                 * by the spec and the resetting would prevent any
                 * strange behaviour.
                 */
                connHandle = 0;
                host = null;
                m_mode = 0;

                /*
                 * Remove this connection from the list of open
                 * connections.
                 */
                int len = openconnections.size();
                for (int i = 0; i < len; i++) {
                    if (openconnections.elementAt(i) == this) {
                        openconnections.removeElementAt(i);
                        break;
                    }
                }

                open_count--;
            }
        }
    
private native intclose0(int port, int handle, int deRegister)
Native function to close sms connection

param
port port number to close
param
handle sms handle returned by open0
param
deRegister Deregistration appID when parameter is 1.
return
0 on success, -1 on failure

protected intclose00(int connHandle, int deRegister)
Close connection.

param
connHandle handle returned by open0
param
deRegister Deregistration appID when parameter is 1.
return
0 on success, -1 on failure

        return close0(m_iport, connHandle, deRegister);
    
private native voidfinalize()
Native finalizer

protected java.lang.StringgetAppID()
Gets the connection parameter in string mode.

return
string that contains a parameter

        if (m_iport > 0) {
            return new String(Integer.toString(m_iport));
        } else {
            return null;
        }
    
public javax.wireless.messaging.MessagenewMessage(java.lang.String type)
Constructs a new message object of a text or binary type. If the TEXT_MESSAGE constant is passed in, the TextMessage interface is implemented by the created object. If the BINARY_MESSAGE constant is passed in, the BinaryMessage interface is implemented by the created object.

If this method is called in a sending mode, a new Message object is requested from the connection. For example:

Message msg = conn.newMessage(TEXT_MESSAGE);

The Message object that was created doesn't have the destination address set. It's the application's responsibility to set it before the message is sent.

If this method is called in receiving mode, the Message object does have its address set. The application can act on the object to extract the address and message data.

param
type TEXT_MESSAGE or BINARY_MESSAGE.
return
a new message.

	String address = null;

	/*
         * Provide the default address from the original open.
         */

	if (((m_mode & Connector.WRITE) > 0) && (host != null)) {
	    address = ADDRESS_PREFIX + host;
	    if (m_iport != 0) {
		address = address + ":" + String.valueOf(m_iport);
	    }
	}

	return newMessage(type, address);
    
public javax.wireless.messaging.MessagenewMessage(java.lang.String type, java.lang.String addr)
Constructs a new message object of a text or binary type and specifies a destination address. If the TEXT_MESSAGE constant is passed in, the TextMessage interface is implemented by the created object. If the BINARY_MESSAGE constant is passed in, the BinaryMessage interface is implemented by the created object.

The destination address addr has the following format:

sms://phone_number:port

param
type TEXT_MESSAGE or BINARY_MESSAGE.
param
addr the destination address of the message.
return
a new Message object.

	Message message = null;

	if (type.equals(MessageConnection.TEXT_MESSAGE)) {

	    message = new TextObject(addr);
	} else {
	    if (type.equals(MessageConnection.BINARY_MESSAGE)) {

                message = new BinaryObject(addr);
	    } else {
                throw new IllegalArgumentException(
                                    "Message type not supported");
	    }
	}


	return message;
    
public intnumberOfSegments(javax.wireless.messaging.Message msg)
Returns how many segments in the underlying protocol would be needed for sending the Message given as the parameter.

Note that this method does not actually send the message; it will only calculate the number of protocol segments needed for sending it.

This method calculates the number of segments required when this message is split into the protocol segments utilizing the underlying protocol's features. Possible implementation's limitations that may limit the number of segments that can be sent using it are not taken into account. These limitations are protocol specific. They are documented with that protocol's adapter definition.

param
msg the message to be used for the calculation
return
number of protocol segments required to send the message. If the Message object can't be sent using the underlying protocol, 0 is returned.


        /** The number of segments required to send the message. */
        int segments = 0;

        /* Generate the proper buffer contents and message type. */
        byte[] msgBuffer = null;
        int messageType = GSM_TEXT;
        if (msg instanceof TextObject)	 {
            msgBuffer = ((TextObject)msg).getBytes();
            if (msgBuffer != null) {
                /*
                 * Attempt to encode the UCS2 bytes as GSM 7-bit.
                 */
                byte[] gsm7bytes = TextEncoder.encode(msgBuffer);
                if (gsm7bytes != null) {
                    msgBuffer = gsm7bytes;
                } else {
                    /*
                     * Encoding attempt failed. Use UCS2 bytes.
                     */
		    messageType = GSM_UCS2;
                }
            }
        } else if (msg instanceof BinaryObject) {
            msgBuffer = ((BinaryObject)msg).getPayloadData();
            messageType = GSM_BINARY;
        } else {
            throw new IllegalArgumentException("Message type not supported.");
        }

        // Pick up the message length.
        if (msgBuffer != null) {

            // Parse address to see if there's a port value.
            boolean hasPort = false;
            String addr = msg.getAddress();
            if (addr != null) {
                // workaround. HttpUrl can throw IAE on zero port.
                try {
                    HttpUrl url = new HttpUrl(addr);
                    if (url.port != -1) {
                        /* No port supplied. */
                        hasPort = true;
                    }
                } catch (IllegalArgumentException iae) {
                    hasPort = false;
                }	

                if (addr.startsWith("cbs:")) {
                    // Can't send a CBS message.
		    return 0;
                }
            }
	    // Other protocols can receive the message.
	    segments = numberOfSegments0(msgBuffer, msgBuffer.length,
					 messageType, hasPort);
        }

	return segments;
    
private native intnumberOfSegments0(byte[] msgBuffer, int msgLen, int msgType, boolean hasPort)
Computes the number of transport-layer segments that would be required to send the given message.

param
msgBuffer The message to be sent.
param
msgLen The length of the message.
param
msgType The message type: binary or text.
param
hasPort Indicates if the message includes a source or destination port number.
return
The number of transport-layer segments required to send the message.

private native intopen0(java.lang.String host, int msid, int port)
Native function to open sms connection

param
host The name of the host for this connection. Can be null.
param
msid Midlet suite ID, Cannot be null.
param
port port to open
return
returns handle to SMS connection.

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

return
An input stream
exception
IOException If an I/O error occurs
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
IOException if an I/O error occurs
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
IOException If an I/O error occurs
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
IOException If an I/O error occurs
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)
Opens a connection. This method is called from the Connector.open() method to obtain the destination address given in the name parameter.

The format for the name string for this method is:

sms://[phone_number:][port_number]

where the phone_number: is optional. If the phone_number parameter is present, the connection is being opened in client mode. This means that messages can be sent. If the parameter is absent, the connection is being opened in server mode. This means that messages can be sent and received.

The connection that is opened is to a low-level transport mechanism which can be any of the following:

  • a datagram Short Message Peer-to-Peer (SMPP) to a service center
  • a comm connection to a phone device with AT-commands
  • a native SMS stack
Currently, the mode and timeouts parameters are ignored.

param
name the target of the connection
param
mode indicates whether the caller intends to write to the connection. Currently, this parameter is ignored.
param
timeouts indicates whether the caller wants timeout exceptions. Currently, this parameter is ignored.
return
this connection
exception
IOException if the connection is closed or unavailable


	return openPrimInternal(name, mode, timeouts);
    
public synchronized javax.microedition.io.ConnectionopenPrimInternal(java.lang.String name, int mode, boolean timeouts)
Opens a connection. This is the internal entry point that allows the CBS protocol handler to use the reserved port for CBS emulated messages.

param
name the target of the connection
param
mode indicates whether the caller intends to write to the connection. Currently, this parameter is ignored.
param
timeouts indicates whether the caller wants timeout exceptions. Currently, this parameter is ignored.
return
this connection
exception
IOException if the connection is closed or unavailable


	/*
	 * If <code>host == null</code>, then we are a server endpoint at
	 * the supplied <code>port</code>.
	 *
	 * If <code>host != null</code> we are a client endpoint at a port
	 * decided by the system and the default address for
	 * SMS messages to be sent is <code>sms://host:port</code>.
	 */

	String portName = null;

	if ((name == null) || (name.length() <= 2) ||
            (name.charAt(0) != '/") ||
            (name.charAt(1) != '/")) {
	    throw new IllegalArgumentException("Missing protocol separator");
	}

	int colon = name.indexOf(':");
	if (colon > 0) {
	    if (colon != 2) {
		host = name.substring(2, colon);
	    }
	    portName = name.substring(colon + 1);
	} else {
	    if (name.length() > 2) {
		host = name.substring(2);
	    }
	}

	if (host != null) {
	    int offset = 0;
	    int len = host.length();
	    char c = '\0";
	    /* Only '+' followed by 0-9 are allowed in the host field. */
	    if (len > 0) {
		c = host.charAt(0);
		if (c == '+") {
		    offset = 1;
		}
		for (int i = offset; i < host.length(); i++) {
		    c = host.charAt(i);
		    if ('0" <= c && c <= '9") {

			continue;
		    } else {
			throw new IllegalArgumentException("Host format");
		    }
		}
	    }
	}

        int portNumber = 0;
        m_iport = 0;
	if (portName != null) {
	    int len = portName.length();
	    if (len == 0) {
		throw new IllegalArgumentException("Port length");
	    }
	    /*
	     * Add a numeric check hat the port is less than the
	     * GSM maximum port number.
	     */
	    try {
		portNumber = Integer.parseInt(portName);
	        m_iport = portNumber;
		if ((portNumber > 65535) || (portNumber < 0)) {
		    throw new IllegalArgumentException("Port range");
		}
	    } catch (NumberFormatException nfe) {
                throw new IllegalArgumentException("Port Number" +
                                              " formatted badly.");
	    }

	}

	if (mode == Connector.READ && host != null && host.length() > 0) {
	    throw new IllegalArgumentException("Cannot read on " +
					       "client connection");
	}

	if ((mode == Connector.WRITE) && (host == null)) {
        /*
         * avoid throwing the following exception for compliance
	 * throw new IllegalArgumentException("Missing host name");
         */
	}

	if ((mode != Connector.READ) &&
	    (mode != Connector.WRITE) &&
	    (mode != Connector.READ_WRITE)) {

	    throw new IllegalArgumentException("Invalid mode");
	}

	/*
	 * Check to see if the application has the permision to
	 * use this connection type.
	 */
	if (!openPermission) {
	    try {
		midletSuite.checkForPermission(Permissions.SMS_SERVER,
						"sms:open");
		openPermission = true;
	    } catch (InterruptedException ie) {
		throw new InterruptedIOException("Interrupted while trying " +
						 "to ask the user permission");
	    }
	}
	/*
	 * Check to see if this connection is already opened.
	 */
	int len = openconnections.size();
	for (int i = 0; i < len; i++) {
	    if (!(openconnections.elementAt(i)
		  instanceof com.sun.midp.io.j2me.sms.Protocol)
		|| ((Protocol) openconnections.elementAt(i)).url.equals(name)) {
		throw new IOException("Already opened");
	    }
	}

	openconnections.addElement(this);
	url = name;

	try {
            connHandle = open0(host, midletSuite.getID(), m_iport);
	} catch (IOException ioexcep) {
	    m_mode = 0;
	    throw new IOException("SMS connection cannot be opened");
	} catch (OutOfMemoryError oomexcep) {
	    m_mode = 0;
	    throw new IOException("SMS connection cannot be opened");
        }

	open_count++;
	m_mode = mode;
	open = true;

	return this;
    
public synchronized javax.wireless.messaging.Messagereceive()
Receives the bytes that have been sent over the connection, constructs a Message object, and returns it.

If there are no Messages waiting on the connection, this method will block until a message is received, or the MessageConnection is closed.

return
a Message object.
exception
java.io.IOException if an error occurs while receiving a message.
exception
java.io.InterruptedIOException if this MessageConnection object is closed during this receive method call.
exception
java.lang.SecurityException if the application does not have permission to receive messages using the given port number.


	/* Check if we have permission to recieve. */
	checkReceivePermission();

	/* Make sure the connection is still open. */
	ensureOpen();

	if (((m_mode & Connector.READ) == 0) || (host != null)) {

	    throw new IOException("Invalid connection mode");
	}

	Message message = null;
	int length = 0;
	try {

	    SMSPacket smsPacket = new SMSPacket();

            /*
             * Packet has been received and deleted from inbox.
             * Time to wake up receive thread.
             */
            // Pick up the SMS message from the message pool.
            length = receive0(m_iport, midletSuite.getID(),
                              connHandle, smsPacket);

	    if (length >= 0) {
	  	String type;
	  	boolean isTextMessage = true;
	  	if (smsPacket.messageType == GSM_BINARY)  {
				type = MessageConnection.BINARY_MESSAGE;
				isTextMessage = false;
	        } else {
				type = MessageConnection.TEXT_MESSAGE;
				isTextMessage = true;
	        }
	        message = newMessage(type,
	        	new String(ADDRESS_PREFIX
                                   + new String(smsPacket.address)
                                   + ":" + smsPacket.port));
	        String messg = null;
	        if (isTextMessage) {
	            if (length > 0) {
	        	if (smsPacket.messageType == GSM_TEXT) {
	                    messg = new String(TextEncoder.toString(
                                      TextEncoder.decode(smsPacket.message)));
	        	} else {
	                    messg = new String(TextEncoder.toString(
                                      smsPacket.message));

	        	}
	            } else {
	                messg = new String("");
                    }
	            ((TextObject)message).setPayloadText(messg);
	        } else {
                    if (length > 0) {
	                ((BinaryObject)message).setPayloadData(
                                                       smsPacket.message);
                    } else {
	                ((BinaryObject)message).setPayloadData(new byte[0]);
                    }
	        }
	        ((MessageObject)message).setTimeStamp(smsPacket.sentAt);
            }
	} catch (InterruptedIOException ex) {
            length = 0;
            throw new InterruptedIOException("MessageConnection is closed");
        } catch (IOException ex) {
            io2InterruptedIOExc(ex, "receiving");
	} finally {
	    if (length < 0) {
		throw new InterruptedIOException("Connection closed error");
	    }
	}


	return message;
    
private native intreceive0(int port, int msid, int handle, com.sun.midp.io.j2me.sms.Protocol$SMSPacket smsPacket)
Receives SMS message

param
port incoming port
param
msid Midlet suite ID
param
handle handle to open SMS connection
param
smsPacket received packet
return
number of bytes received
exception
IOException if an I/O error occurs

public voidsend(javax.wireless.messaging.Message dmsg)
Sends a message over the connection. This method extracts the data payload from the Message object so that it can be sent as a datagram.

param
dmsg a Message object
exception
java.io.IOException if the message could not be sent or because of network failure
exception
java.lang.IllegalArgumentException if the message is incomplete or contains invalid information. This exception is also thrown if the payload of the message exceeds the maximum length for the given messaging protocol.
exception
java.io.InterruptedIOException if a timeout occurs while either trying to send the message or if this Connection object is closed during this send operation.
exception
java.lang.NullPointerException if the parameter is null.
exception
java.lang.SecurityException if the application does not have permission to send the message.

	String phoneNumber = null;
	String address = null;

	if (dmsg == null) {
	    throw new NullPointerException();
	}

        if (dmsg.getAddress() == null) {
            throw new IllegalArgumentException();
	}

        /*
         * parse name into host and port
         */
	String addr = dmsg.getAddress();
        HttpUrl url = new HttpUrl(addr);
        if (url.port == -1) {
            /* no port supplied */
            url.port = 0;
        }

	/* Can not send to cbs address. */
	if (addr.startsWith("cbs:")) {
	    // Can't send a CBS message.
	    throw new IllegalArgumentException("Can't send CBS msg.");
	}

        int numSeg = numberOfSegments(dmsg);
	if ((numSeg <= 0) || (numSeg > 3)) {
            throw new IOException("Error: message is too large");
	}

        try {
            midletSuite.checkForPermission(Permissions.SMS_SEND,
                                           url.host,
                                           Integer.toString(numSeg));
        } catch (InterruptedException ie) {
            throw new InterruptedIOException("Interrupted while trying " +
                             "to ask the user permission");
        }

	ensureOpen();

	if ((m_mode & Connector.WRITE) == 0) {

	    throw new IOException("Invalid mode");
	}

        int sourcePort = 0;
        if ((m_mode & Connector.READ) != 0 && host == null) {
            sourcePort = m_iport;
        }

        for (int restrictedPortIndex = 0;
             restrictedPortIndex < restrictedPorts.length;
             restrictedPortIndex++) {
            if (url.port == restrictedPorts[restrictedPortIndex]) {
                throw new SecurityException(
                  "not allowed to send SMS messages to the restricted ports");
            }
        }

	int messageType = GSM_BINARY;
	byte[] msgBuffer = null;

        if (dmsg instanceof TextObject)	 {
            byte[] gsm7bytes;
            msgBuffer = ((TextObject)dmsg).getBytes();
	    if (msgBuffer != null) {
                /*
                 * Attempt to encode the UCS2 bytes as GSM 7-bit.
                 */
                gsm7bytes = TextEncoder.encode(msgBuffer);
                if (gsm7bytes != null) {
		    msgBuffer = gsm7bytes;
		    messageType = GSM_TEXT;
                } else {
                    /*
                     * Encoding attempt failed. Send UCS2 bytes.
                     */
		    messageType = GSM_UCS2;
                }
	    }

        } else if (dmsg instanceof BinaryObject) {
            msgBuffer = ((BinaryObject)dmsg).getPayloadData();
	} else {
            throw new IllegalArgumentException("Message type not supported");
	}

        try {
            send0(connHandle, messageType,
                           url.host,
                           url.port,
			   sourcePort,
                           msgBuffer);
        } catch (IOException ex) {
            io2InterruptedIOExc(ex, "sending");
        }
    
private native intsend0(int handle, int type, java.lang.String host, int destPort, int sourcePort, byte[] message)
Sends SMS message

param
handle handle to SMS connection.
param
type message type, binary or text.
param
host URL of host sending message
param
destPort destination port
param
sourcePort source port
param
message message buffer
return
number of bytes sent
exception
IOException if an I/O error occurs

protected voidsetAppID(java.lang.String newValue)
Sets the connection parameter in string mode.

param
newValue new value of connection parameter

        try {
            m_iport = Integer.parseInt(newValue);
        } catch (NumberFormatException exc) {
            m_iport = 0;
        }
    
protected intunblock00(int msid)
Unblock the receive thread.

param
msid The MIDlet suite ID.
return
returns handle to the connection.

        return open0(null, msid, 0);
    
private native intwaitUntilMessageAvailable0(int port, int handle)
Waits until message available

param
port incoming port
param
handle handle to SMS connection
return
0 on success, -1 on failure
exception
IOException if an I/O error occurs

protected intwaitUntilMessageAvailable00(int handle)
Waits until message available

param
handle handle to connection
return
0 on success, -1 on failure
exception
IOException if an I/O error occurs

        return waitUntilMessageAvailable0(m_iport, handle);