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 17 package android.content.res; 18 19 import android.os.MemoryFile; 20 import android.os.Parcel; 21 import android.os.ParcelFileDescriptor; 22 import android.os.Parcelable; 23 24 import java.io.FileDescriptor; 25 import java.io.FileInputStream; 26 import java.io.FileOutputStream; 27 import java.io.IOException; 28 import java.io.InputStream; 29 import 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 */ 36 public 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 */ AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length)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 */ getParcelFileDescriptor()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 */ getFileDescriptor()80 public FileDescriptor getFileDescriptor() { 81 return mFd.getFileDescriptor(); 82 } 83 84 /** 85 * Returns the byte offset where this asset entry's data starts. 86 */ getStartOffset()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 */ getLength()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 */ getDeclaredLength()118 public long getDeclaredLength() { 119 return mLength; 120 } 121 122 /** 123 * Convenience for calling <code>getParcelFileDescriptor().close()</code>. 124 */ close()125 public void close() throws IOException { 126 mFd.close(); 127 } 128 129 /** 130 * Checks whether this file descriptor is for a memory file. 131 */ isMemoryFile()132 private boolean isMemoryFile() throws IOException { 133 try { 134 return MemoryFile.isMemoryFile(mFd.getFileDescriptor()); 135 } catch (IOException e) { 136 return false; 137 } 138 } 139 140 /** 141 * Create and return a new auto-close input stream for this asset. This 142 * will either return a full asset {@link AutoCloseInputStream}, or 143 * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream 144 * ParcelFileDescriptor.AutoCloseInputStream} depending on whether the 145 * the object represents a complete file or sub-section of a file. You 146 * should only call this once for a particular asset. 147 */ createInputStream()148 public FileInputStream createInputStream() throws IOException { 149 if (isMemoryFile()) { 150 if (mLength > Integer.MAX_VALUE) { 151 throw new IOException("File length too large for a memory file: " + mLength); 152 } 153 return new AutoCloseMemoryFileInputStream(mFd, (int)mLength); 154 } 155 if (mLength < 0) { 156 return new ParcelFileDescriptor.AutoCloseInputStream(mFd); 157 } 158 return new AutoCloseInputStream(this); 159 } 160 161 /** 162 * Create and return a new auto-close output stream for this asset. This 163 * will either return a full asset {@link AutoCloseOutputStream}, or 164 * an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream 165 * ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the 166 * the object represents a complete file or sub-section of a file. You 167 * should only call this once for a particular asset. 168 */ createOutputStream()169 public FileOutputStream createOutputStream() throws IOException { 170 if (mLength < 0) { 171 return new ParcelFileDescriptor.AutoCloseOutputStream(mFd); 172 } 173 return new AutoCloseOutputStream(this); 174 } 175 176 @Override toString()177 public String toString() { 178 return "{AssetFileDescriptor: " + mFd 179 + " start=" + mStartOffset + " len=" + mLength + "}"; 180 } 181 182 /** 183 * An InputStream you can create on a ParcelFileDescriptor, which will 184 * take care of calling {@link ParcelFileDescriptor#close 185 * ParcelFileDescritor.close()} for you when the stream is closed. 186 */ 187 public static class AutoCloseInputStream 188 extends ParcelFileDescriptor.AutoCloseInputStream { 189 private long mRemaining; 190 AutoCloseInputStream(AssetFileDescriptor fd)191 public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException { 192 super(fd.getParcelFileDescriptor()); 193 super.skip(fd.getStartOffset()); 194 mRemaining = (int)fd.getLength(); 195 } 196 197 @Override available()198 public int available() throws IOException { 199 return mRemaining >= 0 200 ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff) 201 : super.available(); 202 } 203 204 @Override read()205 public int read() throws IOException { 206 if (mRemaining >= 0) { 207 if (mRemaining == 0) return -1; 208 int res = super.read(); 209 if (res >= 0) mRemaining--; 210 return res; 211 } 212 213 return super.read(); 214 } 215 216 @Override read(byte[] buffer, int offset, int count)217 public int read(byte[] buffer, int offset, int count) throws IOException { 218 if (mRemaining >= 0) { 219 if (mRemaining == 0) return -1; 220 if (count > mRemaining) count = (int)mRemaining; 221 int res = super.read(buffer, offset, count); 222 if (res >= 0) mRemaining -= res; 223 return res; 224 } 225 226 return super.read(buffer, offset, count); 227 } 228 229 @Override read(byte[] buffer)230 public int read(byte[] buffer) throws IOException { 231 if (mRemaining >= 0) { 232 if (mRemaining == 0) return -1; 233 int count = buffer.length; 234 if (count > mRemaining) count = (int)mRemaining; 235 int res = super.read(buffer, 0, count); 236 if (res >= 0) mRemaining -= res; 237 return res; 238 } 239 240 return super.read(buffer); 241 } 242 243 @Override skip(long count)244 public long skip(long count) throws IOException { 245 if (mRemaining >= 0) { 246 if (mRemaining == 0) return -1; 247 if (count > mRemaining) count = mRemaining; 248 long res = super.skip(count); 249 if (res >= 0) mRemaining -= res; 250 return res; 251 } 252 253 // TODO Auto-generated method stub 254 return super.skip(count); 255 } 256 257 @Override mark(int readlimit)258 public void mark(int readlimit) { 259 if (mRemaining >= 0) { 260 // Not supported. 261 return; 262 } 263 super.mark(readlimit); 264 } 265 266 @Override markSupported()267 public boolean markSupported() { 268 if (mRemaining >= 0) { 269 return false; 270 } 271 return super.markSupported(); 272 } 273 274 @Override reset()275 public synchronized void reset() throws IOException { 276 if (mRemaining >= 0) { 277 // Not supported. 278 return; 279 } 280 super.reset(); 281 } 282 } 283 284 /** 285 * An input stream that reads from a MemoryFile and closes it when the stream is closed. 286 * This extends FileInputStream just because {@link #createInputStream} returns 287 * a FileInputStream. All the FileInputStream methods are 288 * overridden to use the MemoryFile instead. 289 */ 290 private static class AutoCloseMemoryFileInputStream extends FileInputStream { 291 private ParcelFileDescriptor mParcelFd; 292 private MemoryFile mFile; 293 private InputStream mStream; 294 AutoCloseMemoryFileInputStream(ParcelFileDescriptor fd, int length)295 public AutoCloseMemoryFileInputStream(ParcelFileDescriptor fd, int length) 296 throws IOException { 297 super(fd.getFileDescriptor()); 298 mParcelFd = fd; 299 mFile = new MemoryFile(fd.getFileDescriptor(), length, "r"); 300 mStream = mFile.getInputStream(); 301 } 302 303 @Override available()304 public int available() throws IOException { 305 return mStream.available(); 306 } 307 308 @Override close()309 public void close() throws IOException { 310 mParcelFd.close(); // must close ParcelFileDescriptor, not just the file descriptor, 311 // since it could be a subclass of ParcelFileDescriptor. 312 // E.g. ContentResolver.ParcelFileDescriptorInner.close() releases 313 // a content provider 314 mFile.close(); // to unmap the memory file from the address space. 315 mStream.close(); // doesn't actually do anything 316 } 317 318 @Override getChannel()319 public FileChannel getChannel() { 320 return null; 321 } 322 323 @Override read()324 public int read() throws IOException { 325 return mStream.read(); 326 } 327 328 @Override read(byte[] buffer, int offset, int count)329 public int read(byte[] buffer, int offset, int count) throws IOException { 330 return mStream.read(buffer, offset, count); 331 } 332 333 @Override read(byte[] buffer)334 public int read(byte[] buffer) throws IOException { 335 return mStream.read(buffer); 336 } 337 338 @Override skip(long count)339 public long skip(long count) throws IOException { 340 return mStream.skip(count); 341 } 342 } 343 344 /** 345 * An OutputStream you can create on a ParcelFileDescriptor, which will 346 * take care of calling {@link ParcelFileDescriptor#close 347 * ParcelFileDescritor.close()} for you when the stream is closed. 348 */ 349 public static class AutoCloseOutputStream 350 extends ParcelFileDescriptor.AutoCloseOutputStream { 351 private long mRemaining; 352 AutoCloseOutputStream(AssetFileDescriptor fd)353 public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException { 354 super(fd.getParcelFileDescriptor()); 355 if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) { 356 throw new IOException("Unable to seek"); 357 } 358 mRemaining = (int)fd.getLength(); 359 } 360 361 @Override write(byte[] buffer, int offset, int count)362 public void write(byte[] buffer, int offset, int count) throws IOException { 363 if (mRemaining >= 0) { 364 if (mRemaining == 0) return; 365 if (count > mRemaining) count = (int)mRemaining; 366 super.write(buffer, offset, count); 367 mRemaining -= count; 368 return; 369 } 370 371 super.write(buffer, offset, count); 372 } 373 374 @Override write(byte[] buffer)375 public void write(byte[] buffer) throws IOException { 376 if (mRemaining >= 0) { 377 if (mRemaining == 0) return; 378 int count = buffer.length; 379 if (count > mRemaining) count = (int)mRemaining; 380 super.write(buffer); 381 mRemaining -= count; 382 return; 383 } 384 385 super.write(buffer); 386 } 387 388 @Override write(int oneByte)389 public void write(int oneByte) throws IOException { 390 if (mRemaining >= 0) { 391 if (mRemaining == 0) return; 392 super.write(oneByte); 393 mRemaining--; 394 return; 395 } 396 397 super.write(oneByte); 398 } 399 } 400 401 402 /* Parcelable interface */ describeContents()403 public int describeContents() { 404 return mFd.describeContents(); 405 } 406 writeToParcel(Parcel out, int flags)407 public void writeToParcel(Parcel out, int flags) { 408 mFd.writeToParcel(out, flags); 409 out.writeLong(mStartOffset); 410 out.writeLong(mLength); 411 } 412 AssetFileDescriptor(Parcel src)413 AssetFileDescriptor(Parcel src) { 414 mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src); 415 mStartOffset = src.readLong(); 416 mLength = src.readLong(); 417 } 418 419 public static final Parcelable.Creator<AssetFileDescriptor> CREATOR 420 = new Parcelable.Creator<AssetFileDescriptor>() { 421 public AssetFileDescriptor createFromParcel(Parcel in) { 422 return new AssetFileDescriptor(in); 423 } 424 public AssetFileDescriptor[] newArray(int size) { 425 return new AssetFileDescriptor[size]; 426 } 427 }; 428 429 /** 430 * Creates an AssetFileDescriptor from a memory file. 431 * 432 * @hide 433 */ fromMemoryFile(MemoryFile memoryFile)434 public static AssetFileDescriptor fromMemoryFile(MemoryFile memoryFile) 435 throws IOException { 436 ParcelFileDescriptor fd = memoryFile.getParcelFileDescriptor(); 437 return new AssetFileDescriptor(fd, 0, memoryFile.length()); 438 } 439 440 } 441