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