AssetFileDescriptor.java revision a006b47298539d89dc7a06b54c070cb3e986352a
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; 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 { 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 (length < 0 && startOffset != 0) { 57 throw new IllegalArgumentException( 58 "startOffset must be 0 when using UNKNOWN_LENGTH"); 59 } 60 mFd = fd; 61 mStartOffset = startOffset; 62 mLength = length; 63 } 64 65 /** 66 * The AssetFileDescriptor contains its own ParcelFileDescriptor, which 67 * in addition to the normal FileDescriptor object also allows you to close 68 * the descriptor when you are done with it. 69 */ 70 public ParcelFileDescriptor getParcelFileDescriptor() { 71 return mFd; 72 } 73 74 /** 75 * Returns the FileDescriptor that can be used to read the data in the 76 * file. 77 */ 78 public FileDescriptor getFileDescriptor() { 79 return mFd.getFileDescriptor(); 80 } 81 82 /** 83 * Returns the byte offset where this asset entry's data starts. 84 */ 85 public long getStartOffset() { 86 return mStartOffset; 87 } 88 89 /** 90 * Returns the total number of bytes of this asset entry's data. May be 91 * {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file. 92 * If the AssetFileDescriptor was constructed with {@link #UNKNOWN_LENGTH}, 93 * this will use {@link ParcelFileDescriptor#getStatSize() 94 * ParcelFileDescriptor.getStatSize()} to find the total size of the file, 95 * returning that number if found or {@link #UNKNOWN_LENGTH} if it could 96 * not be determined. 97 * 98 * @see #getDeclaredLength() 99 */ 100 public long getLength() { 101 if (mLength >= 0) { 102 return mLength; 103 } 104 long len = mFd.getStatSize(); 105 return len >= 0 ? len : UNKNOWN_LENGTH; 106 } 107 108 /** 109 * Return the actual number of bytes that were declared when the 110 * AssetFileDescriptor was constructed. Will be 111 * {@link #UNKNOWN_LENGTH} if the length was not declared, meaning data 112 * should be read to the end of the file. 113 * 114 * @see #getDeclaredLength() 115 */ 116 public long getDeclaredLength() { 117 return mLength; 118 } 119 120 /** 121 * Convenience for calling <code>getParcelFileDescriptor().close()</code>. 122 */ 123 public void close() throws IOException { 124 mFd.close(); 125 } 126 127 /** 128 * Create and return a new auto-close input stream for this asset. This 129 * will either return a full asset {@link AutoCloseInputStream}, or 130 * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream 131 * ParcelFileDescriptor.AutoCloseInputStream} depending on whether the 132 * the object represents a complete file or sub-section of a file. You 133 * should only call this once for a particular asset. 134 */ 135 public FileInputStream createInputStream() throws IOException { 136 if (mLength < 0) { 137 return new ParcelFileDescriptor.AutoCloseInputStream(mFd); 138 } 139 return new AutoCloseInputStream(this); 140 } 141 142 /** 143 * Create and return a new auto-close output stream for this asset. This 144 * will either return a full asset {@link AutoCloseOutputStream}, or 145 * an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream 146 * ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the 147 * the object represents a complete file or sub-section of a file. You 148 * should only call this once for a particular asset. 149 */ 150 public FileOutputStream createOutputStream() throws IOException { 151 if (mLength < 0) { 152 return new ParcelFileDescriptor.AutoCloseOutputStream(mFd); 153 } 154 return new AutoCloseOutputStream(this); 155 } 156 157 @Override 158 public String toString() { 159 return "{AssetFileDescriptor: " + mFd 160 + " start=" + mStartOffset + " len=" + mLength + "}"; 161 } 162 163 /** 164 * An InputStream you can create on a ParcelFileDescriptor, which will 165 * take care of calling {@link ParcelFileDescriptor#close 166 * ParcelFileDescritor.close()} for you when the stream is closed. 167 */ 168 public static class AutoCloseInputStream 169 extends ParcelFileDescriptor.AutoCloseInputStream { 170 private long mRemaining; 171 172 public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException { 173 super(fd.getParcelFileDescriptor()); 174 super.skip(fd.getStartOffset()); 175 mRemaining = (int)fd.getLength(); 176 } 177 178 @Override 179 public int available() throws IOException { 180 return mRemaining >= 0 181 ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff) 182 : super.available(); 183 } 184 185 @Override 186 public int read() throws IOException { 187 if (mRemaining >= 0) { 188 if (mRemaining == 0) return -1; 189 int res = super.read(); 190 if (res >= 0) mRemaining--; 191 return res; 192 } 193 194 return super.read(); 195 } 196 197 @Override 198 public int read(byte[] buffer, int offset, int count) throws IOException { 199 if (mRemaining >= 0) { 200 if (mRemaining == 0) return -1; 201 if (count > mRemaining) count = (int)mRemaining; 202 int res = super.read(buffer, offset, count); 203 if (res >= 0) mRemaining -= res; 204 return res; 205 } 206 207 return super.read(buffer, offset, count); 208 } 209 210 @Override 211 public int read(byte[] buffer) throws IOException { 212 if (mRemaining >= 0) { 213 if (mRemaining == 0) return -1; 214 int count = buffer.length; 215 if (count > mRemaining) count = (int)mRemaining; 216 int res = super.read(buffer, 0, count); 217 if (res >= 0) mRemaining -= res; 218 return res; 219 } 220 221 return super.read(buffer); 222 } 223 224 @Override 225 public long skip(long count) throws IOException { 226 if (mRemaining >= 0) { 227 if (mRemaining == 0) return -1; 228 if (count > mRemaining) count = mRemaining; 229 long res = super.skip(count); 230 if (res >= 0) mRemaining -= res; 231 return res; 232 } 233 234 // TODO Auto-generated method stub 235 return super.skip(count); 236 } 237 238 @Override 239 public void mark(int readlimit) { 240 if (mRemaining >= 0) { 241 // Not supported. 242 return; 243 } 244 super.mark(readlimit); 245 } 246 247 @Override 248 public boolean markSupported() { 249 if (mRemaining >= 0) { 250 return false; 251 } 252 return super.markSupported(); 253 } 254 255 @Override 256 public synchronized void reset() throws IOException { 257 if (mRemaining >= 0) { 258 // Not supported. 259 return; 260 } 261 super.reset(); 262 } 263 } 264 265 /** 266 * An OutputStream you can create on a ParcelFileDescriptor, which will 267 * take care of calling {@link ParcelFileDescriptor#close 268 * ParcelFileDescritor.close()} for you when the stream is closed. 269 */ 270 public static class AutoCloseOutputStream 271 extends ParcelFileDescriptor.AutoCloseOutputStream { 272 private long mRemaining; 273 274 public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException { 275 super(fd.getParcelFileDescriptor()); 276 if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) { 277 throw new IOException("Unable to seek"); 278 } 279 mRemaining = (int)fd.getLength(); 280 } 281 282 @Override 283 public void write(byte[] buffer, int offset, int count) throws IOException { 284 if (mRemaining >= 0) { 285 if (mRemaining == 0) return; 286 if (count > mRemaining) count = (int)mRemaining; 287 super.write(buffer, offset, count); 288 mRemaining -= count; 289 return; 290 } 291 292 super.write(buffer, offset, count); 293 } 294 295 @Override 296 public void write(byte[] buffer) throws IOException { 297 if (mRemaining >= 0) { 298 if (mRemaining == 0) return; 299 int count = buffer.length; 300 if (count > mRemaining) count = (int)mRemaining; 301 super.write(buffer); 302 mRemaining -= count; 303 return; 304 } 305 306 super.write(buffer); 307 } 308 309 @Override 310 public void write(int oneByte) throws IOException { 311 if (mRemaining >= 0) { 312 if (mRemaining == 0) return; 313 super.write(oneByte); 314 mRemaining--; 315 return; 316 } 317 318 super.write(oneByte); 319 } 320 } 321 322 323 /* Parcelable interface */ 324 public int describeContents() { 325 return mFd.describeContents(); 326 } 327 328 public void writeToParcel(Parcel out, int flags) { 329 mFd.writeToParcel(out, flags); 330 out.writeLong(mStartOffset); 331 out.writeLong(mLength); 332 } 333 334 AssetFileDescriptor(Parcel src) { 335 mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src); 336 mStartOffset = src.readLong(); 337 mLength = src.readLong(); 338 } 339 340 public static final Parcelable.Creator<AssetFileDescriptor> CREATOR 341 = new Parcelable.Creator<AssetFileDescriptor>() { 342 public AssetFileDescriptor createFromParcel(Parcel in) { 343 return new AssetFileDescriptor(in); 344 } 345 public AssetFileDescriptor[] newArray(int size) { 346 return new AssetFileDescriptor[size]; 347 } 348 }; 349 350} 351