diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/V2SchemeSigner.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/V2SchemeSigner.java index e1853468e..103a0ecf9 100644 --- a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/V2SchemeSigner.java +++ b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/V2SchemeSigner.java @@ -16,7 +16,6 @@ package com.android.apksigner.core.internal.apk.v2; -import com.android.apksigner.core.internal.util.ByteBufferSink; import com.android.apksigner.core.internal.util.Pair; import com.android.apksigner.core.internal.zip.ZipUtils; import com.android.apksigner.core.util.DataSource; @@ -191,8 +190,10 @@ public abstract class V2SchemeSigner { // offset field is treated as pointing to the offset at which the APK Signing Block will // start. long centralDirOffsetForDigesting = beforeCentralDir.size(); - ByteBuffer eocdBuf = copyToByteBuffer(eocd); + ByteBuffer eocdBuf = ByteBuffer.allocate((int) eocd.size()); eocdBuf.order(ByteOrder.LITTLE_ENDIAN); + eocd.copyTo(0, (int) eocd.size(), eocdBuf); + eocdBuf.flip(); ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, centralDirOffsetForDigesting); // Compute digests of APK contents. @@ -600,15 +601,4 @@ public abstract class V2SchemeSigner { } return result.array(); } - - private static ByteBuffer copyToByteBuffer(DataSource dataSource) throws IOException { - long dataSourceSize = dataSource.size(); - if (dataSourceSize > Integer.MAX_VALUE) { - throw new IllegalArgumentException("Data source too large: " + dataSourceSize); - } - ByteBuffer result = ByteBuffer.allocate((int) dataSourceSize); - dataSource.feed(0, result.remaining(), new ByteBufferSink(result)); - result.position(0); - return result; - } } diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/ByteBufferDataSource.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/ByteBufferDataSource.java index f12f02ea8..b2d9ca115 100644 --- a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/ByteBufferDataSource.java +++ b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/ByteBufferDataSource.java @@ -35,7 +35,15 @@ public class ByteBufferDataSource implements DataSource { * buffer between the buffer's position and limit. */ public ByteBufferDataSource(ByteBuffer buffer) { - mBuffer = buffer.slice(); + this(buffer, true); + } + + /** + * Constructs a new {@code ByteBufferDigestSource} based on the data contained in the provided + * buffer between the buffer's position and limit. + */ + private ByteBufferDataSource(ByteBuffer buffer, boolean sliceRequired) { + mBuffer = (sliceRequired) ? buffer.slice() : buffer; mSize = buffer.remaining(); } @@ -45,15 +53,12 @@ public class ByteBufferDataSource implements DataSource { } @Override - public ByteBufferDataSource slice(long offset, long size) { - if ((offset == 0) && (size == mSize)) { - return this; - } + public ByteBuffer getByteBuffer(long offset, int size) { checkChunkValid(offset, size); - // checkChunkValid ensures that it's OK to cast offset and size to int. + // checkChunkValid ensures that it's OK to cast offset to int. int chunkPosition = (int) offset; - int chunkLimit = (int) (chunkPosition + size); + int chunkLimit = chunkPosition + size; // Creating a slice of ByteBuffer modifies the state of the source ByteBuffer (position // and limit fields, to be more specific). We thus use synchronization around these // state-changing operations to make instances of this class thread-safe. @@ -66,35 +71,35 @@ public class ByteBufferDataSource implements DataSource { mBuffer.limit(chunkLimit); mBuffer.position(chunkPosition); - // Create a ByteBufferDataSource for the slice of the buffer between limit and position. - return new ByteBufferDataSource(mBuffer); + return mBuffer.slice(); } } + @Override + public void copyTo(long offset, int size, ByteBuffer dest) { + dest.put(getByteBuffer(offset, size)); + } + @Override public void feed(long offset, long size, DataSink sink) throws IOException { - checkChunkValid(offset, size); - - // checkChunkValid ensures that it's OK to cast offset and size to int. - int chunkPosition = (int) offset; - int chunkLimit = (int) (chunkPosition + size); - ByteBuffer chunk; - // Creating a slice of ByteBuffer modifies the state of the source ByteBuffer (position - // and limit fields, to be more specific). We thus use synchronization around these - // state-changing operations to make instances of this class thread-safe. - synchronized (mBuffer) { - // ByteBuffer.limit(int) and .position(int) check that that the position >= limit - // invariant is not broken. Thus, the only way to safely change position and limit - // without caring about their current values is to first set position to 0 or set the - // limit to capacity. - mBuffer.position(0); - - mBuffer.limit(chunkLimit); - mBuffer.position(chunkPosition); - chunk = mBuffer.slice(); + if ((size < 0) || (size > mSize)) { + throw new IllegalArgumentException("size: " + size + ", source size: " + mSize); } + sink.consume(getByteBuffer(offset, (int) size)); + } - sink.consume(chunk); + @Override + public ByteBufferDataSource slice(long offset, long size) { + if ((offset == 0) && (size == mSize)) { + return this; + } + if ((size < 0) || (size > mSize)) { + throw new IllegalArgumentException("size: " + size + ", source size: " + mSize); + } + return new ByteBufferDataSource( + getByteBuffer(offset, (int) size), + false // no need to slice -- it's already a slice + ); } private void checkChunkValid(long offset, long size) { diff --git a/tools/apksigner/core/src/com/android/apksigner/core/util/DataSource.java b/tools/apksigner/core/src/com/android/apksigner/core/util/DataSource.java index 27ff7a8b3..e268dd29d 100644 --- a/tools/apksigner/core/src/com/android/apksigner/core/util/DataSource.java +++ b/tools/apksigner/core/src/com/android/apksigner/core/util/DataSource.java @@ -17,6 +17,7 @@ package com.android.apksigner.core.util; import java.io.IOException; +import java.nio.ByteBuffer; /** * Abstract representation of a source of data. @@ -29,6 +30,25 @@ import java.io.IOException; * may have worked as the unifying abstraction. *
There are following ways to obtain a chunk of data from the data source: + *
The returned buffer's position is {@code 0}, and the buffer's limit and capacity is + * {@code size}. + * + * @param offset index (in bytes) at which the chunk starts inside data source + * @param size size (in bytes) of the chunk + */ + ByteBuffer getByteBuffer(long offset, int size) throws IOException; + + /** + * Copies the specified chunk from this data source into the provided destination buffer, + * advancing the destination buffer's position by {@code size}. + * + * @param offset index (in bytes) at which the chunk starts inside data source + * @param size size (in bytes) of the chunk + */ + void copyTo(long offset, int size, ByteBuffer dest) throws IOException; + /** * Returns a data source representing the specified region of data of this data source. Changes * to data represented by this data source will also be visible in the returned data source. + * + * @param offset index (in bytes) at which the region starts inside data source + * @param size size (in bytes) of the region */ DataSource slice(long offset, long size); }