diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java index 2679e634f2..519db874b8 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java @@ -174,6 +174,7 @@ public final class DownloadManager { private static final int MSG_RELEASE = 12; private static final String TAG = "DownloadManager"; + private static final int MAX_REMOVE_TASK_COUNT = 1; private final Context context; private final WritableDownloadIndex downloadIndex; @@ -709,6 +710,7 @@ public final class DownloadManager { private int maxParallelDownloads; private int minRetryCount; private int activeDownloadTaskCount; + private int activeRemoveTaskCount; public InternalHandler( HandlerThread thread, @@ -1060,6 +1062,10 @@ public final class DownloadManager { return; } + if (activeRemoveTaskCount >= MAX_REMOVE_TASK_COUNT) { + return; + } + // We can start a remove task. Downloader downloader = downloaderFactory.createDownloader(download.request); activeTask = @@ -1071,6 +1077,7 @@ public final class DownloadManager { minRetryCount, /* internalHandler= */ this); activeTasks.put(download.request.id, activeTask); + activeRemoveTaskCount++; activeTask.start(); } @@ -1100,7 +1107,9 @@ public final class DownloadManager { activeTasks.remove(downloadId); boolean isRemove = task.isRemove; - if (!isRemove && --activeDownloadTaskCount == 0) { + if (isRemove) { + activeRemoveTaskCount--; + } else if (--activeDownloadTaskCount == 0) { removeMessages(MSG_UPDATE_PROGRESS); } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/DownloadManagerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/DownloadManagerTest.java index 9cce8afbed..3cd1dcd83c 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/DownloadManagerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/DownloadManagerTest.java @@ -713,7 +713,61 @@ public class DownloadManagerTest { assertEqualIgnoringUpdateTime(mergedDownload, expectedDownload); } + @Test + public void remove_tasks_run_sequentially() + throws Throwable { + DefaultDownloadIndex defaultDownloadIndex = + new DefaultDownloadIndex(TestUtil.getInMemoryDatabaseProvider()); + defaultDownloadIndex.putDownload( + new Download( + new DownloadRequest.Builder(ID1, Uri.EMPTY).build(), + Download.STATE_REMOVING, + 0, + 1, + 2, + Download.STOP_REASON_NONE, + Download.FAILURE_REASON_NONE + ) + ); + defaultDownloadIndex.putDownload( + new Download( + new DownloadRequest.Builder(ID2, Uri.EMPTY).build(), + Download.STATE_RESTARTING, + 0, + 1, + 2, + Download.STOP_REASON_NONE, + Download.FAILURE_REASON_NONE + ) + ); + setupDownloadManager(100, defaultDownloadIndex); + + // The second removal should wait and the first one should be able to complete. + FakeDownloader downloader0 = getDownloaderAt(0); + assertNoDownloaderAt(1); + downloader0.assertId(ID1); + downloader0.assertRemoveStarted(); + downloader0.finish(); + assertRemoved(ID1); + + // The second removal can start once the first one has completed, and removes a download with + // state STATE_RESTARTING, so it should result in a new download being queued. + FakeDownloader downloader1 = getDownloaderAt(1); + downloader1.assertId(ID2); + downloader1.assertRemoveStarted(); + downloader1.finish(); + assertQueued(ID2); + } + private void setupDownloadManager(int maxParallelDownloads) throws Exception { + setupDownloadManager( + maxParallelDownloads, new DefaultDownloadIndex(TestUtil.getInMemoryDatabaseProvider()) + ); + } + + private void setupDownloadManager( + int maxParallelDownloads, WritableDownloadIndex writableDownloadIndex + ) throws Exception { if (downloadManager != null) { releaseDownloadManager(); } @@ -723,7 +777,7 @@ public class DownloadManagerTest { downloadManager = new DownloadManager( ApplicationProvider.getApplicationContext(), - new DefaultDownloadIndex(TestUtil.getInMemoryDatabaseProvider()), + writableDownloadIndex, new FakeDownloaderFactory()); downloadManager.setMaxParallelDownloads(maxParallelDownloads); downloadManager.setMinRetryCount(MIN_RETRY_COUNT);