diff --git a/RELEASENOTES.md b/RELEASENOTES.md index ce94193ffd..82bdbec106 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -149,6 +149,8 @@ initialization vector used to encrypt the cache contents. * Add `Requirements.DEVICE_STORAGE_NOT_LOW`, which can be specified as a requirement to a `DownloadManager` for it to proceed with downloading. + * For failed downloads, propagate the `Exception` that caused the failure + to `DownloadManager.Listener.onDownloadChanged`. * Audio: * Add a sample count parameter to `MediaCodecRenderer.processOutputBuffer` and `AudioSink.handleBuffer` to allow batching multiple encoded frames diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java index 71b1eda7bf..3926ce3a11 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java @@ -20,6 +20,7 @@ import static com.google.android.exoplayer2.demo.DemoApplication.DOWNLOAD_NOTIFI import android.app.Notification; import android.content.Context; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.google.android.exoplayer2.offline.Download; import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadService; @@ -94,7 +95,8 @@ public class DemoDownloadService extends DownloadService { } @Override - public void onDownloadChanged(@NonNull DownloadManager manager, @NonNull Download download) { + public void onDownloadChanged( + DownloadManager downloadManager, Download download, @Nullable Exception finalException) { Notification notification; if (download.state == Download.STATE_COMPLETED) { notification = diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java index a36635acb0..7522ba0499 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java @@ -126,7 +126,9 @@ public class DownloadTracker { @Override public void onDownloadChanged( - @NonNull DownloadManager downloadManager, @NonNull Download download) { + @NonNull DownloadManager downloadManager, + @NonNull Download download, + @Nullable Exception finalException) { downloads.put(download.request.uri, download); for (Listener listener : listeners) { listener.onDownloadsChanged(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java index a45c95470e..12f8182980 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java @@ -93,8 +93,11 @@ public final class DownloadManager { * * @param downloadManager The reporting instance. * @param download The state of the download. + * @param finalException If the download is transitioning to {@link Download#STATE_FAILED}, this + * is the final exception that resulted in the failure. */ - default void onDownloadChanged(DownloadManager downloadManager, Download download) {} + default void onDownloadChanged( + DownloadManager downloadManager, Download download, @Nullable Exception finalException) {} /** * Called when a download is removed. @@ -614,7 +617,7 @@ public final class DownloadManager { } } else { for (Listener listener : listeners) { - listener.onDownloadChanged(this, updatedDownload); + listener.onDownloadChanged(this, updatedDownload, update.finalException); } } if (waitingForRequirementsChanged) { @@ -906,7 +909,8 @@ public final class DownloadManager { ArrayList updateList = new ArrayList<>(downloads); for (int i = 0; i < downloads.size(); i++) { DownloadUpdate update = - new DownloadUpdate(downloads.get(i), /* isRemove= */ false, updateList); + new DownloadUpdate( + downloads.get(i), /* isRemove= */ false, updateList, /* finalException= */ null); mainHandler.obtainMessage(MSG_DOWNLOAD_UPDATE, update).sendToTarget(); } syncTasks(); @@ -1073,9 +1077,9 @@ public final class DownloadManager { return; } - @Nullable Throwable finalError = task.finalError; - if (finalError != null) { - Log.e(TAG, "Task failed: " + task.request + ", " + isRemove, finalError); + @Nullable Exception finalException = task.finalException; + if (finalException != null) { + Log.e(TAG, "Task failed: " + task.request + ", " + isRemove, finalException); } Download download = @@ -1083,7 +1087,7 @@ public final class DownloadManager { switch (download.state) { case STATE_DOWNLOADING: Assertions.checkState(!isRemove); - onDownloadTaskStopped(download, finalError); + onDownloadTaskStopped(download, finalException); break; case STATE_REMOVING: case STATE_RESTARTING: @@ -1101,16 +1105,16 @@ public final class DownloadManager { syncTasks(); } - private void onDownloadTaskStopped(Download download, @Nullable Throwable finalError) { + private void onDownloadTaskStopped(Download download, @Nullable Exception finalException) { download = new Download( download.request, - finalError == null ? STATE_COMPLETED : STATE_FAILED, + finalException == null ? STATE_COMPLETED : STATE_FAILED, download.startTimeMs, /* updateTimeMs= */ System.currentTimeMillis(), download.contentLength, download.stopReason, - finalError == null ? FAILURE_REASON_NONE : FAILURE_REASON_UNKNOWN, + finalException == null ? FAILURE_REASON_NONE : FAILURE_REASON_UNKNOWN, download.progress); // The download is now in a terminal state, so should not be in the downloads list. downloads.remove(getDownloadIndex(download.request.id)); @@ -1121,7 +1125,8 @@ public final class DownloadManager { Log.e(TAG, "Failed to update index.", e); } DownloadUpdate update = - new DownloadUpdate(download, /* isRemove= */ false, new ArrayList<>(downloads)); + new DownloadUpdate( + download, /* isRemove= */ false, new ArrayList<>(downloads), finalException); mainHandler.obtainMessage(MSG_DOWNLOAD_UPDATE, update).sendToTarget(); } @@ -1139,7 +1144,11 @@ public final class DownloadManager { Log.e(TAG, "Failed to remove from database"); } DownloadUpdate update = - new DownloadUpdate(download, /* isRemove= */ true, new ArrayList<>(downloads)); + new DownloadUpdate( + download, + /* isRemove= */ true, + new ArrayList<>(downloads), + /* finalException= */ null); mainHandler.obtainMessage(MSG_DOWNLOAD_UPDATE, update).sendToTarget(); } } @@ -1194,7 +1203,11 @@ public final class DownloadManager { Log.e(TAG, "Failed to update index.", e); } DownloadUpdate update = - new DownloadUpdate(download, /* isRemove= */ false, new ArrayList<>(downloads)); + new DownloadUpdate( + download, + /* isRemove= */ false, + new ArrayList<>(downloads), + /* finalException= */ null); mainHandler.obtainMessage(MSG_DOWNLOAD_UPDATE, update).sendToTarget(); return download; } @@ -1252,7 +1265,7 @@ public final class DownloadManager { @Nullable private volatile InternalHandler internalHandler; private volatile boolean isCanceled; - @Nullable private Throwable finalError; + @Nullable private Exception finalException; private long contentLength; @@ -1317,8 +1330,10 @@ public final class DownloadManager { } } } - } catch (Throwable e) { - finalError = e; + } catch (InterruptedException e) { + // The task was canceled. Do nothing. + } catch (Exception e) { + finalException = e; } @Nullable Handler internalHandler = this.internalHandler; if (internalHandler != null) { @@ -1355,11 +1370,17 @@ public final class DownloadManager { public final Download download; public final boolean isRemove; public final List downloads; + @Nullable public final Exception finalException; - public DownloadUpdate(Download download, boolean isRemove, List downloads) { + public DownloadUpdate( + Download download, + boolean isRemove, + List downloads, + @Nullable Exception finalException) { this.download = download; this.isRemove = isRemove; this.downloads = downloads; + this.finalException = finalException; } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java index 1c980ca2ef..a0c08071db 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java @@ -974,7 +974,8 @@ public abstract class DownloadService extends Service { } @Override - public void onDownloadChanged(DownloadManager downloadManager, Download download) { + public void onDownloadChanged( + DownloadManager downloadManager, Download download, @Nullable Exception finalException) { if (downloadService != null) { downloadService.notifyDownloadChanged(download); } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java index c3d62fc53b..c9f6552b0a 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java @@ -100,7 +100,8 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen } @Override - public void onDownloadChanged(DownloadManager downloadManager, Download download) { + public void onDownloadChanged( + DownloadManager downloadManager, Download download, @Nullable Exception finalException) { if (download.state == Download.STATE_FAILED) { failureReason = download.failureReason; }