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

NotifierEmul.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.InterruptedIOException;
import javax.microedition.io.SocketConnection;
import javax.microedition.io.ServerSocketConnection;
import javax.microedition.io.Connector;
import com.sun.midp.io.BluetoothUrl;
import com.sun.midp.io.j2me.serversocket.Socket;
import com.sun.midp.security.SecurityToken;
import com.sun.midp.security.ImplicitlyTrustedClass;
import com.sun.midp.main.Configuration;
import com.sun.midp.jsr082.BluetoothUtils;
import com.sun.midp.jsr082.SecurityInitializer;

/**
 * Emulates JSR 82 notifier.
 */
public class NotifierEmul implements EmulUnit {
    /** Integer handle that identifies notifier at native level. */
    private int handle;

    /**
     * Inner class to request security token from SecurityInitializer.
     * SecurityInitializer should be able to check this inner class name.
     */
    static private class SecurityTrusted
        implements ImplicitlyTrustedClass {};

    /** Internal security token that grants access to restricted API. */
    private static SecurityToken internalSecurityToken =
        SecurityInitializer.requestToken(new SecurityTrusted());

    /** Device this notifier works at. */
    DeviceEmul device;
    
    /** Server socet to accept connections. */
    Socket serverSocket;
    
    /** next free socket port. */
    static int nextSocketPort = Configuration.getIntProperty(
            "com.sun.midp.jsr82emul.serverPort", 1234) + 1;
            
    /** 
     * Keeps options of service connection string used for this 
     * notifier creation.
     */
    private ServiceConnectionData serviceData;
    
    /** PSM or channel id. */
    int port;
    
    /** An emulation of client connection. */
    private ConnectionEmul clientConnection = null;
    
    /** Current connection acceptor, <code>null</code> if there is no one. */
    Acceptor acceptor = null;
    
    /** Represents socket client acception that runs in a separate thread. */
    private class Acceptor extends RunnableProcessor {
        /** 
         * Processes acception. 
         * Implementation for abstract method of the superclass.
         */
        protected void process() {
            try {
                if (serverSocket == null) {
                    // normally it means that interruption is in progress
                    throw new IOException("Improper acceptor state");
                }
                SocketConnection sc = (SocketConnection)serverSocket.acceptAndOpen();
                clientConnection = new ConnectionEmul(serviceData);
                clientConnection.open(sc);
            } catch (IOException e) {
                if (!interrupted) {
                    clientConnection.close();
                }
                clientConnection = null;
            }
            
            if (!interrupted) {
                if (clientConnection == null) {
                    notifyAccepted(handle, -1, null, -1, -1);
                } else {
                    notifyAccepted(handle, clientConnection.handle,
                        BluetoothUtils.getAddressBytes(
                            clientConnection.getRemoteAddress()),
                        clientConnection.getReceiveMTU(),
                        clientConnection.getTransmitMTU());
                }
            }
            acceptor = null;
        }
    }
    
    /**
     * Creates new instance and registers it in emulation environment with 
     * given handle. It is initialized by the next request to emulation.
     *
     * @param handle integer handle reserved for the new instance 
     *        in native layer.
     */
    public NotifierEmul(int handle) {
        this.handle = handle;
        // IMPL_NOTE redundant?
        device = DeviceEmul.getLocalDeviceEmul();
        
        // Registering this notifier as a processor for requests to emulation
        ConnRegistry.getInstance().register(handle, this);
    }
    
    /**
     * Initializes this notifier emulation unit.
     *
     * @param serviceData packed info on service details
     */
    public void initialize(ServiceConnectionData serviceData) {
        this.serviceData = serviceData;        
        
        serverSocket = new Socket();
    
        success:
        {
            final int maxTrials = 32;
            for (int i = 0; i < maxTrials; i++, nextSocketPort++) {
                try {
                    serverSocket.open(nextSocketPort, internalSecurityToken);
                } catch (IOException e) {
                    // consider port is busy and just continue trials
                    continue;
                }
                
                serviceData.socketPort = nextSocketPort++;
                break success;
            }
            
            error();
        }
    }
    
    /** Rgisters error occued to pass it to code above the porting layer. */
    private void error() {/*need revisit*/}

    /** 
     * Starts accepting incoming connection.
     * IMPL_NOTE it should completely substitute accept() when moving emul
     * below porting layer completed
     */
    public void startAccept() {
        acceptor = new Acceptor();
        acceptor.start();
        
        try {
            device.registerService(serviceData);
        
        } catch (IOException e) {
            error();
            // IMPL_NOTE 
            // add e details;
        }
    }
    
    /** 
     * Closes the notifier (emulation). 
     * @exception IOException if emulation server does not respond properly
     */
    public void close() {
        if (acceptor != null) {
            acceptor.interrupt();
            acceptor = null;
        }
            
        if (serverSocket != null) {
            try {
                serverSocket.close();
            } catch (IOException e) {
                error();
            }
            
            serverSocket = null;
        }
        
        clientConnection = null;
        
        ConnRegistry.getInstance().unregister(handle);
        if (-1 != serviceData.socketPort) {
            device.unregisterService(serviceData.socketPort);
        }
    }
    
    // IMPL_NOTE move to constants.xml
    /** Accept request code. */
    private static final byte NOTIF_ACCEPT = 0;
    /** Close request code. */
    private static final byte NOTIF_CLOSE = 1;
    /** Initialize request code. */
    private static final byte NOTIF_INIT = 2;
    
    /**
     * Processes request from porting layer to emulation.
     * @param request packed request to process
     */
    public void process(BytePack request) {
        switch(request.extract()) {
        case NOTIF_ACCEPT:
            Log.log("processing NOTIF_ACCEPT");
            startAccept();
            Log.log("processing NOTIF_ACCEPT done");
            break;
        case NOTIF_CLOSE:
            Log.log("processing NOTIF_CLOSE");
            close();
            Log.log("processing NOTIF_CLOSE done");
            break;
        case NOTIF_INIT:
            Log.log("processing NOTIF_INIT");
            initialize(new ServiceConnectionData(
                request.extractBytes(ServiceConnectionData.SERVER_DATA_SIZE),
                ServiceConnectionData.SERVER_DATA));
            Log.log("processing NOTIF_INIT done");
            break;
        default:
            throw new EmulationException("Unknown Notifier request");
        }
    }
    
    /** 
     * Notifies porting layer on completing acception. 
     * @param thisHandle
     * @param connHandle handle connection created as a result of acception,
     *         <code>-1</code> if acception failed.
     * @param peerAddr bluetooth address of device connected. 
     * @param receiveMTU established ReceiveMTU of accepted connection. 
     * @param transmitMTU established TransmitMTU of accepted connection.
     * 
     * IMPL_NOTE 
     * peerAddr may become useless when moving emul below porting layer 
     * completed; thisHandle may be retrieved thru KNI
     *
     */
    private native void notifyAccepted(int thisHandle, int connHandle, 
        byte[] peerAddr, int receiveMTU, int transmitMTU);
}