/*
 * Copyright (C) 2009 The Sipdroid Open Source Project
 * 
 * This file is part of Sipdroid (http://www.sipdroid.org)
 * 
 * Sipdroid is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This source code 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 for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this source code; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

// BEGIN android-note
// address length was changed from long to int for performance reasons.
// END android-note

package org.sipdroid.net.impl;

import java.io.FileDescriptor;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketImpl;
import java.net.UnknownHostException;
import java.nio.channels.Channel;
// BEGIN android-removed
// import java.nio.channels.SelectableChannel;
// END android-removed
/*
 * 
 * This Class is used for native code wrap, the implement class of
 * INetworkSystem.
 * 
 */
public final class OSNetworkSystem {

    // ----------------------------------------------------
    // Class Variables
    // ----------------------------------------------------

    private static final int ERRORCODE_SOCKET_TIMEOUT = -209;

    private static OSNetworkSystem ref = new OSNetworkSystem();
    
//    private static final int INETADDR_REACHABLE = 0;
    
    private static boolean isNetworkInited = false;
    
    // ----------------------------------------------------
    // Class Constructor
    // ----------------------------------------------------

    // can not be instantiated.
    private OSNetworkSystem() {
        super();
    }

    /*
     * @return a static ref of this class
     */
    public static OSNetworkSystem getOSNetworkSystem() {
        return ref;
    }

    // Useing when cache set/get is OK
    // public static native void oneTimeInitializationDatagram(
    // boolean jcl_IPv6_support);
    //
    // public static native void oneTimeInitializationSocket(
    // boolean jcl_IPv6_support);

    // --------------------------------------------------
    // java codes that wrap native codes
    // --------------------------------------------------

    public void createSocket(FileDescriptor fd, boolean preferIPv4Stack)
            throws IOException {
        createSocketImpl(fd, preferIPv4Stack);
    }

    public void createDatagramSocket(FileDescriptor fd, boolean preferIPv4Stack)
            throws SocketException {
        createDatagramSocketImpl(fd, preferIPv4Stack);
    }

    public int read(FileDescriptor aFD, byte[] data, int offset, int count,
            int timeout) throws IOException {
        return readSocketImpl(aFD, data, offset, count, timeout);
    }
    
    public int readDirect(FileDescriptor aFD, int address, int offset, int count,
            int timeout) throws IOException {
        return readSocketDirectImpl(aFD, address, offset, count, timeout);
    }

    public int write(FileDescriptor aFD, byte[] data, int offset, int count)
            throws IOException {
        return writeSocketImpl(aFD, data, offset, count);
    }
    
    public int writeDirect(FileDescriptor aFD, int address, int offset,
            int count) throws IOException {
        return writeSocketDirectImpl(aFD, address, offset, count);
    }

    public void setNonBlocking(FileDescriptor aFD, boolean block)
            throws IOException {
        setNonBlockingImpl(aFD, block);
    }

    public void connectDatagram(FileDescriptor aFD, int port, int trafficClass,
            InetAddress inetAddress) throws SocketException {
        connectDatagramImpl2(aFD, port, trafficClass, inetAddress);
    }

    public int connect(FileDescriptor aFD, int trafficClass,
            InetAddress inetAddress, int port)  throws IOException{
        return connectSocketImpl(aFD, trafficClass, inetAddress, port);
    }

    // BEGIN android-changed
    public int connectWithTimeout(FileDescriptor aFD, int timeout,
            int trafficClass, InetAddress inetAddress, int port, int step,
            byte[] context)  throws IOException{
        return connectWithTimeoutSocketImpl(aFD, timeout, trafficClass,
                inetAddress, port, step, context);
    }
    // END android-changed

    public void connectStreamWithTimeoutSocket(FileDescriptor aFD, int aport,
            int timeout, int trafficClass, InetAddress inetAddress)
            throws IOException {
        connectStreamWithTimeoutSocketImpl(aFD, aport, timeout, trafficClass,
                inetAddress);
    }

    public void bind(FileDescriptor aFD, int port, InetAddress inetAddress)
            throws SocketException {
        socketBindImpl(aFD, port, inetAddress);
    }

    public boolean bind2(FileDescriptor aFD, int port, boolean bindToDevice,
            InetAddress inetAddress) throws SocketException {
        return socketBindImpl2(aFD, port, bindToDevice, inetAddress);
    }

