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