AssetFileDescriptor.java revision cf4550c3198d6b3d92cdc52707fe70d7cc0caa9f
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.MemoryFile; 20import android.os.Parcel; 21import android.os.ParcelFileDescriptor; 22import android.os.Parcelable; 23 24import java.io.FileDescriptor; 25import java.io.FileInputStream; 26import java.io.FileOutputStream; 27import java.io.IOException; 28import java.io.InputStream; 29import java.nio.channels.FileChannel; 30 31/** 32 * File descriptor of an entry in the AssetManager. This provides your own 33 * opened FileDescriptor that can be used to read the data, as well as the 34 * offset and length of that entry's data in the file. 35 */ 36public class AssetFileDescriptor implements Parcelable { 37 /** 38 * Length used with {@link #AssetFileDescriptor(ParcelFileDescriptor, long, long)} 39 * and {@link #getDeclaredLength} when a length has not been declared. This means 40 * the data extends to the end of the file. 41 */ 42 public static final long UNKNOWN_LENGTH = -1; 43 44 private final ParcelFileDescriptor mFd; 45 private final long mStartOffset; 46 private final long mLength; 47 48 /** 49 * Create a new AssetFileDescriptor from the given values. 50 * @param fd The underlying file descriptor. 51 * @param startOffset The location within the file that the asset starts. 52 * This must be 0 if length is UNKNOWN_LENGTH. 53 * @param length The number of bytes of the asset, or 54 * {@link #UNKNOWN_LENGTH if it extends to the end of the file. 55 */ 56 public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, 57 long length) { 58 if (length < 0 && startOffset != 0) { 59 throw new IllegalArgumentException( 60 "startOffset must be 0 when using UNKNOWN_LENGTH"); 61 } 62 mFd = fd; 63 mStartOffset = startOffset; 64 mLength = length; 65 } 66 67 /** 68 * The AssetFileDescriptor contains its own ParcelFileDescriptor, which 69 * in addition to the normal FileDescriptor object also allows you to close 70 * the descriptor when you are done with it. 71 */ 72 public ParcelFileDescriptor getParcelFileDescriptor() { 73 return mFd; 74 } 75 76 /** 77 * Returns the FileDescriptor that can be used to read the data in the 78 * file. 79 */ 80 public FileDescriptor getFileDescriptor() { 81 return mFd.getFileDescriptor(); 82 } 83 84 /** 85 * Returns the byte offset where this asset entry's data starts. 86 */ 87 public long getStartOffset() { 88 return mStartOffset; 89 } 90 91 /** 92 * Returns the total number of bytes of this asset entry's data. May be 93 * {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file. 94 * If the AssetFileDescriptor was constructed with {@link #UNKNOWN_LENGTH}, 95 * this will use {@link ParcelFileDescriptor#getStatSize() 96 * ParcelFileDescriptor.getStatSize()} to find the total size of the file, 97 * returning that number if found or {@link #UNKNOWN_LENGTH} if it could 98 * not be determined. 99 * 100 * @see #getDeclaredLength() 101 */ 102 public long getLength() { 103 if (mLength >= 0) { 104 return mLength; 105 } 106 long len = mFd.getStatSize(); 107 return len >= 0 ? len : UNKNOWN_LENGTH; 108 } 109 110 /** 111 * Return the actual number of bytes that were declared when the 112 * AssetFileDescriptor was constructed. Will be 113 * {@link #UNKNOWN_LENGTH} if the length was not declared, meaning data 114 * should be read to the end of the file. 115 * 116 * @see #getDeclaredLength() 117 */ 118 public long getDeclaredLength() { 119 return mLength; 120 } 121 122 /** 123 * Convenience for calling <code>getParcelFileDescriptor().close()</code>. 124 */ 125 public void close() throws IOException { 126 mFd.close(); 127 } 128 129 /** 130 * Checks whether this file descriptor is for a memory file. 131 */ 132 private boolean isMemoryFile() throws IOException { 133 return MemoryFile.isMemoryFile(mFd.getFileDescriptor()); 134 } 135 136 /** 137 * Create and return a new auto-close input stream for this asset. This 138 * will either return a full asset {@link AutoCloseInputStream}, or 139 * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream 140 * ParcelFileDescriptor.AutoCloseInputStream} depending on whether the 141 * the object represents a complete file or sub-section of a file. You 142 * should only call this once for a particular asset. 143 */ 144 public FileInputStream createInputStream() throws IOException { 145 if (isMemoryFile()) { 146 if (mLength > Integer.MAX_VALUE) { 147 throw new IOException("File length too large for a memory file: " + mLength); 148 } 149 return new AutoCloseMemoryFileInputStream(mFd, (int)mLength); 150 } 151 if (mLength < 0) { 152 return new ParcelFileDescriptor.AutoCloseInputStream(mFd); 153 } 154 return new AutoCloseInputStream(this); 155 } 156 157 /** 158 * Create and return a new auto-close output stream for this asset. This 159 * will either return a full asset {@link AutoCloseOutputStream}, or 160 * an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream 161 * ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the 162 * the object represents a complete file or sub-section of a file. You 163 * should only call this once for a particular asset. 164 */ 165 public FileOutputStream createOutputStream() throws IOException { 166 if (mLength < 0) { 167 return new ParcelFileDescriptor.AutoCloseOutputStream(mFd); 168 } 169 return new AutoCloseOutputStream(this); 170 } 171 172 @Override 173 public String toString() { 174 return "{AssetFileDescriptor: " + mFd 175 + " start=" + mStartOffset + " len=" + mLength + "}"; 176 } 177 178 /** 179 * An InputStream you can create on a ParcelFileDescriptor, which will 180 * take care of calling {@link ParcelFileDescriptor#close 181 * ParcelFileDescritor.close()} for you when the stream is closed. 182 */ 183 public static class AutoCloseInputStream 184 extends ParcelFileDescriptor.AutoCloseInputStream { 185 private long mRemaining; 186 187 public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException { 188 super(fd.getParcelFileDescriptor()); 189 super.skip(fd.getStartOffset()); 190 mRemaining = (int)fd.getLength(); 191 } 192 193 @Override 194 public int available() throws IOException { 195 return mRemaining >= 0 196 ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff) 197 : super.available(); 198 } 199 200 @Override 201 public int read() throws IOException { 202 if (mRemaining >= 0) { 203 if (mRemaining == 0) return -1; 204 int res = super.read(); 205 if (res >= 0) mRemaining--; 206 return res; 207 } 208 209 return super.read(); 210 } 211 212 @Override 213 public int read(byte[] buffer, int offset, int count) throws IOException { 214 if (mRemaining >= 0) { 215 if (mRemaining == 0) return -1; 216 if (count > mRemaining) count = (int)mRemaining; 217 int res = super.read(buffer, offset, count); 218 if (res >= 0) mRemaining -= res; 219 return res; 220 } 221 222 return super.read(buffer, offset, count); 223 } 224 225 @Override 226 public int read(byte[] buffer) throws IOException { 227 if (mRemaining >= 0) { 228 if (mRemaining == 0) return -1; 229 int count = buffer.length; 230 if (count > mRemaining) count = (int)mRemaining; 231 int res = super.read(buffer, 0, count); 232 if (res >= 0) mRemaining -= res; 233 return res; 234 } 235 236 return super.read(buffer); 237 } 238 239 @Override 240 public long skip(long count) throws IOException { 241 if (mRemaining >= 0) { 242 if (mRemaining == 0) return -1; 243 if (count > mRemaining) count = mRemaining; 244 long res = super.skip(count); 245 if (res >= 0) mRemaining -= res; 246 return res; 247 } 248 249 // TODO Auto-generated method stub 250 return super.skip(count); 251 } 252 253 @Override 254 public void mark(int readlimit) { 255 if (mRemaining >= 0) { 256 // Not supported. 257 return; 258 } 259 super.mark(readlimit); 260 } 261 262 @Override 263 public boolean markSupported() { 264 if (mRemaining >= 0) { 265 return false; 266 } 267 return super.markSupported(); 268 } 269 270 @Override 271 public synchronized void reset() throws IOException { 272 if (mRemaining >= 0) { 273 // Not supported. 274 return; 275 } 276 super.reset(); 277 } 278 } 279 280 /** 281 * An input stream that reads from a MemoryFile and closes it when the stream is closed. 282 * This extends FileInputStream just because {@link #createInputStream} returns 283 * a FileInputStream. All the FileInputStream methods are 284 * overridden to use the MemoryFile instead. 285 */ 286 private static class AutoCloseMemoryFileInputStream extends FileInputStream { 287 private ParcelFileDescriptor mParcelFd; 288 private MemoryFile mFile; 289 private InputStream mStream; 290 291 public AutoCloseMemoryFileInputStream(ParcelFileDescriptor fd, int length) 292 throws IOException { 293 super(fd.getFileDescriptor()); 294 mParcelFd = fd; 295 mFile = new MemoryFile(fd.getFileDescriptor(), length, "r"); 296 mStream = mFile.getInputStream(); 297 } 298 299 @Override 300 public int available() throws IOException { 301 return mStream.available(); 302 } 303 304 @Override 305 public void close() throws IOException { 306 mParcelFd.close(); // must close ParcelFileDescriptor, not just the file descriptor, 307 // since it could be a subclass of ParcelFileDescriptor. 308 // E.g. ContentResolver.ParcelFileDescriptorInner.close() releases 309 // a content provider 310 mFile.close(); // to unmap the memory file from the address space. 311 mStream.close(); // doesn't actually do anything 312 } 313 314 @Override 315 public FileChannel getChannel() { 316 return null; 317 } 318 319 @Override 320 public int read() throws IOException { 321 return mStream.read(); 322 } 323 324 @Override 325 public int read(byte[] buffer, int offset, int count) throws IOException { 326 return mStream.read(buffer, offset, count); 327 } 328 329 @Override 330 public int read(byte[] buffer) throws IOException { 331 return mStream.read(buffer); 332 } 333 334 @Override 335 public long skip(long count) throws IOException { 336 return mStream.skip(count); 337 } 338 } 339 340 /** 341 * An OutputStream you can create on a ParcelFileDescriptor, which will 342 * take care of calling {@link ParcelFileDescriptor#close 343 * ParcelFileDescritor.close()} for you when the stream is closed. 344 */ 345 public static class AutoCloseOutputStream 346 extends ParcelFileDescriptor.AutoCloseOutputStream { 347 private long mRemaining; 348 349 public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException { 350 super(fd.getParcelFileDescriptor()); 351 if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) { 352 throw new IOException("Unable to seek"); 353 } 354 mRemaining = (int)fd.getLength(); 355 } 356 357 @Override 358 public void write(byte[] buffer, int offset, int count) throws IOException { 359 if (mRemaining >= 0) { 360 if (mRemaining == 0) return; 361 if (count > mRemaining) count = (int)mRemaining; 362 super.write(buffer, offset, count); 363 mRemaining -= count; 364 return; 365 } 366 367 super.write(buffer, offset, count); 368 } 369 370 @Override 371 public void write(byte[] buffer) throws IOException { 372 if (mRemaining >= 0) { 373 if (mRemaining == 0) return; 374 int count = buffer.length; 375 if (count > mRemaining) count = (int)mRemaining; 376 super.write(buffer); 377 mRemaining -= count; 378 return; 379 } 380 381 super.write(buffer); 382 } 383 384 @Override 385 public void write(int oneByte) throws IOException { 386 if (mRemaining >= 0) { 387 if (mRemaining == 0) return; 388 super.write(oneByte); 389 mRemaining--; 390 return; 391 } 392 393 super.write(oneByte); 394 } 395 } 396 397 398 /* Parcelable interface */ 399 public int describeContents() { 400 return mFd.describeContents(); 401 } 402 403 public void writeToParcel(Parcel out, int flags) { 404 mFd.writeToParcel(out, flags); 405 out.writeLong(mStartOffset); 406 out.writeLong(mLength); 407 } 408 409 AssetFileDescriptor(Parcel src) { 410 mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src); 411 mStartOffset = src.readLong(); 412 mLength = src.readLong(); 413 } 414 415 public static final Parcelable.Creator<AssetFileDescriptor> CREATOR 416 = new Parcelable.Creator<AssetFileDescriptor>() { 417 public AssetFileDescriptor createFromParcel(Parcel in) { 418 return new AssetFileDescriptor(in); 419 } 420 public AssetFileDescriptor[] newArray(int size) { 421 return new AssetFileDescriptor[size]; 422 } 423 }; 424 425 /** 426 * Creates an AssetFileDescriptor from a memory file. 427 * 428 * @hide 429 */ 430 public static AssetFileDescriptor fromMemoryFile(MemoryFile memoryFile) 431 throws IOException { 432 ParcelFileDescriptor fd = memoryFile.getParcelFileDescriptor(); 433 return new AssetFileDescriptor(fd, 0, memoryFile.length()); 434 } 435 436} 437