FileDocCategorySizeDatePackage
ServiceConnectionData.javaAPI DocphoneME MR2 API (J2ME)11843Wed May 02 18:00:30 BST 2007com.sun.midp.jsr82emul

ServiceConnectionData.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.UnsupportedEncodingException;
import com.sun.midp.io.BluetoothUrl;
import javax.bluetooth.BluetoothConnectionException;
import com.sun.midp.jsr082.BluetoothUtils;

/** 
 * Utility class that keeps service connection information, packs it
 * into byte array and restores form it. Has methods to check if a
 * service represented by an instance can accept a connection represented
 * by another one.
 */
class ServiceConnectionData extends BytePack {
    /** 
     * Error type, like in <code>BluetoothConnectionException</code>,
     * ff error occured while looking for connection, <code>-1</code> 
     * if there is no error. Only makes sense in case of CONNECTION_DATA.
     */
    int error = -1;
    
    /** TCP socket port emulated service listens on. */
    int socketPort;
    /** 
     * IP address or host name of service in case of CONNECTION_DATA,  
     * Bluetooth address of client in case of CLIENT_DATA
     */
    byte[] address;
    /** Channel ID or PSM. */
    int port;
    /** ReceiveMTU in server side terms. */
    int receiveMTU;
    /** TransmitMTU in server side terms. */
    int transmitMTU;
    /** Protocol ID as in <code>BluetoothUrl</code>. */
    int protocol;
    /** Shows wither server is master. */
    boolean master;
    /** Shows wither encrypted connection required. */
    boolean encrypt; 
    /** Shows wither authorization required by server. */
    boolean authorize; 
    /** Shows wither authentication required by server. */
    boolean authenticate;
    
    /** 
     * Identifies if a service defined by this instance accepting currently. 
     * This flag is used to overcome TCK inconsistency where client may
     * start connecting to Bluetooth TCK Agent while the latter is still not 
     * ready after previous connection.
     */
    private boolean accepting = true;
    /** 
     * Delay duration for the case when acception is requested while it is
     * not known if corresponding service is accepting.
     */
    private static final int ACCEPT_DELAY = 1000;
    /** 
     * Max amount of acception trials when it is not known if service 
     * is accepting 
     */
    private static final int MAX_ACCEPT_TRIALS = 24;
    
    /** 
     * Packed data type that implies that the following fields are included:
     * socketPort, port, receiveMTU, transmitMTU, receiveMTU, protocol, 
     * master, encrypt, authorize, authenticate.
     */
    static final int SERVER_DATA = 0; 
    /** 
     * Packed data type that implies that the following fields are included:
     * socketPort, IP address, receiveMTU, transmitMTU.
     */
    static final int CONNECTION_DATA = 1; 
    /** 
     * Packed data type that implies that the following fields are included:
     * transmitMTU, receiveMTU, Bluetooth address.
     */
    static final int CLIENT_DATA = 2; 
    /** 
     * Packed data type that implies that the following fields are included:
     * Bluetooth address, protocol, port, transmitMTU, receiveMTU, encrypt,
     * authenticate, master.
     * It is used CONNECT_TO_SERVICE request to the emulation server
     */
    static final int CONN_REQUEST_DATA = 3; 
    
    /** Size of Bluetooth address in bytes representation. */
    private static final int BTADDR_SIZE = 6;
    
    /** Byte array size when packed with CLIENT_DATA type. */
    static final int CLIENT_DATA_SIZE = 4 * 2 + BTADDR_SIZE;
    /** Byte array size when packed with SERVER_DATA type. */
    static final int SERVER_DATA_SIZE = 4 * 4 + 1;
    
    /** Bit mask to highlight protcol type. */
    private final static int PROTOCOL_MASK = 3;
    /** Bit mask to highlight master flag. */
    private final static int MASTER_MASK = 4;
    /** Bit mask to highlight encrypt flas. */
    private final static int ENCRYPT_MASK = 8;
    /** Bit mask to highlight authorize flag. */
    private final static int AUTHORIZE_MASK = 32;
    /** Bit mask to highlight authenticate flag. */
    private final static int AUTHENTICATE_MASK = 16;
    
    /** 
     * Constructs an instance which indicates that unknown error occured
     * while looking for connection.
     */
    ServiceConnectionData() {
        this(BluetoothConnectionException.FAILED_NOINFO);
    }
    
    /** 
     * Constructs an instance which indicates that a connection error occured.
     * @param error error code as in <code>BluetoothConnectionException</code>
     */
    ServiceConnectionData(int error) {
        this.error = error;
    }
    
