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