    public void accept(FileDescriptor fdServer, SocketImpl newSocket,
            FileDescriptor fdnewSocket, int timeout) throws IOException {
        acceptSocketImpl(fdServer, newSocket, fdnewSocket, timeout);
    }

    public int sendDatagram(FileDescriptor fd, byte[] data, int offset,
            int length, int port, boolean bindToDevice, int trafficClass,
            InetAddress inetAddress) throws IOException {
        return sendDatagramImpl(fd, data, offset, length, port, bindToDevice,
                trafficClass, inetAddress);
    }
    
    public int sendDatagramDirect(FileDescriptor fd, int address, int offset,
            int length, int port, boolean bindToDevice, int trafficClass,
            InetAddress inetAddress) throws IOException {
        return sendDatagramDirectImpl(fd, address, offset, length, port, bindToDevice,
                trafficClass, inetAddress);
    }

    public int sendDatagram2(FileDescriptor fd, byte[] data, int offset,
            int length, int port, InetAddress inetAddress) throws IOException {
        return sendDatagramImpl2(fd, data, offset, length, port, inetAddress);
    }

    public int receiveDatagram(FileDescriptor aFD, DatagramPacket packet,
            byte[] data, int offset, int length, int receiveTimeout,
            boolean peek) throws IOException {
        return receiveDatagramImpl(aFD, packet, data, offset, length,
                receiveTimeout, peek);
    }
    
    public int receiveDatagramDirect(FileDescriptor aFD, DatagramPacket packet,
            int address, int offset, int length, int receiveTimeout,
            boolean peek) throws IOException {
        return receiveDatagramDirectImpl(aFD, packet, address, offset, length,
                receiveTimeout, peek);
    }

    public int recvConnectedDatagram(FileDescriptor aFD, DatagramPacket packet,
            byte[] data, int offset, int length, int receiveTimeout,
            boolean peek) throws IOException {
        return recvConnectedDatagramImpl(aFD, packet, data, offset, length,
                receiveTimeout, peek);
    }
    
    public int recvConnectedDatagramDirect(FileDescriptor aFD, DatagramPacket packet, int address,
             int offset, int length, int receiveTimeout, boolean peek)
            throws IOException {
        return recvConnectedDatagramDirectImpl(aFD, packet, address, offset, length, receiveTimeout, peek);
    }

    public int peekDatagram(FileDescriptor aFD, InetAddress sender,
            int receiveTimeout) throws IOException {
        return peekDatagramImpl(aFD, sender, receiveTimeout);
    }

    public int sendConnectedDatagram(FileDescriptor fd, byte[] data,
            int offset, int length, boolean bindToDevice) throws IOException {
        return sendConnectedDatagramImpl(fd, data, offset, length, bindToDevice);
    }
    
    public int sendConnectedDatagramDirect(FileDescriptor fd, int address,
            int offset, int length, boolean bindToDevice) throws IOException {
        return sendConnectedDatagramDirectImpl(fd, address, offset, length, bindToDevice);
    }

    public void disconnectDatagram(FileDescriptor aFD) throws SocketException {
        disconnectDatagramImpl(aFD);
    }

    public void createMulticastSocket(FileDescriptor aFD,
            boolean preferIPv4Stack) throws SocketException {
        createMulticastSocketImpl(aFD, preferIPv4Stack);
    }

    public void createServerStreamSocket(FileDescriptor aFD,
            boolean preferIPv4Stack) throws SocketException {
        createServerStreamSocketImpl(aFD, preferIPv4Stack);
    }

    public int receiveStream(FileDescriptor aFD, byte[] data, int offset,
            int count, int timeout) throws IOException {
        return receiveStreamImpl(aFD, data, offset, count, timeout);
    }

    public int sendStream(FileDescriptor fd, byte[] data, int offset, int count)
            throws IOException {
        return sendStreamImpl(fd, data, offset, count);
    }

    public void shutdownInput(FileDescriptor descriptor) throws IOException {
        shutdownInputImpl(descriptor);
    }

    public void shutdownOutput(FileDescriptor descriptor) throws IOException {
        shutdownOutputImpl(descriptor);
    }

    public boolean supportsUrgentData(FileDescriptor fd) {
        return supportsUrgentDataImpl(fd);
    }

