diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 59cac86f2a6..7c87e71a8e1 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -89,6 +89,7 @@ * Data sources: * Rename `DummyDataSource` to `PlaceHolderDataSource`. + * Workaround OkHttp interrupt handling. * Remove deprecated symbols: * Remove `Player.Listener.onTracksChanged`. Use `Player.Listener.onTracksInfoChanged` instead. diff --git a/libraries/datasource_okhttp/src/main/java/androidx/media3/datasource/okhttp/OkHttpDataSource.java b/libraries/datasource_okhttp/src/main/java/androidx/media3/datasource/okhttp/OkHttpDataSource.java index 42ed4225509..aaa3fe87663 100644 --- a/libraries/datasource_okhttp/src/main/java/androidx/media3/datasource/okhttp/OkHttpDataSource.java +++ b/libraries/datasource_okhttp/src/main/java/androidx/media3/datasource/okhttp/OkHttpDataSource.java @@ -32,10 +32,14 @@ import androidx.media3.datasource.DataSourceException; import androidx.media3.datasource.DataSpec; import androidx.media3.datasource.HttpDataSource; +import androidx.media3.datasource.HttpDataSource.HttpDataSourceException; +import androidx.media3.datasource.HttpDataSource.InvalidContentTypeException; +import androidx.media3.datasource.HttpDataSource.InvalidResponseCodeException; import androidx.media3.datasource.HttpUtil; import androidx.media3.datasource.TransferListener; import com.google.common.base.Predicate; import com.google.common.net.HttpHeaders; +import com.google.common.util.concurrent.SettableFuture; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; @@ -43,8 +47,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutionException; import okhttp3.CacheControl; import okhttp3.Call; +import okhttp3.Callback; import okhttp3.HttpUrl; import okhttp3.MediaType; import okhttp3.OkHttpClient; @@ -299,8 +305,9 @@ public long open(DataSpec dataSpec) throws HttpDataSourceException { Request request = makeRequest(dataSpec); Response response; ResponseBody responseBody; + Call call = callFactory.newCall(request); try { - this.response = callFactory.newCall(request).execute(); + this.response = executeCall(call); response = this.response; responseBody = Assertions.checkNotNull(response.body()); responseByteStream = responseBody.byteStream(); @@ -448,6 +455,35 @@ private Request makeRequest(DataSpec dataSpec) throws HttpDataSourceException { return builder.build(); } + /** + * This method is an interrupt safe replacement of OkHttp Call.execute() which can get in bad + * states if interrupted while writing to the shared connection socket. + */ + private Response executeCall(Call call) throws IOException { + SettableFuture future = SettableFuture.create(); + call.enqueue( + new Callback() { + @Override + public void onFailure(Call call, IOException e) { + future.setException(e); + } + + @Override + public void onResponse(Call call, Response response) { + future.set(response); + } + }); + + try { + return future.get(); + } catch (InterruptedException e) { + call.cancel(); + throw new InterruptedIOException(); + } catch (ExecutionException ee) { + throw new IOException(ee); + } + } + /** * Attempts to skip the specified number of bytes in full. *