diff --git a/extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java b/extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java index ad795b7051..ddf503be92 100644 --- a/extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java +++ b/extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java @@ -1126,13 +1126,15 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource { if (message != null) { if (message.contains("net::ERR_INTERNET_DISCONNECTED")) { return PlaybackException.ERROR_CODE_IO_NETWORK_UNAVAILABLE; + } else if (message.contains("net::ERR_CONTENT_LENGTH_MISMATCH") + || message.contains("net::ERR_SOCKET_NOT_CONNECTED")) { + return PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_CLOSED; } } if (occurredWhileOpeningConnection) { return PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED; - } else { - return PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_CLOSED; } + return PlaybackException.ERROR_CODE_IO_UNSPECIFIED; } private static boolean isCompressed(UrlResponseInfo info) { diff --git a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java index 82aedb0cf8..9d5d1cc5a0 100644 --- a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java +++ b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java @@ -33,7 +33,6 @@ import com.google.android.exoplayer2.upstream.HttpUtil; import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; -import com.google.common.base.Ascii; import com.google.common.base.Predicate; import com.google.common.net.HttpHeaders; import java.io.IOException; @@ -289,17 +288,8 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource { responseBody = Assertions.checkNotNull(response.body()); responseByteStream = responseBody.byteStream(); } catch (IOException e) { - @Nullable String message = e.getMessage(); - if (message != null - && Ascii.toLowerCase(message).matches("cleartext communication.*not permitted.*")) { - throw new CleartextNotPermittedException(e, dataSpec); - } - throw new HttpDataSourceException( - "Unable to connect", - e, - dataSpec, - PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED, - HttpDataSourceException.TYPE_OPEN); + throw HttpDataSourceException.createForIOException( + e, dataSpec, HttpDataSourceException.TYPE_OPEN); } int responseCode = response.code(); @@ -372,11 +362,8 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource { try { return readInternal(buffer, offset, length); } catch (IOException e) { - throw new HttpDataSourceException( - e, - Assertions.checkNotNull(dataSpec), - PlaybackException.ERROR_CODE_IO_UNSPECIFIED, - HttpDataSourceException.TYPE_READ); + throw HttpDataSourceException.createForIOException( + e, castNonNull(dataSpec), HttpDataSourceException.TYPE_READ); } } diff --git a/library/common/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java b/library/common/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java index cbc5dc044d..d2c6b5e993 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java @@ -28,7 +28,6 @@ import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.upstream.DataSpec.HttpMethod; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Util; -import com.google.common.base.Ascii; import com.google.common.base.Predicate; import com.google.common.net.HttpHeaders; import java.io.IOException; @@ -347,35 +346,17 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou bytesToRead = 0; transferInitializing(dataSpec); - try { - connection = makeConnection(dataSpec); - } catch (IOException e) { - @Nullable String message = e.getMessage(); - if (message != null - && Ascii.toLowerCase(message).matches("cleartext http traffic.*not permitted.*")) { - throw new CleartextNotPermittedException(e, dataSpec); - } - throw new HttpDataSourceException( - "Unable to connect", - e, - dataSpec, - PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED, - HttpDataSourceException.TYPE_OPEN); - } - - HttpURLConnection connection = this.connection; String responseMessage; + HttpURLConnection connection; try { + this.connection = makeConnection(dataSpec); + connection = this.connection; responseCode = connection.getResponseCode(); responseMessage = connection.getResponseMessage(); } catch (IOException e) { closeConnectionQuietly(); - throw new HttpDataSourceException( - "Unable to connect", - e, - dataSpec, - PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED, - HttpDataSourceException.TYPE_OPEN); + throw HttpDataSourceException.createForIOException( + e, dataSpec, HttpDataSourceException.TYPE_OPEN); } // Check for a valid response code. @@ -481,11 +462,8 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou try { return readInternal(buffer, offset, length); } catch (IOException e) { - throw new HttpDataSourceException( - e, - castNonNull(dataSpec), - PlaybackException.ERROR_CODE_IO_UNSPECIFIED, - HttpDataSourceException.TYPE_READ); + throw HttpDataSourceException.createForIOException( + e, castNonNull(dataSpec), HttpDataSourceException.TYPE_READ); } } diff --git a/library/common/src/main/java/com/google/android/exoplayer2/upstream/HttpDataSource.java b/library/common/src/main/java/com/google/android/exoplayer2/upstream/HttpDataSource.java index 7566aa8f85..5d590c3308 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/upstream/HttpDataSource.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/upstream/HttpDataSource.java @@ -26,6 +26,8 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -207,6 +209,36 @@ public interface HttpDataSource extends DataSource { /** The error occurred in closing a {@code HttpDataSource}. */ public static final int TYPE_CLOSE = 3; + /** + * Returns a {@code HttpDataSourceException} whose error code is assigned according to the cause + * and type. + */ + public static HttpDataSourceException createForIOException( + IOException cause, DataSpec dataSpec, @Type int type) { + @PlaybackException.ErrorCode int errorCode = PlaybackException.ERROR_CODE_IO_UNSPECIFIED; + if (cause instanceof SocketTimeoutException) { + errorCode = PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT; + } else if (cause instanceof UnknownHostException) { + errorCode = PlaybackException.ERROR_CODE_IO_DNS_FAILED; + } else { + @Nullable String message = cause.getMessage(); + if (message != null) { + if (Ascii.toLowerCase(message).matches("cleartext.*not permitted.*")) { + errorCode = PlaybackException.ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED; + } else if (message.contains("unexpected end of stream")) { + errorCode = PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_CLOSED; + } + } + + if (type == TYPE_OPEN && errorCode == PlaybackException.ERROR_CODE_IO_UNSPECIFIED) { + errorCode = PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED; + } + } + return errorCode == PlaybackException.ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED + ? new CleartextNotPermittedException(cause, dataSpec) + : new HttpDataSourceException(cause, dataSpec, errorCode, type); + } + /** The {@link DataSpec} associated with the current connection. */ public final DataSpec dataSpec;