1e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller/* 2e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Copyright (C) 2014 Square, Inc. 3e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 4e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License"); 5e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * you may not use this file except in compliance with the License. 6e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * You may obtain a copy of the License at 7e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 8e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * http://www.apache.org/licenses/LICENSE-2.0 9e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 10e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Unless required by applicable law or agreed to in writing, software 11e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS, 12e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * See the License for the specific language governing permissions and 14e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * limitations under the License. 15e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */ 16e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpackage okio; 17e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 18e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.File; 19e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.FileInputStream; 20e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.FileNotFoundException; 21e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.FileOutputStream; 22e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.IOException; 23e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.InputStream; 24b5f9076b16fcc41c3dad31aecfdcfd962a7a1f75Neil Fullerimport java.io.InterruptedIOException; 25e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.OutputStream; 26e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.net.Socket; 27b5f9076b16fcc41c3dad31aecfdcfd962a7a1f75Neil Fullerimport java.net.SocketTimeoutException; 28e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.logging.Level; 29e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.logging.Logger; 30e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 31e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static okio.Util.checkOffsetAndCount; 32e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 33e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller/** Essential APIs for working with Okio. */ 34e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpublic final class Okio { 35e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private static final Logger logger = Logger.getLogger(Okio.class.getName()); 36e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 37e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private Okio() { 38e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 39e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 40e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** 41e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Returns a new source that buffers reads from {@code source}. The returned 42e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * source will perform bulk reads into its in-memory buffer. Use this wherever 43e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * you read a source to get an ergonomic and efficient access to data. 44e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */ 45e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public static BufferedSource buffer(Source source) { 46e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (source == null) throw new IllegalArgumentException("source == null"); 47e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return new RealBufferedSource(source); 48e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 49e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 50e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** 51e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Returns a new sink that buffers writes to {@code sink}. The returned sink 52e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * will batch writes to {@code sink}. Use this wherever you write to a sink to 53e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * get an ergonomic and efficient access to data. 54e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */ 55e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public static BufferedSink buffer(Sink sink) { 56e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (sink == null) throw new IllegalArgumentException("sink == null"); 57e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return new RealBufferedSink(sink); 58e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 59e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 60e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** Returns a sink that writes to {@code out}. */ 6171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller public static Sink sink(OutputStream out) { 62e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return sink(out, new Timeout()); 63e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 64e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 65e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private static Sink sink(final OutputStream out, final Timeout timeout) { 66e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (out == null) throw new IllegalArgumentException("out == null"); 67e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (timeout == null) throw new IllegalArgumentException("timeout == null"); 68e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 69e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return new Sink() { 70e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public void write(Buffer source, long byteCount) throws IOException { 71e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller checkOffsetAndCount(source.size, 0, byteCount); 72e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller while (byteCount > 0) { 73e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller timeout.throwIfReached(); 74e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Segment head = source.head; 75e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int toCopy = (int) Math.min(byteCount, head.limit - head.pos); 76e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller out.write(head.data, head.pos, toCopy); 77e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 78e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller head.pos += toCopy; 79e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller byteCount -= toCopy; 80e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller source.size -= toCopy; 81e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 82e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (head.pos == head.limit) { 83e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller source.head = head.pop(); 84a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller SegmentPool.recycle(head); 85e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 86e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 87e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 88e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 89e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public void flush() throws IOException { 90e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller out.flush(); 91e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 92e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 93e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public void close() throws IOException { 94e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller out.close(); 95e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 96e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 97e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public Timeout timeout() { 98e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return timeout; 99e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 100e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 101e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public String toString() { 102e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return "sink(" + out + ")"; 103e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 104e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller }; 105e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 106e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 107e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** 108e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Returns a sink that writes to {@code socket}. Prefer this over {@link 109e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * #sink(OutputStream)} because this method honors timeouts. When the socket 110e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * write times out, the socket is asynchronously closed by a watchdog thread. 111e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */ 11271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller public static Sink sink(Socket socket) throws IOException { 113e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (socket == null) throw new IllegalArgumentException("socket == null"); 114e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller AsyncTimeout timeout = timeout(socket); 115e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Sink sink = sink(socket.getOutputStream(), timeout); 116e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return timeout.sink(sink); 117e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 118e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 119e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** Returns a source that reads from {@code in}. */ 12071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller public static Source source(InputStream in) { 121e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return source(in, new Timeout()); 122e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 123e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 124e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private static Source source(final InputStream in, final Timeout timeout) { 125e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (in == null) throw new IllegalArgumentException("in == null"); 126e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (timeout == null) throw new IllegalArgumentException("timeout == null"); 127e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 128e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return new Source() { 129e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public long read(Buffer sink, long byteCount) throws IOException { 130e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); 131a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (byteCount == 0) return 0; 13271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller try { 13371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller timeout.throwIfReached(); 13471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller Segment tail = sink.writableSegment(1); 13571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller int maxToCopy = (int) Math.min(byteCount, Segment.SIZE - tail.limit); 13671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller int bytesRead = in.read(tail.data, tail.limit, maxToCopy); 13771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller if (bytesRead == -1) return -1; 13871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller tail.limit += bytesRead; 13971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller sink.size += bytesRead; 14071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller return bytesRead; 14171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller } catch (AssertionError e) { 14271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller if (isAndroidGetsocknameError(e)) throw new IOException(e); 14371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller throw e; 14471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller } 145e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 146e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 147e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public void close() throws IOException { 148e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller in.close(); 149e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 150e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 151e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public Timeout timeout() { 152e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return timeout; 153e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 154e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 155e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public String toString() { 156e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return "source(" + in + ")"; 157e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 158e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller }; 159e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 160e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 161e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** Returns a source that reads from {@code file}. */ 162e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public static Source source(File file) throws FileNotFoundException { 163e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (file == null) throw new IllegalArgumentException("file == null"); 164e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return source(new FileInputStream(file)); 165e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 166e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 16736132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // ANDROID-BEGIN 16836132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // /** Returns a source that reads from {@code path}. */ 16936132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // @IgnoreJRERequirement // Should only be invoked on Java 7+. 17036132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // public static Source source(Path path, OpenOption... options) throws IOException { 17136132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // if (path == null) throw new IllegalArgumentException("path == null"); 17236132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // return source(Files.newInputStream(path, options)); 17336132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // } 17436132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // ANDROID-END 175e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 176e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** Returns a sink that writes to {@code file}. */ 177e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public static Sink sink(File file) throws FileNotFoundException { 178e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (file == null) throw new IllegalArgumentException("file == null"); 179e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return sink(new FileOutputStream(file)); 180e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 181e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 182e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** Returns a sink that appends to {@code file}. */ 183e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public static Sink appendingSink(File file) throws FileNotFoundException { 184e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (file == null) throw new IllegalArgumentException("file == null"); 185e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return sink(new FileOutputStream(file, true)); 186e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 187e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 18836132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // ANDROID-BEGIN 18936132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // /** Returns a sink that writes to {@code path}. */ 19036132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // @IgnoreJRERequirement // Should only be invoked on Java 7+. 19136132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // public static Sink sink(Path path, OpenOption... options) throws IOException { 19236132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // if (path == null) throw new IllegalArgumentException("path == null"); 19336132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // return sink(Files.newOutputStream(path, options)); 19436132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // } 19536132b3cd2c814e057c775adc821ae0fb12aeb4cNeil Fuller // ANDROID-END 196e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 197e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** 198e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Returns a source that reads from {@code socket}. Prefer this over {@link 199e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * #source(InputStream)} because this method honors timeouts. When the socket 200e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * read times out, the socket is asynchronously closed by a watchdog thread. 201e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */ 20271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller public static Source source(Socket socket) throws IOException { 203e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (socket == null) throw new IllegalArgumentException("socket == null"); 204e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller AsyncTimeout timeout = timeout(socket); 205e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Source source = source(socket.getInputStream(), timeout); 206e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return timeout.source(source); 207e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 208e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 209e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private static AsyncTimeout timeout(final Socket socket) { 210e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return new AsyncTimeout() { 211b5f9076b16fcc41c3dad31aecfdcfd962a7a1f75Neil Fuller @Override protected IOException newTimeoutException(IOException cause) { 212b5f9076b16fcc41c3dad31aecfdcfd962a7a1f75Neil Fuller InterruptedIOException ioe = new SocketTimeoutException("timeout"); 213b5f9076b16fcc41c3dad31aecfdcfd962a7a1f75Neil Fuller if (cause != null) { 214b5f9076b16fcc41c3dad31aecfdcfd962a7a1f75Neil Fuller ioe.initCause(cause); 215b5f9076b16fcc41c3dad31aecfdcfd962a7a1f75Neil Fuller } 216b5f9076b16fcc41c3dad31aecfdcfd962a7a1f75Neil Fuller return ioe; 217b5f9076b16fcc41c3dad31aecfdcfd962a7a1f75Neil Fuller } 218b5f9076b16fcc41c3dad31aecfdcfd962a7a1f75Neil Fuller 219e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override protected void timedOut() { 220e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller try { 221e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller socket.close(); 222e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (Exception e) { 223e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller logger.log(Level.WARNING, "Failed to close timed out socket " + socket, e); 22471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller } catch (AssertionError e) { 22571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller if (isAndroidGetsocknameError(e)) { 22671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller // Catch this exception due to a Firmware issue up to android 4.2.2 22771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller // https://code.google.com/p/android/issues/detail?id=54072 22871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller logger.log(Level.WARNING, "Failed to close timed out socket " + socket, e); 22971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller } else { 23071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller throw e; 23171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller } 232e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 233e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 234e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller }; 235e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 23671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller 23771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller /** 23871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller * Returns true if {@code e} is due to a firmware bug fixed after Android 4.2.2. 23971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller * https://code.google.com/p/android/issues/detail?id=54072 24071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller */ 24171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller private static boolean isAndroidGetsocknameError(AssertionError e) { 24271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller return e.getCause() != null && e.getMessage() != null 24371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller && e.getMessage().contains("getsockname failed"); 24471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller } 245e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller} 246