/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.res;
import android.os.Bundle;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
File descriptor of an entry in the AssetManager. This provides your own
opened FileDescriptor that can be used to read the data, as well as the
offset and length of that entry's data in the file.
/**
* File descriptor of an entry in the AssetManager. This provides your own
* opened FileDescriptor that can be used to read the data, as well as the
* offset and length of that entry's data in the file.
*/
public class AssetFileDescriptor implements Parcelable, Closeable {
Length used with AssetFileDescriptor(ParcelFileDescriptor, long, long)
and getDeclaredLength
when a length has not been declared. This means the data extends to the end of the file. /**
* Length used with {@link #AssetFileDescriptor(ParcelFileDescriptor, long, long)}
* and {@link #getDeclaredLength} when a length has not been declared. This means
* the data extends to the end of the file.
*/
public static final long UNKNOWN_LENGTH = -1;
private final ParcelFileDescriptor mFd;
private final long mStartOffset;
private final long mLength;
private final Bundle mExtras;
Create a new AssetFileDescriptor from the given values.
Params: - fd – The underlying file descriptor.
- startOffset – The location within the file that the asset starts.
This must be 0 if length is UNKNOWN_LENGTH.
- length – The number of bytes of the asset, or
UNKNOWN_LENGTH
if it extends to the end of the file.
/**
* Create a new AssetFileDescriptor from the given values.
*
* @param fd The underlying file descriptor.
* @param startOffset The location within the file that the asset starts.
* This must be 0 if length is UNKNOWN_LENGTH.
* @param length The number of bytes of the asset, or
* {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
*/
public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
long length) {
this(fd, startOffset, length, null);
}
Create a new AssetFileDescriptor from the given values.
Params: - fd – The underlying file descriptor.
- startOffset – The location within the file that the asset starts.
This must be 0 if length is UNKNOWN_LENGTH.
- length – The number of bytes of the asset, or
UNKNOWN_LENGTH
if it extends to the end of the file. - extras – additional details that can be used to interpret the
underlying file descriptor. May be null.
/**
* Create a new AssetFileDescriptor from the given values.
*
* @param fd The underlying file descriptor.
* @param startOffset The location within the file that the asset starts.
* This must be 0 if length is UNKNOWN_LENGTH.
* @param length The number of bytes of the asset, or
* {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
* @param extras additional details that can be used to interpret the
* underlying file descriptor. May be null.
*/
public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
long length, Bundle extras) {
if (fd == null) {
throw new IllegalArgumentException("fd must not be null");
}
if (length < 0 && startOffset != 0) {
throw new IllegalArgumentException(
"startOffset must be 0 when using UNKNOWN_LENGTH");
}
mFd = fd;
mStartOffset = startOffset;
mLength = length;
mExtras = extras;
}
The AssetFileDescriptor contains its own ParcelFileDescriptor, which
in addition to the normal FileDescriptor object also allows you to close
the descriptor when you are done with it.
/**
* The AssetFileDescriptor contains its own ParcelFileDescriptor, which
* in addition to the normal FileDescriptor object also allows you to close
* the descriptor when you are done with it.
*/
public ParcelFileDescriptor getParcelFileDescriptor() {
return mFd;
}
Returns the FileDescriptor that can be used to read the data in the
file.
/**
* Returns the FileDescriptor that can be used to read the data in the
* file.
*/
public FileDescriptor getFileDescriptor() {
return mFd.getFileDescriptor();
}
Returns the byte offset where this asset entry's data starts.
/**
* Returns the byte offset where this asset entry's data starts.
*/
public long getStartOffset() {
return mStartOffset;
}
Returns any additional details that can be used to interpret the
underlying file descriptor. May be null.
/**
* Returns any additional details that can be used to interpret the
* underlying file descriptor. May be null.
*/
public Bundle getExtras() {
return mExtras;
}
Returns the total number of bytes of this asset entry's data. May be UNKNOWN_LENGTH
if the asset extends to the end of the file. If the AssetFileDescriptor was constructed with UNKNOWN_LENGTH
, this will use
ParcelFileDescriptor.getStatSize()
to find the total size of the file, returning that number if found or UNKNOWN_LENGTH
if it could not be determined. See Also:
/**
* Returns the total number of bytes of this asset entry's data. May be
* {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file.
* If the AssetFileDescriptor was constructed with {@link #UNKNOWN_LENGTH},
* this will use {@link ParcelFileDescriptor#getStatSize()
* ParcelFileDescriptor.getStatSize()} to find the total size of the file,
* returning that number if found or {@link #UNKNOWN_LENGTH} if it could
* not be determined.
*
* @see #getDeclaredLength()
*/
public long getLength() {
if (mLength >= 0) {
return mLength;
}
long len = mFd.getStatSize();
return len >= 0 ? len : UNKNOWN_LENGTH;
}
Return the actual number of bytes that were declared when the AssetFileDescriptor was constructed. Will be UNKNOWN_LENGTH
if the length was not declared, meaning data should be read to the end of the file. See Also:
/**
* Return the actual number of bytes that were declared when the
* AssetFileDescriptor was constructed. Will be
* {@link #UNKNOWN_LENGTH} if the length was not declared, meaning data
* should be read to the end of the file.
*
* @see #getDeclaredLength()
*/
public long getDeclaredLength() {
return mLength;
}
Convenience for calling getParcelFileDescriptor().close()
.
/**
* Convenience for calling <code>getParcelFileDescriptor().close()</code>.
*/
@Override
public void close() throws IOException {
mFd.close();
}
Create and return a new auto-close input stream for this asset. This will either return a full asset AutoCloseInputStream
, or an underlying
ParcelFileDescriptor.AutoCloseInputStream
depending on whether the the object represents a complete file or sub-section of a file. You should only call this once for a particular asset. /**
* Create and return a new auto-close input stream for this asset. This
* will either return a full asset {@link AutoCloseInputStream}, or
* an underlying {@link ParcelFileDescriptor.AutoCloseInputStream
* ParcelFileDescriptor.AutoCloseInputStream} depending on whether the
* the object represents a complete file or sub-section of a file. You
* should only call this once for a particular asset.
*/
public FileInputStream createInputStream() throws IOException {
if (mLength < 0) {
return new ParcelFileDescriptor.AutoCloseInputStream(mFd);
}
return new AutoCloseInputStream(this);
}
Create and return a new auto-close output stream for this asset. This will either return a full asset AutoCloseOutputStream
, or an underlying
ParcelFileDescriptor.AutoCloseOutputStream
depending on whether the the object represents a complete file or sub-section of a file. You should only call this once for a particular asset. /**
* Create and return a new auto-close output stream for this asset. This
* will either return a full asset {@link AutoCloseOutputStream}, or
* an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream
* ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the
* the object represents a complete file or sub-section of a file. You
* should only call this once for a particular asset.
*/
public FileOutputStream createOutputStream() throws IOException {
if (mLength < 0) {
return new ParcelFileDescriptor.AutoCloseOutputStream(mFd);
}
return new AutoCloseOutputStream(this);
}
@Override
public String toString() {
return "{AssetFileDescriptor: " + mFd
+ " start=" + mStartOffset + " len=" + mLength + "}";
}
An InputStream you can create on a ParcelFileDescriptor, which will take care of calling
ParcelFileDescriptor.close()
for you when the stream is closed. /**
* An InputStream you can create on a ParcelFileDescriptor, which will
* take care of calling {@link ParcelFileDescriptor#close
* ParcelFileDescriptor.close()} for you when the stream is closed.
*/
public static class AutoCloseInputStream
extends ParcelFileDescriptor.AutoCloseInputStream {
private long mRemaining;
public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
super(fd.getParcelFileDescriptor());
super.skip(fd.getStartOffset());
mRemaining = (int)fd.getLength();
}
@Override
public int available() throws IOException {
return mRemaining >= 0
? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff)
: super.available();
}
@Override
public int read() throws IOException {
byte[] buffer = new byte[1];
int result = read(buffer, 0, 1);
return result == -1 ? -1 : buffer[0] & 0xff;
}
@Override
public int read(byte[] buffer, int offset, int count) throws IOException {
if (mRemaining >= 0) {
if (mRemaining == 0) return -1;
if (count > mRemaining) count = (int)mRemaining;
int res = super.read(buffer, offset, count);
if (res >= 0) mRemaining -= res;
return res;
}
return super.read(buffer, offset, count);
}
@Override
public int read(byte[] buffer) throws IOException {
return read(buffer, 0, buffer.length);
}
@Override
public long skip(long count) throws IOException {
if (mRemaining >= 0) {
if (mRemaining == 0) return -1;
if (count > mRemaining) count = mRemaining;
long res = super.skip(count);
if (res >= 0) mRemaining -= res;
return res;
}
return super.skip(count);
}
@Override
public void mark(int readlimit) {
if (mRemaining >= 0) {
// Not supported.
return;
}
super.mark(readlimit);
}
@Override
public boolean markSupported() {
if (mRemaining >= 0) {
return false;
}
return super.markSupported();
}
@Override
public synchronized void reset() throws IOException {
if (mRemaining >= 0) {
// Not supported.
return;
}
super.reset();
}
}
An OutputStream you can create on a ParcelFileDescriptor, which will take care of calling
ParcelFileDescriptor.close()
for you when the stream is closed. /**
* An OutputStream you can create on a ParcelFileDescriptor, which will
* take care of calling {@link ParcelFileDescriptor#close
* ParcelFileDescriptor.close()} for you when the stream is closed.
*/
public static class AutoCloseOutputStream
extends ParcelFileDescriptor.AutoCloseOutputStream {
private long mRemaining;
public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException {
super(fd.getParcelFileDescriptor());
if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) {
throw new IOException("Unable to seek");
}
mRemaining = (int)fd.getLength();
}
@Override
public void write(byte[] buffer, int offset, int count) throws IOException {
if (mRemaining >= 0) {
if (mRemaining == 0) return;
if (count > mRemaining) count = (int)mRemaining;
super.write(buffer, offset, count);
mRemaining -= count;
return;
}
super.write(buffer, offset, count);
}
@Override
public void write(byte[] buffer) throws IOException {
if (mRemaining >= 0) {
if (mRemaining == 0) return;
int count = buffer.length;
if (count > mRemaining) count = (int)mRemaining;
super.write(buffer);
mRemaining -= count;
return;
}
super.write(buffer);
}
@Override
public void write(int oneByte) throws IOException {
if (mRemaining >= 0) {
if (mRemaining == 0) return;
super.write(oneByte);
mRemaining--;
return;
}
super.write(oneByte);
}
}
/* Parcelable interface */
@Override
public int describeContents() {
return mFd.describeContents();
}
@Override
public void writeToParcel(Parcel out, int flags) {
mFd.writeToParcel(out, flags);
out.writeLong(mStartOffset);
out.writeLong(mLength);
if (mExtras != null) {
out.writeInt(1);
out.writeBundle(mExtras);
} else {
out.writeInt(0);
}
}
AssetFileDescriptor(Parcel src) {
mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src);
mStartOffset = src.readLong();
mLength = src.readLong();
if (src.readInt() != 0) {
mExtras = src.readBundle();
} else {
mExtras = null;
}
}
public static final Parcelable.Creator<AssetFileDescriptor> CREATOR
= new Parcelable.Creator<AssetFileDescriptor>() {
public AssetFileDescriptor createFromParcel(Parcel in) {
return new AssetFileDescriptor(in);
}
public AssetFileDescriptor[] newArray(int size) {
return new AssetFileDescriptor[size];
}
};
}