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