FileDocCategorySizeDatePackage
Messenger.javaAPI DocphoneME MR2 API (J2ME)8353Wed May 02 18:00:32 BST 2007com.sun.midp.jsr82emul

Messenger.java

/*
 *   
 *
 * Copyright  1990-2007 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt).
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions.
 */
package com.sun.midp.jsr82emul;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * Represents JSR 82 emulation protocol, namely messages and codes recognized 
 * by emulation client and/or server. 
 *
 * It is not a part of JSR 82 implementation and is only used within JSR 82 
 * emulation mode.  The emulation mode allows running tets without real native 
 * Bluetooth libraries or hardware.
 *
 * Emulation server and client communicate thru a socket by means of sending 
 * data packets of the following format: 
 * <table border>
 *   <tr>
 *     <td>1 byte</td>
 *     <td>1 byte</td>
 *     <td>as defined by the previous field</td>
 *   </tr><tr>
 *     <td>packet type code</td>
 *     <td>length of information in bytes</td>
 *     <td>information bytes</td>
 *   </tr>
 * </table>
 */
public final class Messenger {
    /** Keeps encoding for messages. */
    public static final String ENCODING = "ISO8859_1";
    
    /** Responce code that identifies a failure. */
    public static final byte ERROR = -1;
    /** Device registration request code. */
    public static final byte REGISTER_DEVICE = 1;
    /** Successfull registration response. */
    public static final byte REGISTERED = 2;
    
    /** Notification that client does not require server any more. */
    public static final byte DONE = 3;
    /** 
     * Code for specific messages that are only recognized by specific 
     * handlers at server side.
     */
    public static final byte SPECIFIC_MESSAGE = 4;
    
    /** Code for starting advertising service in the ether. */
    public static final byte REGISTER_SERVICE = 5;
    /** Code for unregistering service. */
    public static final byte UNREGISTER_SERVICE = 6;
    /** Request for service connection. */
    public static final byte CONNECT_TO_SERVICE = 7;
    /** Respond that provides service connection details. */
    public static final byte SERVICE_AT = 8;
    
    /** Request for inquiry start. */
    public static final byte START_INQUIRY = 9;
    /** Respond on inquiry completion. */
    public static final byte INQUIRY_COMPLETED = 10;
        
    /** 
     * Command to update device state that includes discoverable mode and
     * device class.
     */
    public static final byte UPDATE_DEVICE_STATE = 11;
    
    /** Keeps code value retrieved by last <code>receive()</code> invocation. */
    private byte code = -1;
    /** Keeps bytes retrieved by last <code>receive()</code> invocation. */
    private byte bytes[] = null;
    
    /** 
     * Constructs an instance. Always use different instances for different 
     * clients/servers to make sure data stored in <code>message</code> and
     * <code>code</code> is appropriate.
     */
    public Messenger() {
    }
    
    /** 
     * Forms a packet with given code and message and sends it to given output
     * stream.
     *
     * @param out the output stream to send to.
     * @param code the code to send.
     * @param message the message to send.
     *
     * @exception IOException if one is issued by <code>out</code> methods.
     */
    public void send(OutputStream out, byte code, String message) 
            throws IOException {
        if (out == null) {
            throw new IllegalArgumentException();
        }
        
        if (message == null) {
            message = "";
        }
        
        byte[] messageBytes = message.getBytes(ENCODING);
        
        sendBytes(out, code, messageBytes);
    }
    
    /** 
     * Forms a packet with given code and bytes that represent given integer, 
     * then sends it to given output stream.
     *
     * @param out the output stream to send to
     * @param code the code to send
     * @param value integer to send
     *
     * @exception IOException if one is issued by <code>out</code> methods.
     */
    public void sendInt(OutputStream out, byte code, int value)
            throws IOException {
            
        byte[] intBytes = new byte[4];
        for (int i = 0; i < 4; i++) {
            intBytes[i] = (byte) (value & 0xff);
            value >>= 8;
        }
        
        sendBytes(out, code, intBytes);
    }

    /** 
     * Forms a packet with given code and bytes to send and sends it to 
     * given output stream.
     *
     * @param out the output stream to send to
     * @param code the code to send
     * @param info byte array to be sent entirely
     *
     * @exception IOException if one is issued by <code>out</code> methods.
     */
    public void sendBytes(OutputStream out, byte code, byte[] info)
            throws IOException {
        
        if (info.length > Byte.MAX_VALUE) {
            throw new IllegalArgumentException();
        }
        
        synchronized (out) {
            out.write(code);
            out.write((byte)info.length);
            out.write(info);
            out.flush();
        }
    }

    /** 
     * Receives a packet from input stream given saving retrieved data in
     * <code>code</code> and <code>message</code>.
     *
     * @param in the input stream to read from.
     * @exception IOException if one is issued by <code>in</code> methods.
     */
    public void receive(InputStream in) throws IOException {
        code = -1;
        bytes = null;
        
        if (in == null) {
            throw new IllegalArgumentException();
        }
        
        synchronized (in) {
            code = (byte) in.read();
            int length = in.read();
            
            if (length == -1) {
                throw new IOException();
            }
            
            bytes = new byte[length];
            
            if (length != in.read(bytes)) {
                throw new IOException();
            }
        }
    }

    /**
     * Provides code read by the last <code>receive()</code> invocation.
     * @return the code received last time, -1 if there is no valid value.
     */
    public byte getCode() {
        return code;
    }
    
    /**
     * Provides message read by the last <code>receive()</code> invocation.
     * @return string message received last time.
     * @exception EmulationException if no valid value read
     */
    public String getMessage() {
        try {
            return new String(bytes, 0, bytes.length, ENCODING);
        } catch (UnsupportedEncodingException e) {
            throw new EmulationException();
        }
    }

    /**
     * Retrieves integer represented by bytes read by the last 
     * <code>receive()</code> invocation. Throws IllegalArgument exception
     * if those bytes do not represent an integer.
     *
     * @return integer represented by bytes received last time.
     * @exception EmulationException if no valid value read
     */
    public int getInt() {
        if (bytes == null || bytes.length != 4) {
            throw new EmulationException();
        }
        
        int res = 0;
        for (int i = 3; i >= 0; i--) {
            res = (res << 8) | (bytes[i] & 0xff); 
        }
        
        return res;
    }
    
    /**
     * Provides bytes read by the last <code>receive()</code> invocation.
     * @return bytes received last time.
     */
    public byte[] getBytes() {
        return bytes;
    }
}