    public void sendUrgentData(FileDescriptor fd, byte value) {
        sendUrgentDataImpl(fd, value);
    }

    public int availableStream(FileDescriptor aFD) throws SocketException {
        return availableStreamImpl(aFD);
    }

    // BEGIN android-removed
    // public void acceptStreamSocket(FileDescriptor fdServer,
    //         SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout)
    //         throws IOException {
    //     acceptStreamSocketImpl(fdServer, newSocket, fdnewSocket, timeout);
    // }
    // 
    // public void createStreamSocket(FileDescriptor aFD, boolean preferIPv4Stack)
    //         throws SocketException {
    //     createStreamSocketImpl(aFD, preferIPv4Stack);
    // }
    // END android-removed

    public void listenStreamSocket(FileDescriptor aFD, int backlog)
            throws SocketException {
        listenStreamSocketImpl(aFD, backlog);
    }
    
    // BEGIN android-removed
    // public boolean isReachableByICMP(final InetAddress dest,
    //         InetAddress source, final int ttl, final int timeout) {
    //     return INETADDR_REACHABLE == isReachableByICMPImpl(dest, source, ttl,
    //             timeout);
    // }
    // END android-removed

    /*
     * 
     * @param 
     *      readChannels all channels interested in read and accept 
     * @param
     *      writeChannels all channels interested in write and connect 
     * @param timeout
     *      timeout in millis @return a set of channels that are ready for operation
     * @throws 
     *      SocketException @return int array, each int approve one of the     * channel if OK
     */

    public int[] select(FileDescriptor[] readFDs,
            FileDescriptor[] writeFDs, long timeout)
            throws SocketException {
        int countRead = readFDs.length;
        int countWrite = writeFDs.length;
        int result = 0;
        if (0 == countRead + countWrite) {
            return (new int[0]);
        }
        int[] flags = new int[countRead + countWrite];

        // handle timeout in native
        result = selectImpl(readFDs, writeFDs, countRead, countWrite, flags,
                timeout);

        if (0 <= result) {
            return flags;
        }
        if (ERRORCODE_SOCKET_TIMEOUT == result) {
            return new int[0];
        }
        throw new SocketException();

    }

    public InetAddress getSocketLocalAddress(FileDescriptor aFD,
            boolean preferIPv6Addresses) {
        return getSocketLocalAddressImpl(aFD, preferIPv6Addresses);
    }

    /*
     * Query the IP stack for the local port to which this socket is bound.
     * 
     * @param aFD the socket descriptor @param preferIPv6Addresses address
     * preference for nodes that support both IPv4 and IPv6 @return int the
     * local port to which the socket is bound
     */
    public int getSocketLocalPort(FileDescriptor aFD,
            boolean preferIPv6Addresses) {
        return getSocketLocalPortImpl(aFD, preferIPv6Addresses);
    }

    /*
     * Query the IP stack for the nominated socket option.
     * 
     * @param aFD the socket descriptor @param opt the socket option type
     * @return the nominated socket option value
     * 
     * @throws SocketException if the option is invalid
     */
    public Object getSocketOption(FileDescriptor aFD, int opt)
            throws SocketException {
        return getSocketOptionImpl(aFD, opt);
    }

    /*
     * Set the nominated socket option in the IP stack.
     * 
     * @param aFD the socket descriptor @param opt the option selector @param
     * optVal the nominated option value
     * 
     * @throws SocketException if the option is invalid or cannot be set
     */
    public void setSocketOption(FileDescriptor aFD, int opt, Object optVal)
            throws SocketException {
        setSocketOptionImpl(aFD, opt, optVal);
    }

    public int getSocketFlags() {
        return getSocketFlagsImpl();
    }

    /*
     * Close the socket in the IP stack.
     * 
     * @param aFD the socket descriptor
     */
    public void socketClose(FileDescriptor aFD) throws IOException {
        socketCloseImpl(aFD);
    }

    public InetAddress getHostByAddr(byte[] addr) throws UnknownHostException {
        return getHostByAddrImpl(addr);
    }

    public InetAddress getHostByName(String addr, boolean preferIPv6Addresses)
            throws UnknownHostException {
        return getHostByNameImpl(addr, preferIPv6Addresses);
    }

    public void setInetAddress(InetAddress sender, byte[] address) {
        setInetAddressImpl(sender, address);
    }

