core, alts, cronet: fix ByteBuffer covariant method usages (#7349)

Java 9 introduces overridden methods with covariant return types for the following methods in java.nio.ByteBuffer:

- position​(int newPosition)
- limit​(int newLimit)
- flip​()
- clear​()
- mark​()
- reset​()
- rewind​()

In Java 9 they all now return ByteBuffer, whereas the methods they override return Buffer, resulting in exceptions like this when executing on Java 8 and lower:

java.lang.NoSuchMethodError: java.nio.ByteBuffer.limit(I)Ljava/nio/ByteBuffer

This is because the generated byte code includes the static return type of the method, which is not found on Java 8 and lower because the overloaded methods with covariant return types don't exist (the issue appears even with source and target 8 or lower in compilation parameters).
The solution is to cast ByteBuffer instances to Buffer before calling the method.
This commit is contained in:
Chengyuan Zhang 2020-08-25 17:21:34 -07:00 committed by GitHub
parent 720df64fd2
commit c30505df04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 84 additions and 74 deletions

View File

@ -17,6 +17,7 @@
package io.grpc.alts.internal;
import com.google.common.base.Preconditions;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.GeneralSecurityException;
@ -63,10 +64,10 @@ public final class AltsFraming {
}
Producer producer = new Producer();
ByteBuffer inputAlias = input.duplicate();
inputAlias.limit(input.position() + dataSize);
((Buffer) inputAlias).limit(input.position() + dataSize);
producer.readBytes(inputAlias);
producer.flush();
input.position(inputAlias.position());
((Buffer) input).position(inputAlias.position());
ByteBuffer output = producer.getRawFrame();
return output;
}
@ -166,10 +167,10 @@ public final class AltsFraming {
int frameLength = buffer.position() + getFrameSuffixLength();
// Set the limit and move to the start.
buffer.flip();
((Buffer) buffer).flip();
// Advance the limit to allow a crypto suffix.
buffer.limit(buffer.limit() + getFrameSuffixLength());
((Buffer) buffer).limit(buffer.limit() + getFrameSuffixLength());
// Write the data length and the message type.
int dataLength = frameLength - FRAME_LENGTH_HEADER_SIZE;
@ -178,17 +179,17 @@ public final class AltsFraming {
buffer.putInt(MESSAGE_TYPE);
// Move the position back to 0, the frame is ready.
buffer.position(0);
((Buffer) buffer).position(0);
isComplete = true;
}
/** Resets the state, preparing to construct a new frame. Must be called between frames. */
private void reset() {
buffer.clear();
((Buffer) buffer).clear();
// Save some space for framing, we'll fill that in later.
buffer.position(getFramePrefixLength());
buffer.limit(buffer.limit() - getFrameSuffixLength());
((Buffer) buffer).position(getFramePrefixLength());
((Buffer) buffer).limit(buffer.limit() - getFrameSuffixLength());
isComplete = false;
}
@ -279,7 +280,7 @@ public final class AltsFraming {
// internal buffer is large enough.
if (buffer.position() == FRAME_LENGTH_HEADER_SIZE && input.hasRemaining()) {
ByteBuffer bufferAlias = buffer.duplicate();
bufferAlias.flip();
((Buffer) bufferAlias).flip();
bufferAlias.order(ByteOrder.LITTLE_ENDIAN);
int dataLength = bufferAlias.getInt();
if (dataLength < FRAME_MESSAGE_TYPE_HEADER_SIZE || dataLength > MAX_DATA_LENGTH) {
@ -292,7 +293,7 @@ public final class AltsFraming {
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(dataLength);
}
buffer.limit(frameLength);
((Buffer) buffer).limit(frameLength);
}
// TODO: Similarly extract and check message type.
@ -300,7 +301,7 @@ public final class AltsFraming {
// Read the remaining data into the internal buffer.
copy(buffer, input);
if (!buffer.hasRemaining()) {
buffer.flip();
((Buffer) buffer).flip();
isComplete = true;
}
return isComplete;
@ -323,7 +324,7 @@ public final class AltsFraming {
/** Resets the state, preparing to parse a new frame. Must be called between frames. */
private void reset() {
buffer.clear();
((Buffer) buffer).clear();
isComplete = false;
}
@ -356,9 +357,9 @@ public final class AltsFraming {
} else {
int count = Math.min(dst.remaining(), src.remaining());
ByteBuffer slice = src.slice();
slice.limit(count);
((Buffer) slice).limit(count);
dst.put(slice);
src.position(src.position() + count);
((Buffer) src).position(src.position() + count);
}
}
}

View File

@ -23,6 +23,7 @@ import com.google.protobuf.ByteString;
import io.grpc.Status;
import io.grpc.alts.internal.HandshakerServiceGrpc.HandshakerServiceStub;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.util.logging.Level;
@ -199,7 +200,7 @@ class AltsHandshakerClient {
throw new GeneralSecurityException(e);
}
handleResponse(resp);
inBytes.position(inBytes.position() + resp.getBytesConsumed());
((Buffer) inBytes).position(inBytes.position() + resp.getBytesConsumed());
return resp.getOutFrames().asReadOnlyByteBuffer();
}
@ -227,7 +228,7 @@ class AltsHandshakerClient {
throw new GeneralSecurityException(e);
}
handleResponse(resp);
inBytes.position(inBytes.position() + resp.getBytesConsumed());
((Buffer) inBytes).position(inBytes.position() + resp.getBytesConsumed());
return resp.getOutFrames().asReadOnlyByteBuffer();
}

View File

@ -22,6 +22,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import io.grpc.alts.internal.HandshakerServiceGrpc.HandshakerServiceStub;
import io.netty.buffer.ByteBufAllocator;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
@ -151,10 +152,10 @@ public final class AltsTsiHandshaker implements TsiHandshaker {
ByteBuffer outputFrameAlias = outputFrame;
if (outputFrame.remaining() > bytes.remaining()) {
outputFrameAlias = outputFrame.duplicate();
outputFrameAlias.limit(outputFrameAlias.position() + bytes.remaining());
((Buffer) outputFrameAlias).limit(outputFrameAlias.position() + bytes.remaining());
}
bytes.put(outputFrameAlias);
outputFrame.position(outputFrameAlias.position());
((Buffer) outputFrame).position(outputFrameAlias.position());
}
/**

View File

@ -19,6 +19,7 @@ package io.grpc.alts.internal;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.GeneralSecurityException;
@ -38,7 +39,7 @@ public class AltsFramingTest {
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(-1); // write invalid length
buffer.put((byte) 0); // write some byte
buffer.flip();
((Buffer) buffer).flip();
try {
parser.readBytes(buffer);
@ -56,7 +57,7 @@ public class AltsFramingTest {
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(AltsFraming.getFrameMessageTypeHeaderSize() - 1); // write invalid length
buffer.put((byte) 0); // write some byte
buffer.flip();
((Buffer) buffer).flip();
try {
parser.readBytes(buffer);
@ -74,7 +75,7 @@ public class AltsFramingTest {
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(AltsFraming.getMaxDataLength() + 1); // write invalid length
buffer.put((byte) 0); // write some byte
buffer.flip();
((Buffer) buffer).flip();
try {
parser.readBytes(buffer);
@ -97,7 +98,7 @@ public class AltsFramingTest {
buffer.putInt(6); // default message type
buffer.put(new byte[dataLength - AltsFraming.getFrameMessageTypeHeaderSize()]); // write data
buffer.put((byte) 0);
buffer.flip();
((Buffer) buffer).flip();
parser.readBytes(buffer);
@ -116,7 +117,7 @@ public class AltsFramingTest {
buffer.putInt(dataLength); // write invalid length
buffer.putInt(6); // default message type
buffer.put((byte) 0);
buffer.flip();
((Buffer) buffer).flip();
parser.readBytes(buffer);

View File

@ -29,6 +29,7 @@ import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.ByteString;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import org.junit.Before;
@ -178,7 +179,7 @@ public class AltsHandshakerClientTest {
.thenReturn(MockAltsHandshakerResp.getOkResponse(BYTES_CONSUMED));
ByteBuffer inBytes = ByteBuffer.allocate(IN_BYTES_SIZE);
inBytes.position(PREFIX_POSITION);
((Buffer) inBytes).position(PREFIX_POSITION);
ByteBuffer outFrame = handshaker.startServerHandshake(inBytes);
assertEquals(ByteString.copyFrom(outFrame), MockAltsHandshakerResp.getOutFrame());

View File

@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.protobuf.ByteString;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import org.junit.Before;
import org.junit.Test;
@ -112,7 +113,7 @@ public class AltsTsiHandshakerTest {
verify(mockServer, never()).startClientHandshake();
verify(mockServer, never()).next(ArgumentMatchers.<ByteBuffer>any());
// Mock transport buffer all consumed by processBytesFromPeer and there is an output frame.
transportBuffer.position(transportBuffer.limit());
((Buffer) transportBuffer).position(transportBuffer.limit());
when(mockServer.startServerHandshake(transportBuffer)).thenReturn(outputFrame);
when(mockServer.isFinished()).thenReturn(false);
@ -127,7 +128,7 @@ public class AltsTsiHandshakerTest {
verify(mockServer, never()).next(ArgumentMatchers.<ByteBuffer>any());
// Mock transport buffer all consumed by processBytesFromPeer and output frame is empty.
// Expect processBytesFromPeer return False, because more data are needed from the peer.
transportBuffer.position(transportBuffer.limit());
((Buffer) transportBuffer).position(transportBuffer.limit());
when(mockServer.startServerHandshake(transportBuffer)).thenReturn(emptyOutputFrame);
when(mockServer.isFinished()).thenReturn(false);
@ -174,7 +175,7 @@ public class AltsTsiHandshakerTest {
when(mockClient.isFinished()).thenReturn(false);
handshakerClient.getBytesToSendToPeer(transportBuffer);
transportBuffer.position(transportBuffer.limit());
((Buffer) transportBuffer).position(transportBuffer.limit());
assertFalse(handshakerClient.processBytesFromPeer(transportBuffer));
}

View File

@ -27,6 +27,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCounted;
import io.netty.util.ResourceLeakDetector;
import io.netty.util.ResourceLeakDetector.Level;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
@ -86,11 +87,11 @@ public class FakeTsiTest {
byte[] transportBufferBytes = new byte[TsiTest.getDefaultTransportBufferSize()];
ByteBuffer transportBuffer = ByteBuffer.wrap(transportBufferBytes);
transportBuffer.limit(0); // Start off with an empty buffer
((Buffer) transportBuffer).limit(0); // Start off with an empty buffer
transportBuffer.clear();
((Buffer) transportBuffer).clear();
clientHandshaker.getBytesToSendToPeer(transportBuffer);
transportBuffer.flip();
((Buffer) transportBuffer).flip();
assertEquals(
FakeTsiHandshaker.State.CLIENT_INIT.toString().trim(),
new String(transportBufferBytes, 4, transportBuffer.remaining(), UTF_8).trim());
@ -99,14 +100,14 @@ public class FakeTsiTest {
assertFalse(transportBuffer.hasRemaining());
// client shouldn't offer any more bytes
transportBuffer.clear();
((Buffer) transportBuffer).clear();
clientHandshaker.getBytesToSendToPeer(transportBuffer);
transportBuffer.flip();
((Buffer) transportBuffer).flip();
assertFalse(transportBuffer.hasRemaining());
transportBuffer.clear();
((Buffer) transportBuffer).clear();
serverHandshaker.getBytesToSendToPeer(transportBuffer);
transportBuffer.flip();
((Buffer) transportBuffer).flip();
assertEquals(
FakeTsiHandshaker.State.SERVER_INIT.toString().trim(),
new String(transportBufferBytes, 4, transportBuffer.remaining(), UTF_8).trim());
@ -115,14 +116,14 @@ public class FakeTsiTest {
assertFalse(transportBuffer.hasRemaining());
// server shouldn't offer any more bytes
transportBuffer.clear();
((Buffer) transportBuffer).clear();
serverHandshaker.getBytesToSendToPeer(transportBuffer);
transportBuffer.flip();
((Buffer) transportBuffer).flip();
assertFalse(transportBuffer.hasRemaining());
transportBuffer.clear();
((Buffer) transportBuffer).clear();
clientHandshaker.getBytesToSendToPeer(transportBuffer);
transportBuffer.flip();
((Buffer) transportBuffer).flip();
assertEquals(
FakeTsiHandshaker.State.CLIENT_FINISHED.toString().trim(),
new String(transportBufferBytes, 4, transportBuffer.remaining(), UTF_8).trim());
@ -131,14 +132,14 @@ public class FakeTsiTest {
assertFalse(transportBuffer.hasRemaining());
// client shouldn't offer any more bytes
transportBuffer.clear();
((Buffer) transportBuffer).clear();
clientHandshaker.getBytesToSendToPeer(transportBuffer);
transportBuffer.flip();
((Buffer) transportBuffer).flip();
assertFalse(transportBuffer.hasRemaining());
transportBuffer.clear();
((Buffer) transportBuffer).clear();
serverHandshaker.getBytesToSendToPeer(transportBuffer);
transportBuffer.flip();
((Buffer) transportBuffer).flip();
assertEquals(
FakeTsiHandshaker.State.SERVER_FINISHED.toString().trim(),
new String(transportBufferBytes, 4, transportBuffer.remaining(), UTF_8).trim());
@ -147,9 +148,9 @@ public class FakeTsiTest {
assertFalse(transportBuffer.hasRemaining());
// server shouldn't offer any more bytes
transportBuffer.clear();
((Buffer) transportBuffer).clear();
serverHandshaker.getBytesToSendToPeer(transportBuffer);
transportBuffer.flip();
((Buffer) transportBuffer).flip();
assertFalse(transportBuffer.hasRemaining());
} catch (GeneralSecurityException e) {
throw new AssertionError(e);

View File

@ -20,6 +20,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.protobuf.ByteString;
import io.grpc.Status;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.SecureRandom;
@ -62,7 +63,7 @@ class MockAltsHandshakerResp {
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(frameSize);
buffer.put(TEST_OUT_FRAME.getBytes(UTF_8));
buffer.flip();
((Buffer) buffer).flip();
return ByteString.copyFrom(buffer);
}

View File

@ -26,6 +26,7 @@ import io.grpc.alts.internal.TsiFrameProtector.Consumer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledByteBufAllocator;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
@ -83,7 +84,7 @@ public final class TsiTest {
byte[] transportBufferBytes = new byte[transportBufferSize];
ByteBuffer transportBuffer = ByteBuffer.wrap(transportBufferBytes);
transportBuffer.limit(0); // Start off with an empty buffer
((Buffer) transportBuffer).limit(0); // Start off with an empty buffer
while (clientHandshaker.isInProgress() || serverHandshaker.isInProgress()) {
for (TsiHandshaker handshaker : new TsiHandshaker[] {clientHandshaker, serverHandshaker}) {
@ -94,9 +95,9 @@ public final class TsiTest {
}
// Put new bytes on the wire, if needed.
if (handshaker.isInProgress()) {
transportBuffer.clear();
((Buffer) transportBuffer).clear();
handshaker.getBytesToSendToPeer(transportBuffer);
transportBuffer.flip();
((Buffer) transportBuffer).flip();
}
}
}

View File

@ -102,18 +102,16 @@ public class CompositeReadableBuffer extends AbstractReadableBuffer {
@Override
public void readBytes(final ByteBuffer dest) {
// Use Buffer instead of ByteBuffer for JDK 9+ compatibility.
final Buffer destAsBuffer = dest;
execute(new ReadOperation() {
@Override
public int readInternal(ReadableBuffer buffer, int length) {
// Change the limit so that only lengthToCopy bytes are available.
int prevLimit = destAsBuffer.limit();
destAsBuffer.limit(destAsBuffer.position() + length);
int prevLimit = dest.limit();
((Buffer) dest).limit(dest.position() + length);
// Write the bytes and restore the original limit.
buffer.readBytes(dest);
destAsBuffer.limit(prevLimit);
((Buffer) dest).limit(prevLimit);
return 0;
}
}, dest.remaining());

View File

@ -210,8 +210,7 @@ public final class ReadableBuffers {
* A {@link ReadableBuffer} that is backed by a {@link ByteBuffer}.
*/
private static class ByteReadableBufferWrapper extends AbstractReadableBuffer {
// Use Buffer instead of ByteBuffer for JDK 9+ compatibility.
final Buffer bytes;
final ByteBuffer bytes;
ByteReadableBufferWrapper(ByteBuffer bytes) {
this.bytes = Preconditions.checkNotNull(bytes, "bytes");
@ -225,19 +224,19 @@ public final class ReadableBuffers {
@Override
public int readUnsignedByte() {
checkReadable(1);
return ((ByteBuffer) bytes).get() & 0xFF;
return bytes.get() & 0xFF;
}
@Override
public void skipBytes(int length) {
checkReadable(length);
bytes.position(bytes.position() + length);
((Buffer) bytes).position(bytes.position() + length);
}
@Override
public void readBytes(byte[] dest, int destOffset, int length) {
checkReadable(length);
((ByteBuffer) bytes).get(dest, destOffset, length);
bytes.get(dest, destOffset, length);
}
@Override
@ -248,10 +247,10 @@ public final class ReadableBuffers {
// Change the limit so that only length bytes are available.
int prevLimit = bytes.limit();
bytes.limit(bytes.position() + length);
((Buffer) bytes).limit(bytes.position() + length);
// Write the bytes and restore the original limit.
dest.put((ByteBuffer) bytes);
dest.put(bytes);
bytes.limit(prevLimit);
}
@ -260,11 +259,11 @@ public final class ReadableBuffers {
checkReadable(length);
if (hasArray()) {
dest.write(array(), arrayOffset(), length);
bytes.position(bytes.position() + length);
((Buffer) bytes).position(bytes.position() + length);
} else {
// The buffer doesn't support array(). Copy the data to an intermediate buffer.
byte[] array = new byte[length];
((ByteBuffer) bytes).get(array);
bytes.get(array);
dest.write(array);
}
}
@ -272,9 +271,9 @@ public final class ReadableBuffers {
@Override
public ByteReadableBufferWrapper readBytes(int length) {
checkReadable(length);
ByteBuffer buffer = ((ByteBuffer) bytes).duplicate();
ByteBuffer buffer = bytes.duplicate();
((Buffer) buffer).limit(bytes.position() + length);
bytes.position(bytes.position() + length);
((Buffer) bytes).position(bytes.position() + length);
return new ByteReadableBufferWrapper(buffer);
}
@ -285,7 +284,7 @@ public final class ReadableBuffers {
@Override
public byte[] array() {
return ((ByteBuffer) bytes).array();
return bytes.array();
}
@Override

View File

@ -23,6 +23,7 @@ import static org.mockito.Mockito.verify;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import org.junit.After;
import org.junit.Before;
@ -119,17 +120,17 @@ public class CompositeReadableBufferTest {
ByteBuffer byteBuffer = ByteBuffer.allocate(EXPECTED_VALUE.length());
int remaining = EXPECTED_VALUE.length();
byteBuffer.limit(1);
((Buffer) byteBuffer).limit(1);
composite.readBytes(byteBuffer);
remaining--;
assertEquals(remaining, composite.readableBytes());
byteBuffer.limit(byteBuffer.limit() + 5);
((Buffer) byteBuffer).limit(byteBuffer.limit() + 5);
composite.readBytes(byteBuffer);
remaining -= 5;
assertEquals(remaining, composite.readableBytes());
byteBuffer.limit(byteBuffer.limit() + remaining);
((Buffer) byteBuffer).limit(byteBuffer.limit() + remaining);
composite.readBytes(byteBuffer);
assertEquals(0, composite.readableBytes());
assertEquals(EXPECTED_VALUE, new String(byteBuffer.array(), UTF_8));

View File

@ -21,6 +21,7 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayOutputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.junit.Test;
@ -86,7 +87,7 @@ public abstract class ReadableBufferTestBase {
ReadableBuffer buffer = buffer();
ByteBuffer byteBuffer = ByteBuffer.allocate(msg.length());
buffer.readBytes(byteBuffer);
byteBuffer.flip();
((Buffer) byteBuffer).flip();
byte[] array = new byte[msg.length()];
byteBuffer.get(array);
assertArrayEquals(msg.getBytes(UTF_8), array);
@ -98,7 +99,7 @@ public abstract class ReadableBufferTestBase {
ReadableBuffer buffer = buffer();
ByteBuffer byteBuffer = ByteBuffer.allocate(2);
buffer.readBytes(byteBuffer);
byteBuffer.flip();
((Buffer) byteBuffer).flip();
byte[] array = new byte[2];
byteBuffer.get(array);
assertArrayEquals(new byte[]{'h', 'e'}, array);

View File

@ -42,6 +42,7 @@ import io.grpc.internal.TransportTracer;
import io.grpc.internal.WritableBuffer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
@ -218,7 +219,7 @@ class CronetClientStream extends AbstractClientStream {
ByteBuffer byteBuffer;
if (buffer != null) {
byteBuffer = ((CronetWritableBuffer) buffer).buffer();
byteBuffer.flip();
((Buffer) byteBuffer).flip();
} else {
byteBuffer = EMPTY_BUFFER;
}
@ -471,7 +472,7 @@ class CronetClientStream extends AbstractClientStream {
@Override
public void onReadCompleted(BidirectionalStream stream, UrlResponseInfo info,
ByteBuffer buffer, boolean endOfStream) {
buffer.flip();
((Buffer) buffer).flip();
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.v(LOG_TAG, "onReadCompleted. Size=" + buffer.remaining());
}

View File

@ -43,6 +43,7 @@ import io.grpc.internal.TransportTracer;
import io.grpc.internal.WritableBuffer;
import io.grpc.testing.TestMethodDescriptors;
import java.io.ByteArrayInputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
@ -176,7 +177,7 @@ public final class CronetClientStreamTest {
// 5 writes are called.
verify(cronetStream, times(5)).write(isA(ByteBuffer.class), eq(false));
ByteBuffer fakeBuffer = ByteBuffer.allocateDirect(8);
fakeBuffer.position(8);
((Buffer) fakeBuffer).position(8);
verify(cronetStream, times(2)).flush();
// 5 onWriteCompleted callbacks for previous writes.
@ -294,7 +295,7 @@ public final class CronetClientStreamTest {
ArgumentCaptor<ByteBuffer> bufferCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
verify(cronetStream, times(1)).write(bufferCaptor.capture(), isA(Boolean.class));
ByteBuffer buffer = bufferCaptor.getValue();
buffer.position(request.length());
((Buffer) buffer).position(request.length());
verify(cronetStream, times(1)).flush();
// Receive response header