    /**
     * Constructs an instance by given byte representation.
     * @param data byte representation
     * @param type type of packed data, must be one of <code>
     * SERVER_DATA, CONNECTION_DATA, CLIENT_DATA, CONN_REQUEST_DATA 
     * </code>
     */
    ServiceConnectionData(byte[] data, int type) {
        super(data);        

        switch (type) {        
        case CONN_REQUEST_DATA:
            address = extractBytes(BTADDR_SIZE);
        
        // the rest of CONN_REQUEST_DATA case is poceeded here as well
        case SERVER_DATA:
            if (type == SERVER_DATA) {
                socketPort = extractInt();
            }
            
            port = extractInt();
            receiveMTU = extractInt();
            transmitMTU = extractInt();
        
            int misc = extract();
            protocol = misc & PROTOCOL_MASK;
            master = (misc & MASTER_MASK) != 0;
            encrypt = (misc & ENCRYPT_MASK) != 0;
            authorize = (misc & AUTHORIZE_MASK) != 0;
            authenticate = (misc & AUTHORIZE_MASK) != 0;
            break;
            
        case CONNECTION_DATA:
            error = extractInt();
            if (error == -1) {
                socketPort = extractInt();
                receiveMTU = extractInt();
                transmitMTU = extractInt();
                address = extractBytes(Const.IP_SIZE);
            }
            break;
        
        case CLIENT_DATA:
            receiveMTU = extractInt();
            transmitMTU = extractInt();
            address = extractBytes(BTADDR_SIZE);
            break;
            
        default:
            throw new IllegalArgumentException();
        }
        
        release();
    }
         
    /**
     * Retrieves bytes representation.     
     * @param type type of packed data, that defines which fields
     *     are to be packed, must be one of <code>
     *     SERVER_DATA, CONNECTION_DATA, CLIENT_DATA, CONN_REQUEST_DATA
     *     </code>
     * @return byte array that keeps packed properties
     */
    byte[] toByteArray(int type) {
        switch (type) {
        case CONN_REQUEST_DATA:
            reset(new byte[BTADDR_SIZE + SERVER_DATA_SIZE - 1]);
            appendBytes(address);
        
        // the rest of CONN_REQUEST_DATA is proceeded here as well
        case SERVER_DATA:
            if (type == SERVER_DATA) {
                reset(new byte[SERVER_DATA_SIZE]);
                appendInt(socketPort);
            }
            
            appendInt(port);
            appendInt(receiveMTU);
            appendInt(transmitMTU);
        
            int misc = protocol;
            misc |= master? MASTER_MASK : 0;
            misc |= encrypt? ENCRYPT_MASK : 0;
            misc |= authorize? AUTHORIZE_MASK : 0;
            misc |= authenticate? AUTHORIZE_MASK : 0;
            append((byte)misc);
            break;
        
        case CONNECTION_DATA:
            if (error == -1) {
                reset(new byte[4 * 4 + Const.IP_SIZE]);
                appendInt(error);
                appendInt(socketPort);
                appendInt(receiveMTU);
                appendInt(transmitMTU);
                appendBytes(address);        
            
            } else {
                reset(new byte[4]);
                appendInt(error);
            }
            break;
            
        case CLIENT_DATA:
            reset(new byte[CLIENT_DATA_SIZE]);
            appendInt(receiveMTU);
            appendInt(transmitMTU);
            appendBytes(address);
            break;
            
        default:
            throw new IllegalArgumentException();
        }
        
        return release();
    }
     
    /**
     * Checks if client connection with given parameters can be accepted,
     * if it can not, sets error code. If acception is possible but 
     * requires fields alignment, makes it modifying connection data 
     * passed as a parameter.
     *
     * @param client parameters of client connection request derived from 
     *         client url, the parameters can be modified by this method
     *         if connection can be accepted.
     * @return error code as in <code>BluetoothConnectionException</code>, if
     *         connection can not be accepted, <code>-1</code> if it can
     */
    void accept(ServiceConnectionData client) {
        client.address = address;
        client.socketPort = socketPort;
        client.error = -1;
        
        if (client.master && master) {
            client.error = BluetoothConnectionException.UNACCEPTABLE_PARAMS;
        
        } else if (client.protocol == BluetoothUrl.L2CAP) {
            // If connection accepted successfully,
            // (receiveMTU, transmitMTU) set here for the parameter 
            // become actual (receiveMTU, transmitMTU) for server and
            // actual (transmitMTU, receiveMTU) for client.
            
            if (client.transmitMTU > receiveMTU || transmitMTU > client.receiveMTU) {
                client.error = BluetoothConnectionException.UNACCEPTABLE_PARAMS;
            } else {
                if (client.transmitMTU == -1) {
                    client.transmitMTU = receiveMTU;
                }
                if (transmitMTU != -1) {
                    client.receiveMTU = transmitMTU;
                }
            }
        }
        
        if (client.error == -1) {
            synchronized (this) {
                int trial = 0;
                for (; trial < MAX_ACCEPT_TRIALS && !accepting; trial++) {
                    try {
                        Thread.sleep(ACCEPT_DELAY);
                    } catch (Throwable t) {}
                }
                
                if (trial == MAX_ACCEPT_TRIALS) {
                    client.error = BluetoothConnectionException.TIMEOUT;
                } else {
                    // accepted successfully, now service is occupied by current client
                    accepting = false;
                }
            }
        }
    }
    
    /** 
     * Sets acception flag to true to identify that represented service 
     * is currebtly accepting. A part of the way that overcomes TCK 
     * inconsistency.
     */
    void setAccepting() {
        accepting = true;
    }
}