    // ---------------------------------------------------
    // Native Codes
    // ---------------------------------------------------

    static native void createSocketImpl(FileDescriptor fd,
            boolean preferIPv4Stack);

    /*
     * Allocate a datagram socket in the IP stack. The socket is associated with
     * the <code>aFD</code>.
     * 
     * @param aFD the FileDescriptor to associate with the socket @param
     * preferIPv4Stack IP stack preference if underlying platform is V4/V6
     * @exception SocketException upon an allocation error
     */
    static native void createDatagramSocketImpl(FileDescriptor aFD,
            boolean preferIPv4Stack) throws SocketException;

    static native int readSocketImpl(FileDescriptor aFD, byte[] data,
            int offset, int count, int timeout) throws IOException;
    
    static native int readSocketDirectImpl(FileDescriptor aFD, int address,
            int offset, int count, int timeout) throws IOException;

    static native int writeSocketImpl(FileDescriptor fd, byte[] data,
            int offset, int count) throws IOException;
    
    static native int writeSocketDirectImpl(FileDescriptor fd, int address,
            int offset, int count) throws IOException;

    static native void setNonBlockingImpl(FileDescriptor aFD,
            boolean block);

    static native int connectSocketImpl(FileDescriptor aFD,
            int trafficClass, InetAddress inetAddress, int port);

    // BEGIN android-changed
    static native int connectWithTimeoutSocketImpl(
            FileDescriptor aFD, int timeout, int trafficClass,
            InetAddress hostname, int port, int step, byte[] context);
    // END android-changed

    static native void connectStreamWithTimeoutSocketImpl(FileDescriptor aFD,
            int aport, int timeout, int trafficClass, InetAddress inetAddress)
            throws IOException;

    static native void socketBindImpl(FileDescriptor aFD, int port,
            InetAddress inetAddress) throws SocketException;

    static native void listenStreamSocketImpl(FileDescriptor aFD, int backlog)
            throws SocketException;

    static native int availableStreamImpl(FileDescriptor aFD)
            throws SocketException;

    static native void acceptSocketImpl(FileDescriptor fdServer,
            SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout)
            throws IOException;

    static native boolean supportsUrgentDataImpl(FileDescriptor fd);

    static native void sendUrgentDataImpl(FileDescriptor fd, byte value);

    /*
     * Connect the socket to a port and address
     * 
     * @param aFD the FileDescriptor to associate with the socket @param port
     * the port to connect to @param trafficClass the traffic Class to be used
     * then the connection is made @param inetAddress address to connect to.
     * 
     * @exception SocketException if the connect fails
     */
    static native void connectDatagramImpl2(FileDescriptor aFD,
            int port, int trafficClass, InetAddress inetAddress)
            throws SocketException;

    /*
     * Disconnect the socket to a port and address
     * 
     * @param aFD the FileDescriptor to associate with the socket
     * 
     * @exception SocketException if the disconnect fails
     */
    static native void disconnectDatagramImpl(FileDescriptor aFD)
            throws SocketException;

    /*
     * Allocate a datagram socket in the IP stack. The socket is associated with
     * the <code>aFD</code>.
     * 
     * @param aFD the FileDescriptor to associate with the socket @param
     * preferIPv4Stack IP stack preference if underlying platform is V4/V6
     * @exception SocketException upon an allocation error
     */

    /*
     * Bind the socket to the port/localhost in the IP stack.
     * 
     * @param aFD the socket descriptor @param port the option selector @param
     * bindToDevice bind the socket to the specified interface @param
     * inetAddress address to connect to. @return if bind successful @exception
     * SocketException thrown if bind operation fails
     */
    static native boolean socketBindImpl2(FileDescriptor aFD,
            int port, boolean bindToDevice, InetAddress inetAddress)
            throws SocketException;

    /*
     * Peek on the socket, update <code>sender</code> address and answer the
     * sender port.
     * 
     * @param aFD the socket FileDescriptor @param sender an InetAddress, to be
     * updated with the sender's address @param receiveTimeout the maximum
     * length of time the socket should block, reading @return int the sender
     * port
     * 
     * @exception IOException upon an read error or timeout
     */
    static native int peekDatagramImpl(FileDescriptor aFD,
            InetAddress sender, int receiveTimeout) throws IOException;

