-
Notifications
You must be signed in to change notification settings - Fork 776
DefaultPreloadManager NPE in maybeClearPreloadMediaSource when TargetPreloadStatusControl returns null #3155
Description
Description
DefaultPreloadManager.setCurrentPlayingIndex() throws a NullPointerException when TargetPreloadStatusControl.getTargetPreloadStatus() returns null for any item in the preload window.
The NPE occurs on every call to setCurrentPlayingIndex(), making the preload system non-functional despite null being a documented valid return value ("don't preload this item").
Version
- Media3 1.9.2 (verified)
- Also verified present in 1.10.0 source and
mainbranch
Stack trace
java.lang.NullPointerException: Attempt to read from field 'int
androidx.media3.exoplayer.source.preload.DefaultPreloadManager$PreloadStatus.stage'
on a null object reference in method 'void
androidx.media3.exoplayer.source.preload.DefaultPreloadManager.maybeClearPreloadMediaSource(
androidx.media3.exoplayer.source.preload.PreloadMediaSource,
androidx.media3.exoplayer.source.preload.DefaultPreloadManager$PreloadStatus)'
at DefaultPreloadManager.maybeClearPreloadMediaSource(DefaultPreloadManager.java:589)
at DefaultPreloadManager.preloadMediaSourceHolderInternal(DefaultPreloadManager.java:567)
at DefaultPreloadManager.preloadMediaSourceHolderInternal(DefaultPreloadManager.java:70)
at BasePreloadManager.maybeStartPreloadingNextSourceHolder(BasePreloadManager.java:586)
at BasePreloadManager.invalidate(BasePreloadManager.java:209)
at DefaultPreloadManager$SimpleRankingDataComparator.setCurrentPlayingIndex(DefaultPreloadManager.java:661)
at DefaultPreloadManager.setCurrentPlayingIndex(DefaultPreloadManager.java:544)
Root cause
BasePreloadManager.maybeStartPreloadingNextSourceHolder() calls targetPreloadStatusControl.getTargetPreloadStatus(rankingData), which may return null per its contract. The null result is passed directly to DefaultPreloadManager.preloadMediaSourceHolderInternal(holder, targetPreloadStatus), then to maybeClearPreloadMediaSource(preloadMediaSource, targetPreloadStatus), which accesses targetPreloadStatus.stage without a null check.
Reproduction
- Create a
DefaultPreloadManagerwith aTargetPreloadStatusControlthat returnsnullfor some items (e.g., the currently playing item at distance 0) - Add multiple items via
add() - Call
setCurrentPlayingIndex()— NPE every time
Suggested fix
Add a null guard in BasePreloadManager.maybeStartPreloadingNextSourceHolder():
this.targetPreloadStatusOfCurrentPreloadingSource =
targetPreloadStatusControl.getTargetPreloadStatus(preloadingHolder.rankingData);
if (this.targetPreloadStatusOfCurrentPreloadingSource == null) {
return false; // skip this source
}
preloadMediaSourceHolderInternal(
preloadingHolder, targetPreloadStatusOfCurrentPreloadingSource);Workaround
Never return null from getTargetPreloadStatus(). Use PRELOAD_STATUS_SOURCE_PREPARED (lightest tier) for items that should not be preloaded. The preload manager handles this status correctly.
Device info
- Pixel 7a, Android 15 (SDK 36)
- HLS content with fMP4/CMAF segments
- 4-tier preload strategy (specifiedRangeLoaded → TRACKS_SELECTED → SOURCE_PREPARED)