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