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 d2adb1514d..24d7993141 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 @@ -1019,7 +1019,7 @@ public final class DownloadManager { // Cancel the downloading task. activeTask.cancel(/* released= */ false); } - // The activeTask is either a remove task, or a downloading task that we just cancelled. In + // The activeTask is either a remove task, or a downloading task that we just canceled. In // the latter case we need to wait for the task to stop before we start a remove task. return; } @@ -1284,6 +1284,7 @@ public final class DownloadManager { if (!isCanceled) { isCanceled = true; downloader.cancel(); + // TODO - This will need propagating deeper as soon as we start using additional threads. interrupt(); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/Downloader.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/Downloader.java index 53485aced6..98079bf200 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/Downloader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/Downloader.java @@ -46,19 +46,13 @@ public interface Downloader { * * @param progressListener A listener to receive progress updates, or {@code null}. * @throws DownloadException Thrown if the content cannot be downloaded. - * @throws InterruptedException If the thread has been interrupted. - * @throws IOException Thrown when there is an io error while downloading. + * @throws IOException If the download did not complete successfully. */ - void download(@Nullable ProgressListener progressListener) - throws InterruptedException, IOException; + void download(@Nullable ProgressListener progressListener) throws IOException; /** Cancels the download operation and prevents future download operations from running. */ void cancel(); - /** - * Removes the content. - * - * @throws InterruptedException Thrown if the thread was interrupted. - */ - void remove() throws InterruptedException; + /** Removes the content. */ + void remove(); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloader.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloader.java index ecdd1748bb..ad3f901653 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloader.java @@ -73,8 +73,7 @@ public final class ProgressiveDownloader implements Downloader { } @Override - public void download(@Nullable ProgressListener progressListener) - throws InterruptedException, IOException { + public void download(@Nullable ProgressListener progressListener) throws IOException { @Nullable PriorityTaskManager priorityTaskManager = dataSource.getUpstreamPriorityTaskManager(); if (priorityTaskManager != null) { priorityTaskManager.add(C.PRIORITY_DOWNLOAD); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/SegmentDownloader.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/SegmentDownloader.java index 56f85c2d53..b332b85a4d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/SegmentDownloader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/SegmentDownloader.java @@ -101,16 +101,8 @@ public abstract class SegmentDownloader> impleme isCanceled = new AtomicBoolean(); } - /** - * Downloads the selected streams in the media. If multiple streams are selected, they are - * downloaded in sync with one another. - * - * @throws IOException Thrown when there is an error downloading. - * @throws InterruptedException If the thread has been interrupted. - */ @Override - public final void download(@Nullable ProgressListener progressListener) - throws IOException, InterruptedException { + public final void download(@Nullable ProgressListener progressListener) throws IOException { @Nullable PriorityTaskManager priorityTaskManager = cacheDataSourceFactory.getUpstreamPriorityTaskManager(); @@ -194,7 +186,7 @@ public abstract class SegmentDownloader> impleme } @Override - public final void remove() throws InterruptedException { + public final void remove() { Cache cache = Assertions.checkNotNull(cacheDataSourceFactory.getCache()); CacheKeyFactory cacheKeyFactory = cacheDataSourceFactory.getCacheKeyFactory(); CacheDataSource dataSource = cacheDataSourceFactory.createDataSourceForRemovingDownload(); @@ -235,13 +227,11 @@ public abstract class SegmentDownloader> impleme * segments from being listed. If true then a partial segment list will be returned. If false * an {@link IOException} will be thrown. * @return The list of downloadable {@link Segment}s. - * @throws InterruptedException Thrown if the thread was interrupted. * @throws IOException Thrown if {@code allowPartialIndex} is false and a load error occurs, or if * the media is not in a form that allows for its segments to be listed. */ protected abstract List getSegments( - DataSource dataSource, M manifest, boolean allowIncompleteList) - throws InterruptedException, IOException; + DataSource dataSource, M manifest, boolean allowIncompleteList) throws IOException; protected static DataSpec getCompressibleDataSpec(Uri uri) { return new DataSpec.Builder().setUri(uri).setFlags(DataSpec.FLAG_ALLOW_GZIP).build(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java index 5d99335e77..f19b818e1a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java @@ -28,6 +28,7 @@ import com.google.android.exoplayer2.util.PriorityTaskManager; import com.google.android.exoplayer2.util.Util; import java.io.EOFException; import java.io.IOException; +import java.io.InterruptedIOException; import java.util.NavigableSet; import java.util.concurrent.atomic.AtomicBoolean; @@ -105,15 +106,17 @@ public final class CacheUtil { * Caches the data defined by {@code dataSpec}, skipping already cached data. Caching stops early * if the end of the input is reached. * + *

To cancel the operation, the caller should both set {@code isCanceled} to true and interrupt + * the calling thread. + * *

This method may be slow and shouldn't normally be called on the main thread. * * @param cache A {@link Cache} to store the data. * @param dataSpec Defines the data to be cached. * @param upstreamDataSource A {@link DataSource} for reading data not in the cache. * @param progressListener A listener to receive progress updates, or {@code null}. - * @param isCanceled An optional flag that will interrupt caching if set to true. - * @throws IOException If an error occurs reading from the source. - * @throws InterruptedException If the thread was interrupted directly or via {@code isCanceled}. + * @param isCanceled An optional flag that will cancel the operation if set to true. + * @throws IOException If an error occurs caching the data, or if the operation was canceled. */ @WorkerThread public static void cache( @@ -122,7 +125,7 @@ public final class CacheUtil { DataSource upstreamDataSource, @Nullable ProgressListener progressListener, @Nullable AtomicBoolean isCanceled) - throws IOException, InterruptedException { + throws IOException { cache( new CacheDataSource(cache, upstreamDataSource), dataSpec, @@ -140,17 +143,19 @@ public final class CacheUtil { * calling code to call {@link PriorityTaskManager#add} to register with the manager before * calling this method, and to call {@link PriorityTaskManager#remove} afterwards to unregister. * + *

To cancel the operation, the caller should both set {@code isCanceled} to true and interrupt + * the calling thread. + * *

This method may be slow and shouldn't normally be called on the main thread. * * @param dataSource A {@link CacheDataSource} to be used for caching the data. * @param dataSpec Defines the data to be cached. * @param progressListener A listener to receive progress updates, or {@code null}. - * @param isCanceled An optional flag that will interrupt caching if set to true. + * @param isCanceled An optional flag that will cancel the operation if set to true. * @param enableEOFException Whether to throw an {@link EOFException} if end of input has been * reached unexpectedly. * @param temporaryBuffer A temporary buffer to be used during caching. - * @throws IOException If an error occurs reading from the source. - * @throws InterruptedException If the thread was interrupted directly or via {@code isCanceled}. + * @throws IOException If an error occurs caching the data, or if the operation was canceled. */ @WorkerThread public static void cache( @@ -160,7 +165,7 @@ public final class CacheUtil { @Nullable AtomicBoolean isCanceled, boolean enableEOFException, byte[] temporaryBuffer) - throws IOException, InterruptedException { + throws IOException { Assertions.checkNotNull(dataSource); Assertions.checkNotNull(temporaryBuffer); @@ -181,7 +186,7 @@ public final class CacheUtil { long position = dataSpec.position; boolean lengthUnset = bytesLeft == C.LENGTH_UNSET; while (bytesLeft != 0) { - throwExceptionIfInterruptedOrCancelled(isCanceled); + throwExceptionIfCanceled(isCanceled); long blockLength = cache.getCachedLength(key, position, lengthUnset ? Long.MAX_VALUE : bytesLeft); if (blockLength > 0) { @@ -233,12 +238,13 @@ public final class CacheUtil { * @param position The position of the data to be read. * @param length Length of the data to be read, or {@link C#LENGTH_UNSET} if it is unknown. * @param dataSource The {@link CacheDataSource} to read the data from. - * @param isCanceled An optional flag that will interrupt caching if set to true. + * @param isCanceled An optional flag that will cancel the operation if set to true. * @param progressNotifier A notifier through which to report progress updates, or {@code null}. * @param isLastBlock Whether this read block is the last block of the content. * @param temporaryBuffer A temporary buffer to be used during caching. * @return Number of read bytes, or 0 if no data is available because the end of the opened range * has been reached. + * @param isCanceled An optional flag that will cancel the operation if set to true. */ private static long readAndDiscard( DataSpec dataSpec, @@ -249,7 +255,7 @@ public final class CacheUtil { @Nullable ProgressNotifier progressNotifier, boolean isLastBlock, byte[] temporaryBuffer) - throws IOException, InterruptedException { + throws IOException { long positionOffset = position - dataSpec.position; long initialPositionOffset = positionOffset; long endOffset = length != C.LENGTH_UNSET ? positionOffset + length : C.POSITION_UNSET; @@ -257,9 +263,13 @@ public final class CacheUtil { while (true) { if (priorityTaskManager != null) { // Wait for any other thread with higher priority to finish its job. - priorityTaskManager.proceed(dataSource.getUpstreamPriority()); + try { + priorityTaskManager.proceed(dataSource.getUpstreamPriority()); + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } } - throwExceptionIfInterruptedOrCancelled(isCanceled); + throwExceptionIfCanceled(isCanceled); try { long resolvedLength = C.LENGTH_UNSET; boolean isDataSourceOpen = false; @@ -286,7 +296,7 @@ public final class CacheUtil { progressNotifier.onRequestLengthResolved(positionOffset + resolvedLength); } while (positionOffset != endOffset) { - throwExceptionIfInterruptedOrCancelled(isCanceled); + throwExceptionIfCanceled(isCanceled); int bytesRead = dataSource.read( temporaryBuffer, @@ -369,10 +379,10 @@ public final class CacheUtil { .buildCacheKey(dataSpec); } - private static void throwExceptionIfInterruptedOrCancelled(@Nullable AtomicBoolean isCanceled) - throws InterruptedException { - if (Thread.interrupted() || (isCanceled != null && isCanceled.get())) { - throw new InterruptedException(); + private static void throwExceptionIfCanceled(@Nullable AtomicBoolean isCanceled) + throws InterruptedIOException { + if (isCanceled != null && isCanceled.get()) { + throw new InterruptedIOException(); } } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java index 941a09da14..0f4abf2f89 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java @@ -862,7 +862,7 @@ public class DownloadManagerTest { } @Override - public void download(ProgressListener listener) throws InterruptedException, IOException { + public void download(ProgressListener listener) throws IOException { startCount.incrementAndGet(); downloadStarted.open(); try { @@ -884,7 +884,7 @@ public class DownloadManagerTest { } @Override - public void remove() throws InterruptedException { + public void remove() { startCount.incrementAndGet(); removeStarted.open(); try { @@ -937,9 +937,11 @@ public class DownloadManagerTest { // Internal methods. - private void block() throws InterruptedException { + private void block() { try { blocker.block(); + } catch (InterruptedException e) { + throw new IllegalStateException(e); // Never happens. } finally { blocker.close(); } diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java index 30da21d35d..7b85d46f66 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java @@ -97,7 +97,7 @@ public final class DashDownloader extends SegmentDownloader { @Override protected List getSegments( DataSource dataSource, DashManifest manifest, boolean allowIncompleteList) - throws InterruptedException, IOException { + throws IOException { ArrayList segments = new ArrayList<>(); for (int i = 0; i < manifest.getPeriodCount(); i++) { Period period = manifest.getPeriod(i); @@ -124,7 +124,7 @@ public final class DashDownloader extends SegmentDownloader { long periodDurationUs, boolean allowIncompleteList, ArrayList out) - throws IOException, InterruptedException { + throws IOException { for (int i = 0; i < adaptationSet.representations.size(); i++) { Representation representation = adaptationSet.representations.get(i); DashSegmentIndex index; @@ -172,8 +172,7 @@ public final class DashDownloader extends SegmentDownloader { } private static @Nullable DashSegmentIndex getSegmentIndex( - DataSource dataSource, int trackType, Representation representation) - throws IOException, InterruptedException { + DataSource dataSource, int trackType, Representation representation) throws IOException { DashSegmentIndex index = representation.getIndex(); if (index != null) { return index;