1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 1995, 2007, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.  Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27package java.net;
28
29import java.io.FileDescriptor;
30import java.io.FileOutputStream;
31import java.io.IOException;
32import java.nio.channels.FileChannel;
33
34import dalvik.system.BlockGuard;
35import sun.misc.IoTrace;
36
37/**
38 * This stream extends FileOutputStream to implement a
39 * SocketOutputStream. Note that this class should <b>NOT</b> be
40 * public.
41 *
42 * @author      Jonathan Payne
43 * @author      Arthur van Hoff
44 */
45class SocketOutputStream extends FileOutputStream
46{
47    private AbstractPlainSocketImpl impl = null;
48    private byte temp[] = new byte[1];
49    private Socket socket = null;
50
51    /**
52     * Creates a new SocketOutputStream. Can only be called
53     * by a Socket. This method needs to hang on to the owner Socket so
54     * that the fd will not be closed.
55     * @param impl the socket output stream inplemented
56     */
57    SocketOutputStream(AbstractPlainSocketImpl impl) throws IOException {
58        super(impl.getFileDescriptor());
59        this.impl = impl;
60        socket = impl.getSocket();
61    }
62
63    /**
64     * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
65     * object associated with this file output stream. </p>
66     *
67     * The <code>getChannel</code> method of <code>SocketOutputStream</code>
68     * returns <code>null</code> since it is a socket based stream.</p>
69     *
70     * @return  the file channel associated with this file output stream
71     *
72     * @since 1.4
73     * @spec JSR-51
74     */
75    public final FileChannel getChannel() {
76        return null;
77    }
78
79    /**
80     * Writes to the socket.
81     * @param fd the FileDescriptor
82     * @param b the data to be written
83     * @param off the start offset in the data
84     * @param len the number of bytes that are written
85     * @exception IOException If an I/O error has occurred.
86     */
87    private native void socketWrite0(FileDescriptor fd, byte[] b, int off,
88                                     int len) throws IOException;
89
90    /**
91     * Writes to the socket with appropriate locking of the
92     * FileDescriptor.
93     * @param b the data to be written
94     * @param off the start offset in the data
95     * @param len the number of bytes that are written
96     * @exception IOException If an I/O error has occurred.
97     */
98    private void socketWrite(byte b[], int off, int len) throws IOException {
99
100        if (len <= 0 || off < 0 || off + len > b.length) {
101            if (len == 0) {
102                return;
103            }
104            throw new ArrayIndexOutOfBoundsException();
105        }
106
107        Object traceContext = IoTrace.socketWriteBegin();
108        int bytesWritten = 0;
109        FileDescriptor fd = impl.acquireFD();
110        try {
111            BlockGuard.getThreadPolicy().onNetwork();
112            socketWrite0(fd, b, off, len);
113            bytesWritten = len;
114        } catch (SocketException se) {
115            if (se instanceof sun.net.ConnectionResetException) {
116                impl.setConnectionResetPending();
117                se = new SocketException("Connection reset");
118            }
119            if (impl.isClosedOrPending()) {
120                throw new SocketException("Socket closed");
121            } else {
122                throw se;
123            }
124        } finally {
125            IoTrace.socketWriteEnd(traceContext, impl.address, impl.port, bytesWritten);
126        }
127    }
128
129    /**
130     * Writes a byte to the socket.
131     * @param b the data to be written
132     * @exception IOException If an I/O error has occurred.
133     */
134    public void write(int b) throws IOException {
135        temp[0] = (byte)b;
136        socketWrite(temp, 0, 1);
137    }
138
139    /**
140     * Writes the contents of the buffer <i>b</i> to the socket.
141     * @param b the data to be written
142     * @exception SocketException If an I/O error has occurred.
143     */
144    public void write(byte b[]) throws IOException {
145        socketWrite(b, 0, b.length);
146    }
147
148    /**
149     * Writes <i>length</i> bytes from buffer <i>b</i> starting at
150     * offset <i>len</i>.
151     * @param b the data to be written
152     * @param off the start offset in the data
153     * @param len the number of bytes that are written
154     * @exception SocketException If an I/O error has occurred.
155     */
156    public void write(byte b[], int off, int len) throws IOException {
157        socketWrite(b, off, len);
158    }
159
160    /**
161     * Closes the stream.
162     */
163    private boolean closing = false;
164    public void close() throws IOException {
165        // Prevent recursion. See BugId 4484411
166        if (closing)
167            return;
168        closing = true;
169        if (socket != null) {
170            if (!socket.isClosed())
171                socket.close();
172        } else
173            impl.close();
174        closing = false;
175    }
176
177    /**
178     * Overrides finalize, the fd is closed by the Socket.
179     */
180    protected void finalize() {}
181}
182