Fix for HttpURLConnection not always throwing SocketTimeoutException

Contains upstream changes:
Okio: https://github.com/square/okio/pull/154
OkHttp: https://github.com/square/okhttp/pull/1698 (pending)

Bug: 21396523
Change-Id: Ibe913cb8584331a470716272dcd42c088cbb975e
diff --git a/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/HttpOverSpdyTest.java b/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/HttpOverSpdyTest.java
index ab8f3c9..be9f10e 100644
--- a/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/HttpOverSpdyTest.java
+++ b/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/HttpOverSpdyTest.java
@@ -32,6 +32,7 @@
 import java.net.Authenticator;
 import java.net.CookieManager;
 import java.net.HttpURLConnection;
+import java.net.SocketTimeoutException;
 import java.net.URL;
 import java.util.Arrays;
 import java.util.Collections;
@@ -307,8 +308,8 @@
     try {
       readAscii(connection.getInputStream(), Integer.MAX_VALUE);
       fail("Should have timed out!");
-    } catch (IOException e){
-      assertEquals("timeout", e.getMessage());
+    } catch (SocketTimeoutException expected){
+      assertEquals("timeout", expected.getMessage());
     }
   }
 
diff --git a/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/URLConnectionTest.java b/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/URLConnectionTest.java
index 7caf404..3be5a2d 100644
--- a/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/URLConnectionTest.java
+++ b/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/URLConnectionTest.java
@@ -57,6 +57,7 @@
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.SocketAddress;
+import java.net.SocketTimeoutException;
 import java.net.URI;
 import java.net.URL;
 import java.net.URLConnection;
@@ -2213,7 +2214,7 @@
     try {
       in.read(); // if Content-Length was accurate, this would return -1 immediately
       fail();
-    } catch (IOException expected) {
+    } catch (SocketTimeoutException expected) {
     }
   }
 
@@ -2249,7 +2250,7 @@
       byte[] data = new byte[16 * 1024 * 1024]; // 16 MiB.
       out.write(data);
       fail();
-    } catch (IOException expected) {
+    } catch (SocketTimeoutException expected) {
     }
   }
 
diff --git a/okhttp/src/main/java/com/squareup/okhttp/internal/spdy/SpdyStream.java b/okhttp/src/main/java/com/squareup/okhttp/internal/spdy/SpdyStream.java
index 05ce57a..cdbd7aa 100644
--- a/okhttp/src/main/java/com/squareup/okhttp/internal/spdy/SpdyStream.java
+++ b/okhttp/src/main/java/com/squareup/okhttp/internal/spdy/SpdyStream.java
@@ -19,6 +19,7 @@
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InterruptedIOException;
+import java.net.SocketTimeoutException;
 import java.util.ArrayList;
 import java.util.List;
 import okio.AsyncTimeout;
@@ -600,8 +601,16 @@
       closeLater(ErrorCode.CANCEL);
     }
 
-    public void exitAndThrowIfTimedOut() throws InterruptedIOException {
-      if (exit()) throw new InterruptedIOException("timeout");
+    @Override protected IOException newTimeoutException(IOException cause) {
+      SocketTimeoutException socketTimeoutException = new SocketTimeoutException("timeout");
+      if (cause != null) {
+        socketTimeoutException.initCause(cause);
+      }
+      return socketTimeoutException;
+    }
+
+    public void exitAndThrowIfTimedOut() throws IOException {
+      if (exit()) throw newTimeoutException(null /* cause */);
     }
   }
 }
diff --git a/okio/okio/src/main/java/okio/AsyncTimeout.java b/okio/okio/src/main/java/okio/AsyncTimeout.java
index cda6ff4..dbadfb4 100644
--- a/okio/okio/src/main/java/okio/AsyncTimeout.java
+++ b/okio/okio/src/main/java/okio/AsyncTimeout.java
@@ -241,22 +241,36 @@
   }
 
   /**
-   * Throws an InterruptedIOException if {@code throwOnTimeout} is true and a
-   * timeout occurred.
+   * Throws an IOException if {@code throwOnTimeout} is {@code true} and a
+   * timeout occurred. See {@link #newTimeoutException(java.io.IOException)}
+   * for the type of exception thrown.
    */
   final void exit(boolean throwOnTimeout) throws IOException {
     boolean timedOut = exit();
-    if (timedOut && throwOnTimeout) throw new InterruptedIOException("timeout");
+    if (timedOut && throwOnTimeout) throw newTimeoutException(null);
   }
 
   /**
-   * Returns either {@code cause} or an InterruptedIOException that's caused by
-   * {@code cause} if a timeout occurred.
+   * Returns either {@code cause} or an IOException that's caused by
+   * {@code cause} if a timeout occurred. See
+   * {@link #newTimeoutException(java.io.IOException)} for the type of
+   * exception returned.
    */
   final IOException exit(IOException cause) throws IOException {
     if (!exit()) return cause;
+    return newTimeoutException(cause);
+  }
+
+  /**
+   * Returns an {@link IOException} to represent a timeout. By default this method returns
+   * {@link java.io.InterruptedIOException}. If {@code cause} is non-null it is set as the cause of
+   * the returned exception.
+   */
+  protected IOException newTimeoutException(IOException cause) {
     InterruptedIOException e = new InterruptedIOException("timeout");
-    e.initCause(cause);
+    if (cause != null) {
+      e.initCause(cause);
+    }
     return e;
   }
 
diff --git a/okio/okio/src/main/java/okio/Okio.java b/okio/okio/src/main/java/okio/Okio.java
index df3f8bf..1eba4e0 100644
--- a/okio/okio/src/main/java/okio/Okio.java
+++ b/okio/okio/src/main/java/okio/Okio.java
@@ -21,8 +21,10 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InterruptedIOException;
 import java.io.OutputStream;
 import java.net.Socket;
+import java.net.SocketTimeoutException;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -201,6 +203,14 @@
 
   private static AsyncTimeout timeout(final Socket socket) {
     return new AsyncTimeout() {
+      @Override protected IOException newTimeoutException(IOException cause) {
+        InterruptedIOException ioe = new SocketTimeoutException("timeout");
+        if (cause != null) {
+          ioe.initCause(cause);
+        }
+        return ioe;
+      }
+
       @Override protected void timedOut() {
         try {
           socket.close();
diff --git a/okio/okio/src/test/java/okio/SocketTimeoutTest.java b/okio/okio/src/test/java/okio/SocketTimeoutTest.java
index 6bb73e6..23ea211 100644
--- a/okio/okio/src/test/java/okio/SocketTimeoutTest.java
+++ b/okio/okio/src/test/java/okio/SocketTimeoutTest.java
@@ -18,10 +18,10 @@
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InterruptedIOException;
 import java.io.OutputStream;
 import java.net.ServerSocket;
 import java.net.Socket;
+import java.net.SocketTimeoutException;
 import java.util.concurrent.TimeUnit;
 import org.junit.Test;
 
@@ -50,7 +50,7 @@
     try {
       source.require(ONE_MB);
       fail();
-    } catch (InterruptedIOException expected) {
+    } catch (SocketTimeoutException expected) {
     }
     socket.close();
   }
@@ -75,7 +75,7 @@
       sink.write(new Buffer().write(data), data.length);
       sink.flush();
       fail();
-    } catch (InterruptedIOException expected) {
+    } catch (SocketTimeoutException expected) {
     }
     long elapsed = System.nanoTime() - start;
     socket.close();