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.Parcel; 20 import android.os.ParcelFileDescriptor; 21 import android.os.Parcelable; 22 23 import java.io.FileDescriptor; 24 import java.io.FileInputStream; 25 import java.io.FileOutputStream; 26 import 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 */ 33 public 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 */ AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length)53 public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, 54 long length) { 55 if (fd == null) { 56 throw new IllegalArgumentException("fd must not be null"); 57 } 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 * Create and return a new auto-close input stream for this asset. This 131 * will either return a full asset {@link AutoCloseInputStream}, or 132 * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream 133 * ParcelFileDescriptor.AutoCloseInputStream} depending on whether the 134 * the object represents a complete file or sub-section of a file. You 135 * should only call this once for a particular asset. 136 */ createInputStream()137 public FileInputStream createInputStream() throws IOException { 138 if (mLength < 0) { 139 return new ParcelFileDescriptor.AutoCloseInputStream(mFd); 140 } 141 return new AutoCloseInputStream(this); 142 } 143 144 /** 145 * Create and return a new auto-close output stream for this asset. This 146 * will either return a full asset {@link AutoCloseOutputStream}, or 147 * an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream 148 * ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the 149 * the object represents a complete file or sub-section of a file. You 150 * should only call this once for a particular asset. 151 */ createOutputStream()152 public FileOutputStream createOutputStream() throws IOException { 153 if (mLength < 0) { 154 return new ParcelFileDescriptor.AutoCloseOutputStream(mFd); 155 } 156 return new AutoCloseOutputStream(this); 157 } 158 159 @Override toString()160 public String toString() { 161 return "{AssetFileDescriptor: " + mFd 162 + " start=" + mStartOffset + " len=" + mLength + "}"; 163 } 164 165 /** 166 * An InputStream you can create on a ParcelFileDescriptor, which will 167 * take care of calling {@link ParcelFileDescriptor#close 168 * ParcelFileDescritor.close()} for you when the stream is closed. 169 */ 170 public static class AutoCloseInputStream 171 extends ParcelFileDescriptor.AutoCloseInputStream { 172 private long mRemaining; 173 AutoCloseInputStream(AssetFileDescriptor fd)174 public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException { 175 super(fd.getParcelFileDescriptor()); 176 super.skip(fd.getStartOffset()); 177 mRemaining = (int)fd.getLength(); 178 } 179 180 @Override available()181 public int available() throws IOException { 182 return mRemaining >= 0 183 ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff) 184 : super.available(); 185 } 186 187 @Override read()188 public int read() throws IOException { 189 byte[] buffer = new byte[1]; 190 int result = read(buffer, 0, 1); 191 return result == -1 ? -1 : buffer[0] & 0xff; 192 } 193 194 @Override read(byte[] buffer, int offset, int count)195 public int read(byte[] buffer, int offset, int count) throws IOException { 196 if (mRemaining >= 0) { 197 if (mRemaining == 0) return -1; 198 if (count > mRemaining) count = (int)mRemaining; 199 int res = super.read(buffer, offset, count); 200 if (res >= 0) mRemaining -= res; 201 return res; 202 } 203 204 return super.read(buffer, offset, count); 205 } 206 207 @Override read(byte[] buffer)208 public int read(byte[] buffer) throws IOException { 209 return read(buffer, 0, buffer.length); 210 } 211 212 @Override skip(long count)213 public long skip(long count) throws IOException { 214 if (mRemaining >= 0) { 215 if (mRemaining == 0) return -1; 216 if (count > mRemaining) count = mRemaining; 217 long res = super.skip(count); 218 if (res >= 0) mRemaining -= res; 219 return res; 220 } 221 222 return super.skip(count); 223 } 224 225 @Override mark(int readlimit)226 public void mark(int readlimit) { 227 if (mRemaining >= 0) { 228 // Not supported. 229 return; 230 } 231 super.mark(readlimit); 232 } 233 234 @Override markSupported()235 public boolean markSupported() { 236 if (mRemaining >= 0) { 237 return false; 238 } 239 return super.markSupported(); 240 } 241 242 @Override reset()243 public synchronized void reset() throws IOException { 244 if (mRemaining >= 0) { 245 // Not supported. 246 return; 247 } 248 super.reset(); 249 } 250 } 251 252 /** 253 * An OutputStream you can create on a ParcelFileDescriptor, which will 254 * take care of calling {@link ParcelFileDescriptor#close 255 * ParcelFileDescritor.close()} for you when the stream is closed. 256 */ 257 public static class AutoCloseOutputStream 258 extends ParcelFileDescriptor.AutoCloseOutputStream { 259 private long mRemaining; 260 AutoCloseOutputStream(AssetFileDescriptor fd)261 public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException { 262 super(fd.getParcelFileDescriptor()); 263 if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) { 264 throw new IOException("Unable to seek"); 265 } 266 mRemaining = (int)fd.getLength(); 267 } 268 269 @Override write(byte[] buffer, int offset, int count)270 public void write(byte[] buffer, int offset, int count) throws IOException { 271 if (mRemaining >= 0) { 272 if (mRemaining == 0) return; 273 if (count > mRemaining) count = (int)mRemaining; 274 super.write(buffer, offset, count); 275 mRemaining -= count; 276 return; 277 } 278 279 super.write(buffer, offset, count); 280 } 281 282 @Override write(byte[] buffer)283 public void write(byte[] buffer) throws IOException { 284 if (mRemaining >= 0) { 285 if (mRemaining == 0) return; 286 int count = buffer.length; 287 if (count > mRemaining) count = (int)mRemaining; 288 super.write(buffer); 289 mRemaining -= count; 290 return; 291 } 292 293 super.write(buffer); 294 } 295 296 @Override write(int oneByte)297 public void write(int oneByte) throws IOException { 298 if (mRemaining >= 0) { 299 if (mRemaining == 0) return; 300 super.write(oneByte); 301 mRemaining--; 302 return; 303 } 304 305 super.write(oneByte); 306 } 307 } 308 309 310 /* Parcelable interface */ describeContents()311 public int describeContents() { 312 return mFd.describeContents(); 313 } 314 writeToParcel(Parcel out, int flags)315 public void writeToParcel(Parcel out, int flags) { 316 mFd.writeToParcel(out, flags); 317 out.writeLong(mStartOffset); 318 out.writeLong(mLength); 319 } 320 AssetFileDescriptor(Parcel src)321 AssetFileDescriptor(Parcel src) { 322 mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src); 323 mStartOffset = src.readLong(); 324 mLength = src.readLong(); 325 } 326 327 public static final Parcelable.Creator<AssetFileDescriptor> CREATOR 328 = new Parcelable.Creator<AssetFileDescriptor>() { 329 public AssetFileDescriptor createFromParcel(Parcel in) { 330 return new AssetFileDescriptor(in); 331 } 332 public AssetFileDescriptor[] newArray(int size) { 333 return new AssetFileDescriptor[size]; 334 } 335 }; 336 337 } 338