Simplify the error code handling.

PiperOrigin-RevId: 387786273
This commit is contained in:
claincly 2021-07-30 13:43:22 +01:00 committed by Christos Tsilopoulos
parent f19a3674f3
commit df49f90b7f
5 changed files with 160 additions and 101 deletions

View File

@ -358,16 +358,10 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
transferStarted(dataSpec);
try {
if (!skipFully(bytesToSkip)) {
throw new DataSourceException(PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
}
} catch (IOException e) {
skipFully(bytesToSkip, dataSpec);
} catch (HttpDataSourceException e) {
closeConnectionQuietly();
throw new HttpDataSourceException(
e,
dataSpec,
PlaybackException.ERROR_CODE_IO_UNSPECIFIED,
HttpDataSourceException.TYPE_OPEN);
throw e;
}
return bytesToRead;
@ -452,16 +446,17 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
* Attempts to skip the specified number of bytes in full.
*
* @param bytesToSkip The number of bytes to skip.
* @throws InterruptedIOException If the thread is interrupted during the operation.
* @throws IOException If an error occurs reading from the source.
* @return Whether the bytes were skipped in full. If {@code false} then the data ended before the
* specified number of bytes were skipped. Always {@code true} if {@code bytesToSkip == 0}.
* @param dataSpec The {@link DataSpec}.
* @throws HttpDataSourceException If the thread is interrupted during the operation, or an error
* occurs while reading from the source, or if the data ended before skipping the specified
* number of bytes.
*/
private boolean skipFully(long bytesToSkip) throws IOException {
private void skipFully(long bytesToSkip, DataSpec dataSpec) throws HttpDataSourceException {
if (bytesToSkip == 0) {
return true;
return;
}
byte[] skipBuffer = new byte[4096];
try {
while (bytesToSkip > 0) {
int readLength = (int) min(bytesToSkip, skipBuffer.length);
int read = castNonNull(responseByteStream).read(skipBuffer, 0, readLength);
@ -469,12 +464,25 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
throw new InterruptedIOException();
}
if (read == -1) {
return false;
throw new HttpDataSourceException(
dataSpec,
PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE,
HttpDataSourceException.TYPE_OPEN);
}
bytesToSkip -= read;
bytesTransferred(read);
}
return true;
return;
} catch (IOException e) {
if (e instanceof HttpDataSourceException) {
throw (HttpDataSourceException) e;
} else {
throw new HttpDataSourceException(
dataSpec,
PlaybackException.ERROR_CODE_IO_UNSPECIFIED,
HttpDataSourceException.TYPE_OPEN);
}
}
}
/**

View File

@ -37,8 +37,8 @@ import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.NoRouteToHostException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
@ -459,11 +459,13 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
transferStarted(dataSpec);
try {
if (!skipFully(bytesToSkip)) {
throw new DataSourceException(PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
}
skipFully(bytesToSkip, dataSpec);
} catch (IOException e) {
closeConnectionQuietly();
if (e instanceof HttpDataSourceException) {
throw (HttpDataSourceException) e;
}
throw new HttpDataSourceException(
e,
dataSpec,
@ -562,7 +564,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|| responseCode == HTTP_STATUS_TEMPORARY_REDIRECT
|| responseCode == HTTP_STATUS_PERMANENT_REDIRECT)) {
connection.disconnect();
url = handleRedirect(url, location);
url = handleRedirect(url, location, dataSpec);
} else if (httpMethod == DataSpec.HTTP_METHOD_POST
&& (responseCode == HttpURLConnection.HTTP_MULT_CHOICE
|| responseCode == HttpURLConnection.HTTP_MOVED_PERM
@ -576,14 +578,18 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
httpMethod = DataSpec.HTTP_METHOD_GET;
httpBody = null;
}
url = handleRedirect(url, location);
url = handleRedirect(url, location, dataSpec);
} else {
return connection;
}
}
// If we get here we've been redirected more times than are permitted.
throw new NoRouteToHostException("Too many redirects: " + redirectCount);
throw new HttpDataSourceException(
new NoRouteToHostException("Too many redirects: " + redirectCount),
dataSpec,
PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED,
HttpDataSourceException.TYPE_OPEN);
}
/**
@ -658,27 +664,50 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
*
* @param originalUrl The original URL.
* @param location The Location header in the response. May be {@code null}.
* @param dataSpec The {@link DataSpec}.
* @return The next URL.
* @throws IOException If redirection isn't possible.
* @throws HttpDataSourceException If redirection isn't possible.
*/
private URL handleRedirect(URL originalUrl, @Nullable String location) throws IOException {
private URL handleRedirect(URL originalUrl, @Nullable String location, DataSpec dataSpec)
throws HttpDataSourceException {
if (location == null) {
throw new ProtocolException("Null location redirect");
throw new HttpDataSourceException(
"Null location redirect",
dataSpec,
PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED,
HttpDataSourceException.TYPE_OPEN);
}
// Form the new url.
URL url = new URL(originalUrl, location);
URL url;
try {
url = new URL(originalUrl, location);
} catch (MalformedURLException e) {
throw new HttpDataSourceException(
e,
dataSpec,
PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED,
HttpDataSourceException.TYPE_OPEN);
}
// Check that the protocol of the new url is supported.
String protocol = url.getProtocol();
if (!"https".equals(protocol) && !"http".equals(protocol)) {
throw new ProtocolException("Unsupported protocol redirect: " + protocol);
throw new HttpDataSourceException(
"Unsupported protocol redirect: " + protocol,
dataSpec,
PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED,
HttpDataSourceException.TYPE_OPEN);
}
if (!allowCrossProtocolRedirects && !protocol.equals(originalUrl.getProtocol())) {
throw new ProtocolException(
throw new HttpDataSourceException(
"Disallowed cross-protocol redirect ("
+ originalUrl.getProtocol()
+ " to "
+ protocol
+ ")");
+ ")",
dataSpec,
PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED,
HttpDataSourceException.TYPE_OPEN);
}
return url;
}
@ -687,29 +716,34 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
* Attempts to skip the specified number of bytes in full.
*
* @param bytesToSkip The number of bytes to skip.
* @throws InterruptedIOException If the thread is interrupted during the operation.
* @throws IOException If an error occurs reading from the source.
* @return Whether the bytes were skipped in full. If {@code false} then the data ended before the
* specified number of bytes were skipped. Always {@code true} if {@code bytesToSkip == 0}.
* @param dataSpec The {@link DataSpec}.
* @throws IOException If the thread is interrupted during the operation, or if the data ended
* before skipping the specified number of bytes.
*/
private boolean skipFully(long bytesToSkip) throws IOException {
private void skipFully(long bytesToSkip, DataSpec dataSpec) throws IOException {
if (bytesToSkip == 0) {
return true;
return;
}
byte[] skipBuffer = new byte[4096];
while (bytesToSkip > 0) {
int readLength = (int) min(bytesToSkip, skipBuffer.length);
int read = castNonNull(inputStream).read(skipBuffer, 0, readLength);
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedIOException();
throw new HttpDataSourceException(
new InterruptedIOException(),
dataSpec,
PlaybackException.ERROR_CODE_IO_UNSPECIFIED,
HttpDataSourceException.TYPE_OPEN);
}
if (read == -1) {
return false;
throw new HttpDataSourceException(
dataSpec,
PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE,
HttpDataSourceException.TYPE_OPEN);
}
bytesToSkip -= read;
bytesTransferred(read);
}
return true;
}
/**

View File

@ -83,7 +83,7 @@ public final class AssetDataSource extends BaseDataSource {
// assetManager.open() returns an AssetInputStream, whose skip() implementation only skips
// fewer bytes than requested if the skip is beyond the end of the asset's data.
throw new AssetDataSourceException(
/* cause=*/ null, PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
/* cause= */ null, PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
}
if (dataSpec.length != C.LENGTH_UNSET) {
bytesRemaining = dataSpec.length;

View File

@ -39,17 +39,12 @@ public final class ContentDataSource extends BaseDataSource {
/** @deprecated Use {@link #ContentDataSourceException(IOException, int)}. */
@Deprecated
public ContentDataSourceException(IOException cause) {
super(cause, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
this(cause, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
}
/**
* Creates a new instance.
*
* @param cause The error cause.
* @param errorCode See {@link PlaybackException.ErrorCode}.
*/
/** Creates a new instance. */
public ContentDataSourceException(
IOException cause, @PlaybackException.ErrorCode int errorCode) {
@Nullable IOException cause, @PlaybackException.ErrorCode int errorCode) {
super(cause, errorCode);
}
}
@ -78,7 +73,10 @@ public final class ContentDataSource extends BaseDataSource {
AssetFileDescriptor assetFileDescriptor = resolver.openAssetFileDescriptor(uri, "r");
this.assetFileDescriptor = assetFileDescriptor;
if (assetFileDescriptor == null) {
throw new FileNotFoundException("Could not open file descriptor for: " + uri);
// openAssetFileDescriptor returns null if the provider recently crashed.
throw new ContentDataSourceException(
new IOException("Could not open file descriptor for: " + uri),
PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
}
long assetFileDescriptorLength = assetFileDescriptor.getLength();
@ -93,7 +91,8 @@ public final class ContentDataSource extends BaseDataSource {
// file.
if (assetFileDescriptorLength != AssetFileDescriptor.UNKNOWN_LENGTH
&& dataSpec.position > assetFileDescriptorLength) {
throw new DataSourceException(PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
throw new ContentDataSourceException(
/* cause= */ null, PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
}
long assetFileDescriptorOffset = assetFileDescriptor.getStartOffset();
long skipped =
@ -102,7 +101,8 @@ public final class ContentDataSource extends BaseDataSource {
if (skipped != dataSpec.position) {
// We expect the skip to be satisfied in full. If it isn't then we're probably trying to
// read beyond the end of the last resource in the file.
throw new DataSourceException(PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
throw new ContentDataSourceException(
/* cause= */ null, PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
}
if (assetFileDescriptorLength == AssetFileDescriptor.UNKNOWN_LENGTH) {
// The asset must extend to the end of the file. We can try and resolve the length with
@ -115,18 +115,25 @@ public final class ContentDataSource extends BaseDataSource {
bytesRemaining = channelSize - channel.position();
if (bytesRemaining < 0) {
// The skip above was satisfied in full, but skipped beyond the end of the file.
throw new DataSourceException(
PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
throw new ContentDataSourceException(
/* cause= */ null, PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
}
}
} else {
bytesRemaining = assetFileDescriptorLength - skipped;
if (bytesRemaining < 0) {
throw new DataSourceException(PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
throw new ContentDataSourceException(
/* cause= */ null, PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
}
}
} catch (ContentDataSourceException e) {
throw e;
} catch (IOException e) {
throw new ContentDataSourceException(e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
throw new ContentDataSourceException(
e,
e instanceof FileNotFoundException
? PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND
: PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
}
if (dataSpec.length != C.LENGTH_UNSET) {

View File

@ -56,37 +56,24 @@ public final class RawResourceDataSource extends BaseDataSource {
/** Thrown when an {@link IOException} is encountered reading from a raw resource. */
public static class RawResourceDataSourceException extends DataSourceException {
/** @deprecated Use {@link #RawResourceDataSourceException(String, int)}. */
/** @deprecated Use {@link #RawResourceDataSourceException(String, Throwable, int)}. */
@Deprecated
public RawResourceDataSourceException(String message) {
super(message, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
super(message, /* cause= */ null, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
}
/**
* Creates a new instance.
*
* @param message The error message.
* @param errorCode See {@link PlaybackException.ErrorCode}.
*/
public RawResourceDataSourceException(
String message, @PlaybackException.ErrorCode int errorCode) {
super(message, errorCode);
}
/** @deprecated Use {@link #RawResourceDataSourceException(Throwable, int)}. */
/** @deprecated Use {@link #RawResourceDataSourceException(String, Throwable, int)}. */
@Deprecated
public RawResourceDataSourceException(Throwable e) {
super(e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
public RawResourceDataSourceException(Throwable cause) {
super(cause, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
}
/**
* Creates a new instance.
*
* @param e The error cause.
* @param errorCode See {@link PlaybackException.ErrorCode}.
*/
public RawResourceDataSourceException(Throwable e, @PlaybackException.ErrorCode int errorCode) {
super(e, errorCode);
/** Creates a new instance. */
public RawResourceDataSourceException(
@Nullable String message,
@Nullable Throwable cause,
@PlaybackException.ErrorCode int errorCode) {
super(message, cause, errorCode);
}
}
@ -134,7 +121,8 @@ public final class RawResourceDataSource extends BaseDataSource {
} catch (NumberFormatException e) {
throw new RawResourceDataSourceException(
"Resource identifier must be an integer.",
PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND);
/* cause= */ null,
PlaybackException.ERROR_CODE_FAILED_RUNTIME_CHECK);
}
} else if (TextUtils.equals(ContentResolver.SCHEME_ANDROID_RESOURCE, uri.getScheme())) {
String path = Assertions.checkNotNull(uri.getPath());
@ -148,7 +136,9 @@ public final class RawResourceDataSource extends BaseDataSource {
resourceName, /* defType= */ "raw", /* defPackage= */ packageName);
if (resourceId == 0) {
throw new RawResourceDataSourceException(
"Resource not found.", PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND);
"Resource not found.",
/* cause= */ null,
PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND);
}
} else {
throw new RawResourceDataSourceException(
@ -156,7 +146,8 @@ public final class RawResourceDataSource extends BaseDataSource {
+ RAW_RESOURCE_SCHEME
+ " or "
+ ContentResolver.SCHEME_ANDROID_RESOURCE,
PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND);
/* cause= */ null,
PlaybackException.ERROR_CODE_FAILED_RUNTIME_CHECK);
}
transferInitializing(dataSpec);
@ -165,13 +156,16 @@ public final class RawResourceDataSource extends BaseDataSource {
try {
assetFileDescriptor = resources.openRawResourceFd(resourceId);
} catch (Resources.NotFoundException e) {
throw new RawResourceDataSourceException(e, PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND);
throw new RawResourceDataSourceException(
/* message= */ null, e, PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND);
}
this.assetFileDescriptor = assetFileDescriptor;
if (assetFileDescriptor == null) {
throw new RawResourceDataSourceException(
"Resource is compressed: " + uri, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
"Resource is compressed: " + uri,
/* cause= */ null,
PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
}
long assetFileDescriptorLength = assetFileDescriptor.getLength();
@ -187,7 +181,10 @@ public final class RawResourceDataSource extends BaseDataSource {
// extends to the end of the file.
if (assetFileDescriptorLength != AssetFileDescriptor.UNKNOWN_LENGTH
&& dataSpec.position > assetFileDescriptorLength) {
throw new DataSourceException(PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
throw new RawResourceDataSourceException(
/* message= */ null,
/* cause= */ null,
PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
}
long assetFileDescriptorOffset = assetFileDescriptor.getStartOffset();
long skipped =
@ -196,7 +193,10 @@ public final class RawResourceDataSource extends BaseDataSource {
if (skipped != dataSpec.position) {
// We expect the skip to be satisfied in full. If it isn't then we're probably trying to
// read beyond the end of the last resource in the file.
throw new DataSourceException(PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
throw new RawResourceDataSourceException(
/* message= */ null,
/* cause= */ null,
PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
}
if (assetFileDescriptorLength == AssetFileDescriptor.UNKNOWN_LENGTH) {
// The asset must extend to the end of the file. We can try and resolve the length with
@ -208,7 +208,9 @@ public final class RawResourceDataSource extends BaseDataSource {
bytesRemaining = channel.size() - channel.position();
if (bytesRemaining < 0) {
// The skip above was satisfied in full, but skipped beyond the end of the file.
throw new DataSourceException(
throw new RawResourceDataSourceException(
/* message= */ null,
/* cause= */ null,
PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
}
}
@ -218,8 +220,11 @@ public final class RawResourceDataSource extends BaseDataSource {
throw new DataSourceException(PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
}
}
} catch (RawResourceDataSourceException e) {
throw e;
} catch (IOException e) {
throw new RawResourceDataSourceException(e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
throw new RawResourceDataSourceException(
/* message= */ null, e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
}
if (dataSpec.length != C.LENGTH_UNSET) {
@ -245,14 +250,17 @@ public final class RawResourceDataSource extends BaseDataSource {
bytesRemaining == C.LENGTH_UNSET ? length : (int) min(bytesRemaining, length);
bytesRead = castNonNull(inputStream).read(buffer, offset, bytesToRead);
} catch (IOException e) {
throw new RawResourceDataSourceException(e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
throw new RawResourceDataSourceException(
/* message= */ null, e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
}
if (bytesRead == -1) {
if (bytesRemaining != C.LENGTH_UNSET) {
// End of stream reached having not read sufficient data.
throw new RawResourceDataSourceException(
new EOFException(), PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
"End of stream reached having not read sufficient data.",
new EOFException(),
PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
}
return C.RESULT_END_OF_INPUT;
}
@ -278,7 +286,8 @@ public final class RawResourceDataSource extends BaseDataSource {
inputStream.close();
}
} catch (IOException e) {
throw new RawResourceDataSourceException(e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
throw new RawResourceDataSourceException(
/* message= */ null, e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
} finally {
inputStream = null;
try {
@ -286,7 +295,8 @@ public final class RawResourceDataSource extends BaseDataSource {
assetFileDescriptor.close();
}
} catch (IOException e) {
throw new RawResourceDataSourceException(e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
throw new RawResourceDataSourceException(
/* message= */ null, e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
} finally {
assetFileDescriptor = null;
if (opened) {