+ * If the total length of the data being read is known, then this length minus {@code bytesRead()}
+ * is returned. If the total length is unknown, {@link C#LENGTH_UNBOUNDED} is returned.
+ *
+ * @return The remaining length, or {@link C#LENGTH_UNBOUNDED}.
+ */
+ protected final long bytesRemaining() {
+ return bytesToRead == C.LENGTH_UNBOUNDED ? bytesToRead : bytesToRead - bytesRead;
+ }
+
+ /**
+ * Establishes a connection.
+ */
+ private Request makeRequest(DataSpec dataSpec) {
+ long position = dataSpec.position;
+ long length = dataSpec.length;
+ boolean allowGzip = (dataSpec.flags & DataSpec.FLAG_ALLOW_GZIP) != 0;
+
+ HttpUrl url = HttpUrl.parse(dataSpec.uri.toString());
+ Request.Builder builder = new Request.Builder().url(url);
+ if (cacheControl != null) {
+ builder.cacheControl(cacheControl);
+ }
+ synchronized (requestProperties) {
+ for (Map.Entry
+ * This implementation is based roughly on {@code libcore.io.Streams.skipByReading()}.
+ *
+ * @throws InterruptedIOException If the thread is interrupted during the operation.
+ * @throws EOFException If the end of the input stream is reached before the bytes are skipped.
+ */
+ private void skipInternal() throws IOException {
+ if (bytesSkipped == bytesToSkip) {
+ return;
+ }
+
+ // Acquire the shared skip buffer.
+ byte[] skipBuffer = skipBufferReference.getAndSet(null);
+ if (skipBuffer == null) {
+ skipBuffer = new byte[4096];
+ }
+
+ while (bytesSkipped != bytesToSkip) {
+ int readLength = (int) Math.min(bytesToSkip - bytesSkipped, skipBuffer.length);
+ int read = responseByteStream.read(skipBuffer, 0, readLength);
+ if (Thread.interrupted()) {
+ throw new InterruptedIOException();
+ }
+ if (read == -1) {
+ throw new EOFException();
+ }
+ bytesSkipped += read;
+ if (listener != null) {
+ listener.onBytesTransferred(read);
+ }
+ }
+
+ // Release the shared skip buffer.
+ skipBufferReference.set(skipBuffer);
+ }
+
+ /**
+ * Reads up to {@code length} bytes of data and stores them into {@code buffer}, starting at
+ * index {@code offset}.
+ *
+ * This method blocks until at least one byte of data can be read, the end of the opened range is
+ * detected, or an exception is thrown.
+ *
+ * @param buffer The buffer into which the read data should be stored.
+ * @param offset The start offset into {@code buffer} at which data should be written.
+ * @param readLength The maximum number of bytes to read.
+ * @return The number of bytes read, or {@link C#RESULT_END_OF_INPUT} if the end of the opened
+ * range is reached.
+ * @throws IOException If an error occurs reading from the source.
+ */
+ private int readInternal(byte[] buffer, int offset, int readLength) throws IOException {
+ readLength = bytesToRead == C.LENGTH_UNBOUNDED ? readLength
+ : (int) Math.min(readLength, bytesToRead - bytesRead);
+ if (readLength == 0) {
+ // We've read all of the requested data.
+ return C.RESULT_END_OF_INPUT;
+ }
+
+ int read = responseByteStream.read(buffer, offset, readLength);
+ if (read == -1) {
+ if (bytesToRead != C.LENGTH_UNBOUNDED && bytesToRead != bytesRead) {
+ // The server closed the connection having not sent sufficient data.
+ throw new EOFException();
+ }
+ return C.RESULT_END_OF_INPUT;
+ }
+
+ bytesRead += read;
+ if (listener != null) {
+ listener.onBytesTransferred(read);
+ }
+ return read;
+ }
+
+ /**
+ * Closes the current connection quietly, if there is one.
+ */
+ private void closeConnectionQuietly() {
+ response.body().close();
+ response = null;
+ responseByteStream = null;
+ }
+
+}
diff --git a/extensions/okhttp/src/main/project.properties b/extensions/okhttp/src/main/project.properties
new file mode 100644
index 0000000000..b92a03b7ab
--- /dev/null
+++ b/extensions/okhttp/src/main/project.properties
@@ -0,0 +1,16 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-23
+android.library=true
+android.library.reference.1=../../../../library/src/main