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