    /*
     * Recieve data on the socket into the specified buffer. The packet fields
     * <code>data</code> & <code>length</code> are passed in addition to
     * <code>packet</code> to eliminate the JNI field access calls.
     * 
     * @param aFD the socket FileDescriptor @param packet the DatagramPacket to
     * receive into @param data the data buffer of the packet @param offset the
     * offset in the data buffer @param length the length of the data buffer in
     * the packet @param receiveTimeout the maximum length of time the socket
     * should block, reading @param peek indicates to peek at the data @return
     * number of data received @exception IOException upon an read error or
     * timeout
     */
    static native int receiveDatagramImpl(FileDescriptor aFD,
            DatagramPacket packet, byte[] data, int offset, int length,
            int receiveTimeout, boolean peek) throws IOException;
    
    static native int receiveDatagramDirectImpl(FileDescriptor aFD,
            DatagramPacket packet, int address, int offset, int length,
            int receiveTimeout, boolean peek) throws IOException;

    /*
     * Recieve data on the connected socket into the specified buffer. The
     * packet fields <code>data</code> & <code>length</code> are passed in
     * addition to <code>packet</code> to eliminate the JNI field access
     * calls.
     * 
     * @param aFD the socket FileDescriptor @param packet the DatagramPacket to
     * receive into @param data the data buffer of the packet @param offset the
     * offset in the data buffer @param length the length of the data buffer in
     * the packet @param receiveTimeout the maximum length of time the socket
     * should block, reading @param peek indicates to peek at the data @return
     * number of data received @exception IOException upon an read error or
     * timeout
     */
    static native int recvConnectedDatagramImpl(FileDescriptor aFD,
            DatagramPacket packet, byte[] data, int offset, int length,
            int receiveTimeout, boolean peek) throws IOException;
    
    static native int recvConnectedDatagramDirectImpl(FileDescriptor aFD,
            DatagramPacket packet, int address, int offset, int length,
            int receiveTimeout, boolean peek) throws IOException;

    /*
     * Send the <code>data</code> to the nominated target <code>address</code>
     * and <code>port</code>. These values are derived from the
     * DatagramPacket to reduce the field calls within JNI.
     * 
     * @param fd the socket FileDescriptor @param data the data buffer of the
     * packet @param offset the offset in the data buffer @param length the
     * length of the data buffer in the packet @param port the target host port
     * @param bindToDevice if bind to device @param trafficClass the traffic
     * class to be used when the datagram is sent @param inetAddress address to
     * connect to. @return number of data send
     * 
     * @exception IOException upon an read error or timeout
     */
    static native int sendDatagramImpl(FileDescriptor fd,
            byte[] data, int offset, int length, int port,
            boolean bindToDevice, int trafficClass, InetAddress inetAddress)
            throws IOException;
    
    static native int sendDatagramDirectImpl(FileDescriptor fd,
            int address, int offset, int length, int port,
            boolean bindToDevice, int trafficClass, InetAddress inetAddress)
            throws IOException;

    /*
     * Send the <code>data</code> to the address and port to which the was
     * connnected and <code>port</code>.
     * 
     * @param fd the socket FileDescriptor @param data the data buffer of the
     * packet @param offset the offset in the data buffer @param length the
     * length of the data buffer in the packet @param bindToDevice not used,
     * current kept in case needed as was the case for sendDatagramImpl @return
     * number of data send @exception IOException upon an read error or timeout
     */
    static native int sendConnectedDatagramImpl(FileDescriptor fd,
            byte[] data, int offset, int length, boolean bindToDevice)
            throws IOException;
    
    static native int sendConnectedDatagramDirectImpl(FileDescriptor fd,
            int address, int offset, int length, boolean bindToDevice)
            throws IOException;

    /*
     * Answer the result of attempting to create a server stream socket in the
     * IP stack. Any special options required for server sockets will be set by
     * this method.
     * 
     * @param aFD the socket FileDescriptor @param preferIPv4Stack if use IPV4
     * @exception SocketException if an error occurs while creating the socket
     */
    static native void createServerStreamSocketImpl(FileDescriptor aFD,
            boolean preferIPv4Stack) throws SocketException;

    /*
     * Answer the result of attempting to create a multicast socket in the IP
     * stack. Any special options required for server sockets will be set by
     * this method.
     * 
     * @param aFD the socket FileDescriptor @param preferIPv4Stack if use IPV4
     * @exception SocketException if an error occurs while creating the socket
     */
    static native void createMulticastSocketImpl(FileDescriptor aFD,
            boolean preferIPv4Stack) throws SocketException;

