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 (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 */ getParcelFileDescriptor()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 */ getFileDescriptor()77 public FileDescriptor getFileDescriptor() { 78 return mFd.getFileDescriptor(); 79 } 80 81 /** 82 * Returns the byte offset where this asset entry's data starts. 83 */ getStartOffset()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 */ getLength()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 */ getDeclaredLength()115 public long getDeclaredLength() { 116 return mLength; 117 } 118 119 /** 120 * Convenience for calling <code>getParcelFileDescriptor().close()</code>. 121 */ close()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 */ createInputStream()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 */ createOutputStream()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 toString()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 AutoCloseInputStream(AssetFileDescriptor fd)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 available()178 public int available() throws IOException { 179 return mRemaining >= 0 180 ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff) 181 : super.available(); 182 } 183 184 @Override read()185 public int read() throws IOException { 186 byte[] buffer = new byte[1]; 187 int result = read(buffer, 0, 1); 188 return result == -1 ? -1 : buffer[0] & 0xff; 189 } 190 191 @Override read(byte[] buffer, int offset, int count)192 public int read(byte[] buffer, int offset, int count) throws IOException { 193 if (mRemaining >= 0) { 194 if (mRemaining == 0) return -1; 195 if (count > mRemaining) count = (int)mRemaining; 196 int res = super.read(buffer, offset, count); 197 if (res >= 0) mRemaining -= res; 198 return res; 199 } 200 201 return super.read(buffer, offset, count); 202 } 203 204 @Override read(byte[] buffer)205 public int read(byte[] buffer) throws IOException { 206 return read(buffer, 0, buffer.length); 207 } 208 209 @Override skip(long count)210 public long skip(long count) throws IOException { 211 if (mRemaining >= 0) { 212 if (mRemaining == 0) return -1; 213 if (count > mRemaining) count = mRemaining; 214 long res = super.skip(count); 215 if (res >= 0) mRemaining -= res; 216 return res; 217 } 218 219 return super.skip(count); 220 } 221 222 @Override mark(int readlimit)223 public void mark(int readlimit) { 224 if (mRemaining >= 0) { 225 // Not supported. 226 return; 227 } 228 super.mark(readlimit); 229 } 230 231 @Override markSupported()232 public boolean markSupported() { 233 if (mRemaining >= 0) { 234 return false; 235 } 236 return super.markSupported(); 237 } 238 239 @Override reset()240 public synchronized void reset() throws IOException { 241 if (mRemaining >= 0) { 242 // Not supported. 243 return; 244 } 245 super.reset(); 246 } 247 } 248 249 /** 250 * An OutputStream you can create on a ParcelFileDescriptor, which will 251 * take care of calling {@link ParcelFileDescriptor#close 252 * ParcelFileDescritor.close()} for you when the stream is closed. 253 */ 254 public static class AutoCloseOutputStream 255 extends ParcelFileDescriptor.AutoCloseOutputStream { 256 private long mRemaining; 257 AutoCloseOutputStream(AssetFileDescriptor fd)258 public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException { 259 super(fd.getParcelFileDescriptor()); 260 if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) { 261 throw new IOException("Unable to seek"); 262 } 263 mRemaining = (int)fd.getLength(); 264 } 265 266 @Override write(byte[] buffer, int offset, int count)267 public void write(byte[] buffer, int offset, int count) throws IOException { 268 if (mRemaining >= 0) { 269 if (mRemaining == 0) return; 270 if (count > mRemaining) count = (int)mRemaining; 271 super.write(buffer, offset, count); 272 mRemaining -= count; 273 return; 274 } 275 276 super.write(buffer, offset, count); 277 } 278 279 @Override write(byte[] buffer)280 public void write(byte[] buffer) throws IOException { 281 if (mRemaining >= 0) { 282 if (mRemaining == 0) return; 283 int count = buffer.length; 284 if (count > mRemaining) count = (int)mRemaining; 285 super.write(buffer); 286 mRemaining -= count; 287 return; 288 } 289 290 super.write(buffer); 291 } 292 293 @Override write(int oneByte)294 public void write(int oneByte) throws IOException { 295 if (mRemaining >= 0) { 296 if (mRemaining == 0) return; 297 super.write(oneByte); 298 mRemaining--; 299 return; 300 } 301 302 super.write(oneByte); 303 } 304 } 305 306 307 /* Parcelable interface */ describeContents()308 public int describeContents() { 309 return mFd.describeContents(); 310 } 311 writeToParcel(Parcel out, int flags)312 public void writeToParcel(Parcel out, int flags) { 313 mFd.writeToParcel(out, flags); 314 out.writeLong(mStartOffset); 315 out.writeLong(mLength); 316 } 317 AssetFileDescriptor(Parcel src)318 AssetFileDescriptor(Parcel src) { 319 mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src); 320 mStartOffset = src.readLong(); 321 mLength = src.readLong(); 322 } 323 324 public static final Parcelable.Creator<AssetFileDescriptor> CREATOR 325 = new Parcelable.Creator<AssetFileDescriptor>() { 326 public AssetFileDescriptor createFromParcel(Parcel in) { 327 return new AssetFileDescriptor(in); 328 } 329 public AssetFileDescriptor[] newArray(int size) { 330 return new AssetFileDescriptor[size]; 331 } 332 }; 333 334 } 335