1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.io;
19
20import dalvik.system.CloseGuard;
21
22import java.nio.NioUtils;
23import java.nio.channels.FileChannel;
24import java.util.Arrays;
25import libcore.io.ErrnoException;
26import libcore.io.IoBridge;
27import libcore.io.IoUtils;
28import libcore.io.Libcore;
29import libcore.io.Streams;
30import static libcore.io.OsConstants.*;
31
32/**
33 * An input stream that reads bytes from a file.
34 * <pre>   {@code
35 *   File file = ...
36 *   InputStream in = null;
37 *   try {
38 *     in = new BufferedInputStream(new FileInputStream(file));
39 *     ...
40 *   } finally {
41 *     if (in != null) {
42 *       in.close();
43 *     }
44 *   }
45 * }</pre>
46 *
47 * <p>This stream is <strong>not buffered</strong>. Most callers should wrap
48 * this stream with a {@link BufferedInputStream}.
49 *
50 * <p>Use {@link FileReader} to read characters, as opposed to bytes, from a
51 * file.
52 *
53 * @see BufferedInputStream
54 * @see FileOutputStream
55 */
56public class FileInputStream extends InputStream implements Closeable {
57
58    private FileDescriptor fd;
59    private final boolean shouldClose;
60
61    /** The unique file channel. Lazily initialized because it's rarely needed. */
62    private FileChannel channel;
63
64    private final CloseGuard guard = CloseGuard.get();
65
66    /**
67     * Constructs a new {@code FileInputStream} that reads from {@code file}.
68     *
69     * @param file
70     *            the file from which this stream reads.
71     * @throws FileNotFoundException
72     *             if {@code file} does not exist.
73     */
74    public FileInputStream(File file) throws FileNotFoundException {
75        if (file == null) {
76            throw new NullPointerException("file == null");
77        }
78        this.fd = IoBridge.open(file.getAbsolutePath(), O_RDONLY);
79        this.shouldClose = true;
80        guard.open("close");
81    }
82
83    /**
84     * Constructs a new {@code FileInputStream} that reads from {@code fd}.
85     *
86     * @param fd
87     *            the FileDescriptor from which this stream reads.
88     * @throws NullPointerException
89     *             if {@code fd} is {@code null}.
90     */
91    public FileInputStream(FileDescriptor fd) {
92        if (fd == null) {
93            throw new NullPointerException("fd == null");
94        }
95        this.fd = fd;
96        this.shouldClose = false;
97        // Note that we do not call guard.open here because the
98        // FileDescriptor is not owned by the stream.
99    }
100
101    /**
102     * Equivalent to {@code new FileInputStream(new File(path))}.
103     */
104    public FileInputStream(String path) throws FileNotFoundException {
105        this(new File(path));
106    }
107
108    @Override
109    public int available() throws IOException {
110        return IoBridge.available(fd);
111    }
112
113    @Override
114    public void close() throws IOException {
115        guard.close();
116        synchronized (this) {
117            if (channel != null) {
118                channel.close();
119            }
120            if (shouldClose) {
121                IoUtils.close(fd);
122            } else {
123                // An owned fd has been invalidated by IoUtils.close, but
124                // we need to explicitly stop using an unowned fd (http://b/4361076).
125                fd = new FileDescriptor();
126            }
127        }
128    }
129
130    /**
131     * Ensures that all resources for this stream are released when it is about
132     * to be garbage collected.
133     *
134     * @throws IOException
135     *             if an error occurs attempting to finalize this stream.
136     */
137    @Override protected void finalize() throws IOException {
138        try {
139            if (guard != null) {
140                guard.warnIfOpen();
141            }
142            close();
143        } finally {
144            try {
145                super.finalize();
146            } catch (Throwable t) {
147                // for consistency with the RI, we must override Object.finalize() to
148                // remove the 'throws Throwable' clause.
149                throw new AssertionError(t);
150            }
151        }
152    }
153
154    /**
155     * Returns a read-only {@link FileChannel} that shares its position with
156     * this stream.
157     */
158    public FileChannel getChannel() {
159        synchronized (this) {
160            if (channel == null) {
161                channel = NioUtils.newFileChannel(this, fd, O_RDONLY);
162            }
163            return channel;
164        }
165    }
166
167    /**
168     * Returns the underlying file descriptor.
169     */
170    public final FileDescriptor getFD() throws IOException {
171        return fd;
172    }
173
174    @Override public int read() throws IOException {
175        return Streams.readSingleByte(this);
176    }
177
178    @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
179        return IoBridge.read(fd, buffer, byteOffset, byteCount);
180    }
181
182    @Override
183    public long skip(long byteCount) throws IOException {
184        if (byteCount < 0) {
185            throw new IOException("byteCount < 0: " + byteCount);
186        }
187        try {
188            // Try lseek(2). That returns the new offset, but we'll throw an
189            // exception if it couldn't perform exactly the seek we asked for.
190            Libcore.os.lseek(fd, byteCount, SEEK_CUR);
191            return byteCount;
192        } catch (ErrnoException errnoException) {
193            if (errnoException.errno == ESPIPE) {
194                // You can't seek on a pipe, so fall back to the superclass' implementation.
195                return super.skip(byteCount);
196            }
197            throw errnoException.rethrowAsIOException();
198        }
199    }
200}
201