    /*
     * Recieve at most <code>count</code> bytes into the buffer <code>data</code>
     * at the <code>offset</code> on the socket.
     * 
     * @param aFD the socket FileDescriptor @param data the receive buffer
     * @param offset the offset into the buffer @param count the max number of
     * bytes to receive @param timeout the max time the read operation should
     * block waiting for data @return int the actual number of bytes read
     * @throws IOException @exception SocketException if an error occurs while
     * reading
     */
    static native int receiveStreamImpl(FileDescriptor aFD, byte[] data,
            int offset, int count, int timeout) throws IOException;

    /*
     * Send <code>count</code> bytes from the buffer <code>data</code> at
     * the <code>offset</code>, on the socket.
     * 
     * @param fd
     * 
     * @param data the send buffer @param offset the offset into the buffer
     * @param count the number of bytes to receive @return int the actual number
     * of bytes sent @throws IOException @exception SocketException if an error
     * occurs while writing
     */
    static native int sendStreamImpl(FileDescriptor fd, byte[] data,
            int offset, int count) throws IOException;

    private native void shutdownInputImpl(FileDescriptor descriptor)
            throws IOException;

    private native void shutdownOutputImpl(FileDescriptor descriptor)
            throws IOException;

    // BEGIN android-removed
    // static native void acceptStreamSocketImpl(FileDescriptor fdServer,
    //         SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout)
    //         throws IOException;
    // 
    // static native void createStreamSocketImpl(FileDescriptor aFD,
    //         boolean preferIPv4Stack) throws SocketException;
    // END android-removed

    static native int sendDatagramImpl2(FileDescriptor fd, byte[] data,
            int offset, int length, int port, InetAddress inetAddress)
            throws IOException;

    static native int selectImpl(FileDescriptor[] readfd,
            FileDescriptor[] writefd, int cread, int cwirte, int[] flags,
            long timeout);

    static native InetAddress getSocketLocalAddressImpl(FileDescriptor aFD,
            boolean preferIPv6Addresses);

    /*
     * Query the IP stack for the local port to which this socket is bound.
     * 
     * @param aFD the socket descriptor @param preferIPv6Addresses address
     * preference for nodes that support both IPv4 and IPv6 @return int the
     * local port to which the socket is bound
     */
    static native int getSocketLocalPortImpl(FileDescriptor aFD,
            boolean preferIPv6Addresses);

    /*
     * Query the IP stack for the nominated socket option.
     * 
     * @param aFD the socket descriptor @param opt the socket option type
     * @return the nominated socket option value
     * 
     * @throws SocketException if the option is invalid
     */
    static native Object getSocketOptionImpl(FileDescriptor aFD, int opt)
            throws SocketException;

    /*
     * Set the nominated socket option in the IP stack.
     * 
     * @param aFD the socket descriptor @param opt the option selector @param
     * optVal the nominated option value
     * 
     * @throws SocketException if the option is invalid or cannot be set
     */
    static native void setSocketOptionImpl(FileDescriptor aFD, int opt,
            Object optVal) throws SocketException;

    static native int getSocketFlagsImpl();

    /*
     * Close the socket in the IP stack.
     * 
     * @param aFD the socket descriptor
     */
    static native void socketCloseImpl(FileDescriptor aFD);

    static native InetAddress getHostByAddrImpl(byte[] addr)
            throws UnknownHostException;

    static native InetAddress getHostByNameImpl(String addr,
            boolean preferIPv6Addresses) throws UnknownHostException;

    native void setInetAddressImpl(InetAddress sender, byte[] address);

    // BEGIN android-removed
    // native int isReachableByICMPImpl(InetAddress addr, InetAddress local,
    //         int ttl, int timeout);
    // END android-removed
    
    native Channel inheritedChannelImpl();

    public Channel inheritedChannel() {
        return inheritedChannelImpl();
    }
    
    public void oneTimeInitialization(boolean jcl_supports_ipv6){
        if (!isNetworkInited){
            oneTimeInitializationImpl(jcl_supports_ipv6);
            isNetworkInited = true;
        } 
    }
    
    native void oneTimeInitializationImpl (boolean jcl_supports_ipv6);
}