AssetFileDescriptor.java revision 32559e191fd2580393d77161a32bcaeaa49fbe5d
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.content.res; 18 19import android.os.Parcel; 20import android.os.ParcelFileDescriptor; 21import android.os.Parcelable; 22 23import java.io.Closeable; 24import java.io.FileDescriptor; 25import java.io.FileInputStream; 26import java.io.FileOutputStream; 27import java.io.IOException; 28 29/** 30 * File descriptor of an entry in the AssetManager. This provides your own 31 * opened FileDescriptor that can be used to read the data, as well as the 32 * offset and length of that entry's data in the file. 33 */ 34public class AssetFileDescriptor implements Parcelable, Closeable { 35 /** 36 * Length used with {@link #AssetFileDescriptor(ParcelFileDescriptor, long, long)} 37 * and {@link #getDeclaredLength} when a length has not been declared. This means 38 * the data extends to the end of the file. 39 */ 40 public static final long UNKNOWN_LENGTH = -1; 41 42 private final ParcelFileDescriptor mFd; 43 private final long mStartOffset; 44 private final long mLength; 45 46 /** 47 * Create a new AssetFileDescriptor from the given values. 48 * @param fd The underlying file descriptor. 49 * @param startOffset The location within the file that the asset starts. 50 * This must be 0 if length is UNKNOWN_LENGTH. 51 * @param length The number of bytes of the asset, or 52 * {@link #UNKNOWN_LENGTH} if it extends to the end of the file. 53 */ 54 public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, 55 long length) { 56 if (fd == null) { 57 throw new IllegalArgumentException("fd must not be null"); 58 } 59 if (length < 0 && startOffset != 0) { 60 throw new IllegalArgumentException( 61 "startOffset must be 0 when using UNKNOWN_LENGTH"); 62 } 63 mFd = fd; 64 mStartOffset = startOffset; 65 mLength = length; 66 } 67 68 /** 69 * The AssetFileDescriptor contains its own ParcelFileDescriptor, which 70 * in addition to the normal FileDescriptor object also allows you to close 71 * the descriptor when you are done with it. 72 */ 73 public ParcelFileDescriptor getParcelFileDescriptor() { 74 return mFd; 75 } 76 77 /** 78 * Returns the FileDescriptor that can be used to read the data in the 79 * file. 80 */ 81 public FileDescriptor getFileDescriptor() { 82 return mFd.getFileDescriptor(); 83 } 84 85 /** 86 * Returns the byte offset where this asset entry's data starts. 87 */ 88 public long getStartOffset() { 89 return mStartOffset; 90 } 91 92 /** 93 * Returns the total number of bytes of this asset entry's data. May be 94 * {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file. 95 * If the AssetFileDescriptor was constructed with {@link #UNKNOWN_LENGTH}, 96 * this will use {@link ParcelFileDescriptor#getStatSize() 97 * ParcelFileDescriptor.getStatSize()} to find the total size of the file, 98 * returning that number if found or {@link #UNKNOWN_LENGTH} if it could 99 * not be determined. 100 * 101 * @see #getDeclaredLength() 102 */ 103 public long getLength() { 104 if (mLength >= 0) { 105 return mLength; 106 } 107 long len = mFd.getStatSize(); 108 return len >= 0 ? len : UNKNOWN_LENGTH; 109 } 110 111 /** 112 * Return the actual number of bytes that were declared when the 113 * AssetFileDescriptor was constructed. Will be 114 * {@link #UNKNOWN_LENGTH} if the length was not declared, meaning data 115 * should be read to the end of the file. 116 * 117 * @see #getDeclaredLength() 118 */ 119 public long getDeclaredLength() { 120 return mLength; 121 } 122 123 /** 124 * Convenience for calling <code>getParcelFileDescriptor().close()</code>. 125 */ 126 @Override 127 public void close() throws IOException { 128 mFd.close(); 129 } 130 131 /** 132 * Create and return a new auto-close input stream for this asset. This 133 * will either return a full asset {@link AutoCloseInputStream}, or 134 * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream 135 * ParcelFileDescriptor.AutoCloseInputStream} depending on whether the 136 * the object represents a complete file or sub-section of a file. You 137 * should only call this once for a particular asset. 138 */ 139 public FileInputStream createInputStream() throws IOException { 140 if (mLength < 0) { 141 return new ParcelFileDescriptor.AutoCloseInputStream(mFd); 142 } 143 return new AutoCloseInputStream(this); 144 } 145 146 /** 147 * Create and return a new auto-close output stream for this asset. This 148 * will either return a full asset {@link AutoCloseOutputStream}, or 149 * an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream 150 * ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the 151 * the object represents a complete file or sub-section of a file. You 152 * should only call this once for a particular asset. 153 */ 154 public FileOutputStream createOutputStream() throws IOException { 155 if (mLength < 0) { 156 return new ParcelFileDescriptor.AutoCloseOutputStream(mFd); 157 } 158 return new AutoCloseOutputStream(this); 159 } 160 161 @Override 162 public String toString() { 163 return "{AssetFileDescriptor: " + mFd 164 + " start=" + mStartOffset + " len=" + mLength + "}"; 165 } 166 167 /** 168 * An InputStream you can create on a ParcelFileDescriptor, which will 169 * take care of calling {@link ParcelFileDescriptor#close 170 * ParcelFileDescritor.close()} for you when the stream is closed. 171 */ 172 public static class AutoCloseInputStream 173 extends ParcelFileDescriptor.AutoCloseInputStream { 174 private long mRemaining; 175 176 public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException { 177 super(fd.getParcelFileDescriptor()); 178 super.skip(fd.getStartOffset()); 179 mRemaining = (int)fd.getLength(); 180 } 181 182 @Override 183 public int available() throws IOException { 184 return mRemaining >= 0 185 ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff) 186 : super.available(); 187 } 188 189 @Override 190 public int read() throws IOException { 191 byte[] buffer = new byte[1]; 192 int result = read(buffer, 0, 1); 193 return result == -1 ? -1 : buffer[0] & 0xff; 194 } 195 196 @Override 197 public int read(byte[] buffer, int offset, int count) throws IOException { 198 if (mRemaining >= 0) { 199 if (mRemaining == 0) return -1; 200 if (count > mRemaining) count = (int)mRemaining; 201 int res = super.read(buffer, offset, count); 202 if (res >= 0) mRemaining -= res; 203 return res; 204 } 205 206 return super.read(buffer, offset, count); 207 } 208 209 @Override 210 public int read(byte[] buffer) throws IOException { 211 return read(buffer, 0, buffer.length); 212 } 213 214 @Override 215 public long skip(long count) throws IOException { 216 if (mRemaining >= 0) { 217 if (mRemaining == 0) return -1; 218 if (count > mRemaining) count = mRemaining; 219 long res = super.skip(count); 220 if (res >= 0) mRemaining -= res; 221 return res; 222 } 223 224 return super.skip(count); 225 } 226 227 @Override 228 public void mark(int readlimit) { 229 if (mRemaining >= 0) { 230 // Not supported. 231 return; 232 } 233 super.mark(readlimit); 234 } 235 236 @Override 237 public boolean markSupported() { 238 if (mRemaining >= 0) { 239 return false; 240 } 241 return super.markSupported(); 242 } 243 244 @Override 245 public synchronized void reset() throws IOException { 246 if (mRemaining >= 0) { 247 // Not supported. 248 return; 249 } 250 super.reset(); 251 } 252 } 253 254 /** 255 * An OutputStream you can create on a ParcelFileDescriptor, which will 256 * take care of calling {@link ParcelFileDescriptor#close 257 * ParcelFileDescritor.close()} for you when the stream is closed. 258 */ 259 public static class AutoCloseOutputStream 260 extends ParcelFileDescriptor.AutoCloseOutputStream { 261 private long mRemaining; 262 263 public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException { 264 super(fd.getParcelFileDescriptor()); 265 if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) { 266 throw new IOException("Unable to seek"); 267 } 268 mRemaining = (int)fd.getLength(); 269 } 270 271 @Override 272 public void write(byte[] buffer, int offset, int count) throws IOException { 273 if (mRemaining >= 0) { 274 if (mRemaining == 0) return; 275 if (count > mRemaining) count = (int)mRemaining; 276 super.write(buffer, offset, count); 277 mRemaining -= count; 278 return; 279 } 280 281 super.write(buffer, offset, count); 282 } 283 284 @Override 285 public void write(byte[] buffer) throws IOException { 286 if (mRemaining >= 0) { 287 if (mRemaining == 0) return; 288 int count = buffer.length; 289 if (count > mRemaining) count = (int)mRemaining; 290 super.write(buffer); 291 mRemaining -= count; 292 return; 293 } 294 295 super.write(buffer); 296 } 297 298 @Override 299 public void write(int oneByte) throws IOException { 300 if (mRemaining >= 0) { 301 if (mRemaining == 0) return; 302 super.write(oneByte); 303 mRemaining--; 304 return; 305 } 306 307 super.write(oneByte); 308 } 309 } 310 311 312 /* Parcelable interface */ 313 public int describeContents() { 314 return mFd.describeContents(); 315 } 316 317 public void writeToParcel(Parcel out, int flags) { 318 mFd.writeToParcel(out, flags); 319 out.writeLong(mStartOffset); 320 out.writeLong(mLength); 321 } 322 323 AssetFileDescriptor(Parcel src) { 324 mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src); 325 mStartOffset = src.readLong(); 326 mLength = src.readLong(); 327 } 328 329 public static final Parcelable.Creator<AssetFileDescriptor> CREATOR 330 = new Parcelable.Creator<AssetFileDescriptor>() { 331 public AssetFileDescriptor createFromParcel(Parcel in) { 332 return new AssetFileDescriptor(in); 333 } 334 public AssetFileDescriptor[] newArray(int size) { 335 return new AssetFileDescriptor[size]; 336 } 337 }; 338 339} 340