From 6a9ff95bf01ae9ef62f8f82aeb109cfdb8d8bdaf Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 12 Jul 2024 08:07:40 -0700 Subject: [PATCH] Bump minSdk to 21 and remove resulting simple dead code All other AndroidX libraries have already increased their min SDK to 21. This change renames private symbols to remove `V21` suffixes and similar, but doesn't change public or protected symbols with similar names, to avoid needless breakages/churn on users of the library. Some of the dead code removal is more complex, so I've split it out into follow-up changes to make it easier to review. PiperOrigin-RevId: 651776556 --- RELEASENOTES.md | 2 + api.txt | 4 +- constants.gradle | 2 +- .../media3/demo/main/DemoDownloadService.java | 2 +- .../media3/common/AudioAttributes.java | 2 - .../main/java/androidx/media3/common/C.java | 2 - .../androidx/media3/common/util/Util.java | 28 +--- .../datasource/DefaultHttpDataSource.java | 51 ------- .../media3/datasource/FileDataSource.java | 14 +- .../datasource/FileDescriptorDataSource.java | 2 - .../analytics/MediaMetricsListener.java | 5 +- .../audio/AudioCapabilitiesReceiver.java | 25 ++-- .../exoplayer/audio/DecoderAudioRenderer.java | 4 +- .../exoplayer/audio/DefaultAudioSink.java | 130 +++--------------- .../audio/MediaCodecAudioRenderer.java | 6 +- .../media3/exoplayer/drm/DrmUtil.java | 27 +--- .../exoplayer/drm/FrameworkCryptoConfig.java | 14 +- .../exoplayer/drm/FrameworkMediaDrm.java | 14 +- .../mediacodec/MediaCodecAdapter.java | 2 - .../MediaCodecDecoderException.java | 14 +- .../exoplayer/mediacodec/MediaCodecInfo.java | 56 ++------ .../exoplayer/mediacodec/MediaCodecUtil.java | 57 +------- .../SynchronousMediaCodecAdapter.java | 25 +--- .../scheduler/PlatformScheduler.java | 2 - .../exoplayer/scheduler/Requirements.java | 4 +- .../video/CompositingVideoSinkProvider.java | 60 -------- .../audio/DecoderAudioRendererTest.java | 12 -- .../exoplayer/audio/DefaultAudioSinkTest.java | 14 -- .../media3/muxer/Mp4MoovStructure.java | 4 +- .../DefaultMediaNotificationProvider.java | 6 +- .../media3/session/MediaLibraryService.java | 9 +- .../session/MediaNotificationManager.java | 17 +-- .../androidx/media3/session/MediaSession.java | 26 +--- .../media3/session/MediaSessionImpl.java | 12 +- .../session/MediaSessionLegacyStub.java | 26 +--- .../androidx/media3/session/SessionToken.java | 3 +- .../playback/gts/DashStreamingTest.java | 2 +- .../playback/gts/DebugRenderersFactory.java | 4 - .../playback/gts/EnumerateDecodersTest.java | 27 ++-- ...lerCompatCallbackWithMediaSessionTest.java | 60 +++----- ...lerListenerWithMediaSessionCompatTest.java | 2 +- ...aSessionCompatCallbackAggregationTest.java | 19 --- ...ntrollerWithFrameworkMediaSessionTest.java | 7 - ...aControllerWithMediaSessionCompatTest.java | 22 +-- ...CallbackWithMediaControllerCompatTest.java | 6 +- ...CompatCallbackWithMediaControllerTest.java | 6 +- .../session/MediaSessionKeyEventTest.java | 25 +--- .../media3/session/MediaSessionTest.java | 3 +- .../media3/session/SessionTokenTest.java | 4 - .../test/utils/AssetContentProvider.java | 4 +- .../test/utils/CapturingRenderersFactory.java | 1 - .../media3/test/utils/DecodeOneFrameUtil.java | 3 - .../media3/test/utils/SsimHelper.java | 2 - .../test/utils/VideoDecodingWrapper.java | 2 - .../robolectric/ShadowMediaCodecConfig.java | 9 -- .../media3/ui/CaptionStyleCompat.java | 33 +---- .../androidx/media3/ui/DefaultTimeBar.java | 9 +- .../media3/ui/DefaultTrackNameProvider.java | 3 +- .../media3/ui/LegacyPlayerControlView.java | 22 +-- .../media3/ui/PlayerNotificationManager.java | 27 +--- 60 files changed, 170 insertions(+), 815 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b8667e3cb8..855d04aaa2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -11,6 +11,8 @@ * Add override for `SimpleBasePlayer.State.Builder.setPlaylist()` to directly specify a `Timeline` and current `Tracks` and `Metadata` instead of building a playlist structure. + * Increase `minSdk` to 21 (Android Lollipop). This is aligned with all + other AndroidX libraries. * ExoPlayer: * `MediaCodecRenderer.onProcessedStreamChange()` can now be called for every media item. Previously it was not called for the first one. Use diff --git a/api.txt b/api.txt index e13090db32..911f5b4e5b 100644 --- a/api.txt +++ b/api.txt @@ -26,7 +26,7 @@ package androidx.media3.common { } public final class AudioAttributes { - method @RequiresApi(21) public androidx.media3.common.AudioAttributes.AudioAttributesV21 getAudioAttributesV21(); + method public androidx.media3.common.AudioAttributes.AudioAttributesV21 getAudioAttributesV21(); field public static final androidx.media3.common.AudioAttributes DEFAULT; field @androidx.media3.common.C.AudioAllowedCapturePolicy public final int allowedCapturePolicy; field @androidx.media3.common.C.AudioContentType public final int contentType; @@ -35,7 +35,7 @@ package androidx.media3.common { field @androidx.media3.common.C.AudioUsage public final int usage; } - @RequiresApi(21) public static final class AudioAttributes.AudioAttributesV21 { + public static final class AudioAttributes.AudioAttributesV21 { field public final android.media.AudioAttributes audioAttributes; } diff --git a/constants.gradle b/constants.gradle index cb5b6e5592..2e0070ba32 100644 --- a/constants.gradle +++ b/constants.gradle @@ -14,7 +14,7 @@ project.ext { releaseVersion = '1.4.0-rc01' releaseVersionCode = 1_004_000_2_01 - minSdkVersion = 19 + minSdkVersion = 21 // See https://developer.android.com/training/cars/media/automotive-os#automotive-module automotiveMinSdkVersion = 28 appTargetSdkVersion = 34 diff --git a/demos/main/src/main/java/androidx/media3/demo/main/DemoDownloadService.java b/demos/main/src/main/java/androidx/media3/demo/main/DemoDownloadService.java index c14bfc4ba9..d32fa90c83 100644 --- a/demos/main/src/main/java/androidx/media3/demo/main/DemoDownloadService.java +++ b/demos/main/src/main/java/androidx/media3/demo/main/DemoDownloadService.java @@ -63,7 +63,7 @@ public class DemoDownloadService extends DownloadService { @Override protected Scheduler getScheduler() { - return Util.SDK_INT >= 21 ? new PlatformScheduler(this, JOB_ID) : null; + return new PlatformScheduler(this, JOB_ID); } @Override diff --git a/libraries/common/src/main/java/androidx/media3/common/AudioAttributes.java b/libraries/common/src/main/java/androidx/media3/common/AudioAttributes.java index 34c7540ea7..2f00ce03c7 100644 --- a/libraries/common/src/main/java/androidx/media3/common/AudioAttributes.java +++ b/libraries/common/src/main/java/androidx/media3/common/AudioAttributes.java @@ -37,7 +37,6 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; public final class AudioAttributes { /** A direct wrapper around {@link android.media.AudioAttributes}. */ - @RequiresApi(21) public static final class AudioAttributesV21 { public final android.media.AudioAttributes audioAttributes; @@ -165,7 +164,6 @@ public final class AudioAttributes { *

Some fields are ignored if the corresponding {@link android.media.AudioAttributes.Builder} * setter is not available on the current API level. */ - @RequiresApi(21) public AudioAttributesV21 getAudioAttributesV21() { if (audioAttributesV21 == null) { audioAttributesV21 = new AudioAttributesV21(this); diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index 0ae9200b50..fb8598a7ca 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -32,7 +32,6 @@ import android.media.MediaFormat; import android.net.Uri; import android.view.Surface; import androidx.annotation.IntDef; -import androidx.annotation.RequiresApi; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import com.google.errorprone.annotations.InlineMe; @@ -1698,7 +1697,6 @@ public final class C { replacement = "Util.generateAudioSessionIdV21(context)", imports = {"androidx.media3.common.util.Util"}) @Deprecated - @RequiresApi(21) public static int generateAudioSessionIdV21(Context context) { return Util.generateAudioSessionIdV21(context); } diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Util.java b/libraries/common/src/main/java/androidx/media3/common/util/Util.java index 7582a7c072..b938c1d6e2 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/Util.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/Util.java @@ -962,16 +962,14 @@ public final class Util { /** * Returns the language tag for a {@link Locale}. * - *

For API levels ≥ 21, this tag is IETF BCP 47 compliant. Use {@link - * #normalizeLanguageCode(String)} to retrieve a normalized IETF BCP 47 language tag for all API - * levels if needed. + *

This tag is IETF BCP 47 compliant. * * @param locale A {@link Locale}. * @return The language tag. */ @UnstableApi public static String getLocaleLanguageTag(Locale locale) { - return SDK_INT >= 21 ? getLocaleLanguageTagV21(locale) : locale.toString(); + return locale.toLanguageTag(); } /** @@ -2253,7 +2251,6 @@ public final class Util { /** Creates {@link AudioFormat} with given sampleRate, channelConfig, and encoding. */ @UnstableApi - @RequiresApi(21) public static AudioFormat getAudioFormat(int sampleRate, int channelConfig, int encoding) { return new AudioFormat.Builder() .setSampleRate(sampleRate) @@ -2417,7 +2414,6 @@ public final class Util { * @see AudioManager#generateAudioSessionId() */ @UnstableApi - @RequiresApi(21) public static int generateAudioSessionIdV21(Context context) { @Nullable AudioManager audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE)); @@ -3065,8 +3061,7 @@ public final class Util { */ @UnstableApi public static boolean isWear(Context context) { - return SDK_INT >= 20 - && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); } /** @@ -3502,9 +3497,7 @@ public final class Util { @UnstableApi public static Drawable getDrawable( Context context, Resources resources, @DrawableRes int drawableRes) { - return SDK_INT >= 21 - ? Api21.getDrawable(context, resources, drawableRes) - : resources.getDrawable(drawableRes); + return resources.getDrawable(drawableRes, context.getTheme()); } /** @@ -3666,11 +3659,6 @@ public final class Util { return split(config.getLocales().toLanguageTags(), ","); } - @RequiresApi(21) - private static String getLocaleLanguageTagV21(Locale locale) { - return locale.toLanguageTag(); - } - private static HashMap createIsoLanguageReplacementMap() { String[] iso2Languages = Locale.getISOLanguages(); HashMap replacedLanguages = @@ -3890,14 +3878,6 @@ public final class Util { 0xF3 }; - @RequiresApi(21) - private static final class Api21 { - @DoNotInline - public static Drawable getDrawable(Context context, Resources resources, @DrawableRes int res) { - return resources.getDrawable(res, context.getTheme()); - } - } - @RequiresApi(29) private static class Api29 { diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/DefaultHttpDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/DefaultHttpDataSource.java index 2779b49db1..f2152be2df 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/DefaultHttpDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/DefaultHttpDataSource.java @@ -40,7 +40,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; -import java.lang.reflect.Method; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.NoRouteToHostException; @@ -248,7 +247,6 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou private static final int MAX_REDIRECTS = 20; // Same limit as okhttp. private static final int HTTP_STATUS_TEMPORARY_REDIRECT = 307; private static final int HTTP_STATUS_PERMANENT_REDIRECT = 308; - private static final long MAX_BYTES_TO_DRAIN = 2048; private final boolean allowCrossProtocolRedirects; private final boolean crossProtocolRedirectsForceOriginal; @@ -482,9 +480,6 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou try { @Nullable InputStream inputStream = this.inputStream; if (inputStream != null) { - long bytesRemaining = - bytesToRead == C.LENGTH_UNSET ? C.LENGTH_UNSET : bytesToRead - bytesRead; - maybeTerminateInputStream(connection, bytesRemaining); try { inputStream.close(); } catch (IOException e) { @@ -784,52 +779,6 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou return read; } - /** - * On platform API levels 19 and 20, okhttp's implementation of {@link InputStream#close} can - * block for a long time if the stream has a lot of data remaining. Call this method before - * closing the input stream to make a best effort to cause the input stream to encounter an - * unexpected end of input, working around this issue. On other platform API levels, the method - * does nothing. - * - * @param connection The connection whose {@link InputStream} should be terminated. - * @param bytesRemaining The number of bytes remaining to be read from the input stream if its - * length is known. {@link C#LENGTH_UNSET} otherwise. - */ - private static void maybeTerminateInputStream( - @Nullable HttpURLConnection connection, long bytesRemaining) { - if (connection == null || Util.SDK_INT > 20) { - return; - } - - try { - InputStream inputStream = connection.getInputStream(); - if (bytesRemaining == C.LENGTH_UNSET) { - // If the input stream has already ended, do nothing. The socket may be re-used. - if (inputStream.read() == -1) { - return; - } - } else if (bytesRemaining <= MAX_BYTES_TO_DRAIN) { - // There isn't much data left. Prefer to allow it to drain, which may allow the socket to be - // re-used. - return; - } - String className = inputStream.getClass().getName(); - if ("com.android.okhttp.internal.http.HttpTransport$ChunkedInputStream".equals(className) - || "com.android.okhttp.internal.http.HttpTransport$FixedLengthInputStream" - .equals(className)) { - Class superclass = inputStream.getClass().getSuperclass(); - Method unexpectedEndOfInput = - checkNotNull(superclass).getDeclaredMethod("unexpectedEndOfInput"); - unexpectedEndOfInput.setAccessible(true); - unexpectedEndOfInput.invoke(inputStream); - } - } catch (Exception e) { - // If an IOException then the connection didn't ever have an input stream, or it was closed - // already. If another type of exception then something went wrong, most likely the device - // isn't using okhttp. - } - } - /** Closes the current connection quietly, if there is one. */ private void closeConnectionQuietly() { if (connection != null) { diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/FileDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/FileDataSource.java index 2bbfa5eab2..8781daf66a 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/FileDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/FileDataSource.java @@ -22,14 +22,11 @@ import android.net.Uri; import android.system.ErrnoException; import android.system.OsConstants; import android.text.TextUtils; -import androidx.annotation.DoNotInline; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.media3.common.C; import androidx.media3.common.PlaybackException; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.UnstableApi; -import androidx.media3.common.util.Util; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.FileNotFoundException; import java.io.IOException; @@ -200,7 +197,8 @@ public final class FileDataSource extends BaseDataSource { // different SDK versions. throw new FileDataSourceException( e, - Util.SDK_INT >= 21 && Api21.isPermissionError(e.getCause()) + e.getCause() instanceof ErrnoException + && ((ErrnoException) e.getCause()).errno == OsConstants.EACCES ? PlaybackException.ERROR_CODE_IO_NO_PERMISSION : PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND); } catch (SecurityException e) { @@ -209,12 +207,4 @@ public final class FileDataSource extends BaseDataSource { throw new FileDataSourceException(e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED); } } - - @RequiresApi(21) - private static final class Api21 { - @DoNotInline - private static boolean isPermissionError(@Nullable Throwable e) { - return e instanceof ErrnoException && ((ErrnoException) e).errno == OsConstants.EACCES; - } - } } diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/FileDescriptorDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/FileDescriptorDataSource.java index 72565e991c..883d8aafca 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/FileDescriptorDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/FileDescriptorDataSource.java @@ -24,7 +24,6 @@ import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.media3.common.C; import androidx.media3.common.PlaybackException; import androidx.media3.common.util.UnstableApi; @@ -48,7 +47,6 @@ import java.util.Set; * #open(DataSpec)} is not actually used for reading data. Instead, the underlying {@link * FileDescriptor} is used for all read operations. */ -@RequiresApi(21) @UnstableApi public class FileDescriptorDataSource extends BaseDataSource { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java index 8ec2802f56..89af53033c 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java @@ -748,7 +748,7 @@ public final class MediaMetricsListener } else if (cause instanceof DrmSession.DrmSessionException) { // Unpack DrmSessionException. cause = checkNotNull(cause.getCause()); - if (Util.SDK_INT >= 21 && cause instanceof MediaDrm.MediaDrmStateException) { + if (cause instanceof MediaDrm.MediaDrmStateException) { String diagnosticsInfo = ((MediaDrm.MediaDrmStateException) cause).getDiagnosticInfo(); int subErrorCode = Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticsInfo); int errorCode = getDrmErrorCode(subErrorCode); @@ -771,8 +771,7 @@ public final class MediaMetricsListener } else if (cause instanceof FileDataSource.FileDataSourceException && cause.getCause() instanceof FileNotFoundException) { @Nullable Throwable notFoundCause = checkNotNull(cause.getCause()).getCause(); - if (Util.SDK_INT >= 21 - && notFoundCause instanceof ErrnoException + if (notFoundCause instanceof ErrnoException && ((ErrnoException) notFoundCause).errno == OsConstants.EACCES) { return new ErrorInfo(PlaybackErrorEvent.ERROR_IO_NO_PERMISSION, /* subErrorCode= */ 0); } else { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilitiesReceiver.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilitiesReceiver.java index 6fe5ca14b0..3ed10f9a7c 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilitiesReceiver.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilitiesReceiver.java @@ -57,7 +57,7 @@ public final class AudioCapabilitiesReceiver { private final Listener listener; private final Handler handler; @Nullable private final AudioDeviceCallbackV23 audioDeviceCallback; - @Nullable private final BroadcastReceiver hdmiAudioPlugBroadcastReceiver; + private final BroadcastReceiver hdmiAudioPlugBroadcastReceiver; @Nullable private final ExternalSurroundSoundSettingObserver externalSurroundSoundSettingObserver; @Nullable private AudioCapabilities audioCapabilities; @@ -105,8 +105,7 @@ public final class AudioCapabilitiesReceiver { this.routedDevice = routedDevice; handler = Util.createHandlerForCurrentOrMainLooper(); audioDeviceCallback = Util.SDK_INT >= 23 ? new AudioDeviceCallbackV23() : null; - hdmiAudioPlugBroadcastReceiver = - Util.SDK_INT >= 21 ? new HdmiAudioPlugBroadcastReceiver() : null; + hdmiAudioPlugBroadcastReceiver = new HdmiAudioPlugBroadcastReceiver(); Uri externalSurroundSoundUri = AudioCapabilities.getExternalSurroundSoundGlobalSettingUri(); externalSurroundSoundSettingObserver = externalSurroundSoundUri != null @@ -162,16 +161,12 @@ public final class AudioCapabilitiesReceiver { if (Util.SDK_INT >= 23 && audioDeviceCallback != null) { Api23.registerAudioDeviceCallback(context, audioDeviceCallback, handler); } - @Nullable Intent stickyIntent = null; - if (hdmiAudioPlugBroadcastReceiver != null) { - IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG); - stickyIntent = - context.registerReceiver( - hdmiAudioPlugBroadcastReceiver, - intentFilter, - /* broadcastPermission= */ null, - handler); - } + Intent stickyIntent = + context.registerReceiver( + hdmiAudioPlugBroadcastReceiver, + new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG), + /* broadcastPermission= */ null, + handler); audioCapabilities = AudioCapabilities.getCapabilitiesInternal( context, stickyIntent, audioAttributes, routedDevice); @@ -190,9 +185,7 @@ public final class AudioCapabilitiesReceiver { if (Util.SDK_INT >= 23 && audioDeviceCallback != null) { Api23.unregisterAudioDeviceCallback(context, audioDeviceCallback); } - if (hdmiAudioPlugBroadcastReceiver != null) { - context.unregisterReceiver(hdmiAudioPlugBroadcastReceiver); - } + context.unregisterReceiver(hdmiAudioPlugBroadcastReceiver); if (externalSurroundSoundSettingObserver != null) { externalSurroundSoundSettingObserver.unregister(); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java index 5a40f90214..89c6557c4b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java @@ -244,9 +244,7 @@ public abstract class DecoderAudioRenderer< if (formatSupport <= C.FORMAT_UNSUPPORTED_DRM) { return RendererCapabilities.create(formatSupport); } - @TunnelingSupport - int tunnelingSupport = Util.SDK_INT >= 21 ? TUNNELING_SUPPORTED : TUNNELING_NOT_SUPPORTED; - return RendererCapabilities.create(formatSupport, ADAPTIVE_NOT_SEAMLESS, tunnelingSupport); + return RendererCapabilities.create(formatSupport, ADAPTIVE_NOT_SEAMLESS, TUNNELING_SUPPORTED); } /** diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java index 1de4cf13b4..2c28631a49 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java @@ -337,8 +337,8 @@ public final class DefaultAudioSink implements AudioSink { /** * Sets whether to enable 32-bit float output or integer output. Where possible, 32-bit float * output will be used if the input is 32-bit float, and also if the input is high resolution - * (24-bit or 32-bit) integer PCM. Float output is supported from API level 21. Audio processing - * (for example, speed adjustment) will not be available when float output is in use. + * (24-bit or 32-bit) integer PCM. Audio processing (for example, speed adjustment) will not be + * available when float output is in use. * *

The default value is {@code false}. */ @@ -541,8 +541,6 @@ public final class DefaultAudioSink implements AudioSink { @Nullable private ByteBuffer inputBuffer; private int inputBufferAccessUnitCount; @Nullable private ByteBuffer outputBuffer; - private byte @MonotonicNonNull [] preV21OutputBuffer; - private int preV21OutputBufferOffset; private boolean handledEndOfStream; private boolean stoppedAudioTrack; private boolean handledOffloadOnPresentationEnded; @@ -571,7 +569,7 @@ public final class DefaultAudioSink implements AudioSink { ? getCapabilities(context, audioAttributes, /* routedDevice= */ null) : builder.audioCapabilities; audioProcessorChain = builder.audioProcessorChain; - enableFloatOutput = Util.SDK_INT >= 21 && builder.enableFloatOutput; + enableFloatOutput = builder.enableFloatOutput; preferAudioTrackPlaybackParams = Util.SDK_INT >= 23 && builder.enableAudioTrackPlaybackParams; offloadMode = OFFLOAD_MODE_DISABLED; audioTrackBufferSizeProvider = builder.audioTrackBufferSizeProvider; @@ -700,14 +698,6 @@ public final class DefaultAudioSink implements AudioSink { trimmingAudioProcessor.setTrimFrameCount( inputFormat.encoderDelay, inputFormat.encoderPadding); - if (Util.SDK_INT < 21 && inputFormat.channelCount == 8 && outputChannels == null) { - // AudioTrack doesn't support 8 channel output before Android L. Discard the last two (side) - // channels to give a 6 channel stream that is supported. - outputChannels = new int[6]; - for (int i = 0; i < outputChannels.length; i++) { - outputChannels[i] = i; - } - } channelMappingAudioProcessor.setChannelMap(outputChannels); AudioProcessor.AudioFormat outputFormat = new AudioProcessor.AudioFormat(inputFormat); @@ -1160,32 +1150,10 @@ public final class DefaultAudioSink implements AudioSink { Assertions.checkArgument(outputBuffer == buffer); } else { outputBuffer = buffer; - if (Util.SDK_INT < 21) { - int bytesRemaining = buffer.remaining(); - if (preV21OutputBuffer == null || preV21OutputBuffer.length < bytesRemaining) { - preV21OutputBuffer = new byte[bytesRemaining]; - } - int originalPosition = buffer.position(); - buffer.get(preV21OutputBuffer, 0, bytesRemaining); - buffer.position(originalPosition); - preV21OutputBufferOffset = 0; - } } int bytesRemaining = buffer.remaining(); int bytesWrittenOrError = 0; // Error if negative - if (Util.SDK_INT < 21) { // outputMode == OUTPUT_MODE_PCM. - // Work out how many bytes we can write without the risk of blocking. - int bytesToWrite = audioTrackPositionTracker.getAvailableBufferSize(writtenPcmBytes); - if (bytesToWrite > 0) { - bytesToWrite = min(bytesRemaining, bytesToWrite); - bytesWrittenOrError = - audioTrack.write(preV21OutputBuffer, preV21OutputBufferOffset, bytesToWrite); - if (bytesWrittenOrError > 0) { // No error - preV21OutputBufferOffset += bytesWrittenOrError; - buffer.position(buffer.position() + bytesWrittenOrError); - } - } - } else if (tunneling) { + if (tunneling) { Assertions.checkState(avSyncPresentationTimeUs != C.TIME_UNSET); if (avSyncPresentationTimeUs == C.TIME_END_OF_SOURCE) { // Audio processors during tunneling are required to produce buffers immediately when @@ -1196,10 +1164,9 @@ public final class DefaultAudioSink implements AudioSink { lastTunnelingAvSyncPresentationTimeUs = avSyncPresentationTimeUs; } bytesWrittenOrError = - writeNonBlockingWithAvSyncV21( - audioTrack, buffer, bytesRemaining, avSyncPresentationTimeUs); + writeNonBlockingWithAvSync(audioTrack, buffer, bytesRemaining, avSyncPresentationTimeUs); } else { - bytesWrittenOrError = writeNonBlockingV21(audioTrack, buffer, bytesRemaining); + bytesWrittenOrError = writeNonBlocking(audioTrack, buffer, bytesRemaining); } lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime(); @@ -1402,7 +1369,6 @@ public final class DefaultAudioSink implements AudioSink { @Override public void enableTunnelingV21() { - Assertions.checkState(Util.SDK_INT >= 21); Assertions.checkState(externalAudioSessionIdProvided); if (!tunneling) { tunneling = true; @@ -1445,12 +1411,8 @@ public final class DefaultAudioSink implements AudioSink { } private void setVolumeInternal() { - if (!isAudioTrackInitialized()) { - // Do nothing. - } else if (Util.SDK_INT >= 21) { - setVolumeInternalV21(audioTrack, volume); - } else { - setVolumeInternalV3(audioTrack, volume); + if (isAudioTrackInitialized()) { + audioTrack.setVolume(volume); } } @@ -1474,14 +1436,6 @@ public final class DefaultAudioSink implements AudioSink { if (isOffloadedPlayback(audioTrack)) { checkNotNull(offloadStreamEventCallbackV29).unregister(audioTrack); } - if (Util.SDK_INT < 21 && !externalAudioSessionIdProvided) { - // Prior to API level 21, audio sessions are not kept alive once there are no components - // associated with them. If we generated the session ID internally, the only component - // associated with the session is the audio track that's being released, and therefore - // the session will not be kept alive. As a result, we need to generate a new session when - // we next create an audio track. - audioSessionId = C.AUDIO_SESSION_ID_UNSET; - } AudioTrackConfig oldAudioTrackConfig = configuration.buildAudioTrackConfig(); if (pendingConfiguration != null) { configuration = pendingConfiguration; @@ -1822,13 +1776,11 @@ public final class DefaultAudioSink implements AudioSink { } } - @RequiresApi(21) - private static int writeNonBlockingV21(AudioTrack audioTrack, ByteBuffer buffer, int size) { + private static int writeNonBlocking(AudioTrack audioTrack, ByteBuffer buffer, int size) { return audioTrack.write(buffer, size, AudioTrack.WRITE_NON_BLOCKING); } - @RequiresApi(21) - private int writeNonBlockingWithAvSyncV21( + private int writeNonBlockingWithAvSync( AudioTrack audioTrack, ByteBuffer buffer, int size, long presentationTimeUs) { if (Util.SDK_INT >= 26) { // The underlying platform AudioTrack writes AV sync headers directly. @@ -1858,7 +1810,7 @@ public final class DefaultAudioSink implements AudioSink { return 0; } } - int result = writeNonBlockingV21(audioTrack, buffer, size); + int result = writeNonBlocking(audioTrack, buffer, size); if (result < 0) { bytesUntilNextAvSync = 0; return result; @@ -1867,15 +1819,6 @@ public final class DefaultAudioSink implements AudioSink { return result; } - @RequiresApi(21) - private static void setVolumeInternalV21(AudioTrack audioTrack, float volume) { - audioTrack.setVolume(volume); - } - - private static void setVolumeInternalV3(AudioTrack audioTrack, float volume) { - audioTrack.setStereoVolume(volume, volume); - } - private void playPendingData() { if (!stoppedAudioTrack) { stoppedAudioTrack = true; @@ -2253,10 +2196,8 @@ public final class DefaultAudioSink implements AudioSink { private AudioTrack createAudioTrack(AudioAttributes audioAttributes, int audioSessionId) { if (Util.SDK_INT >= 29) { return createAudioTrackV29(audioAttributes, audioSessionId); - } else if (Util.SDK_INT >= 21) { - return createAudioTrackV21(audioAttributes, audioSessionId); } else { - return createAudioTrackV9(audioAttributes, audioSessionId); + return createAudioTrackV21(audioAttributes, audioSessionId); } } @@ -2265,7 +2206,7 @@ public final class DefaultAudioSink implements AudioSink { AudioFormat audioFormat = Util.getAudioFormat(outputSampleRate, outputChannelConfig, outputEncoding); android.media.AudioAttributes audioTrackAttributes = - getAudioTrackAttributesV21(audioAttributes, tunneling); + getAudioTrackAttributes(audioAttributes, tunneling); return new AudioTrack.Builder() .setAudioAttributes(audioTrackAttributes) .setAudioFormat(audioFormat) @@ -2276,59 +2217,28 @@ public final class DefaultAudioSink implements AudioSink { .build(); } - @RequiresApi(21) private AudioTrack createAudioTrackV21(AudioAttributes audioAttributes, int audioSessionId) { return new AudioTrack( - getAudioTrackAttributesV21(audioAttributes, tunneling), + getAudioTrackAttributes(audioAttributes, tunneling), Util.getAudioFormat(outputSampleRate, outputChannelConfig, outputEncoding), bufferSize, AudioTrack.MODE_STREAM, audioSessionId); } - @SuppressWarnings("deprecation") // Using deprecated AudioTrack constructor. - private AudioTrack createAudioTrackV9(AudioAttributes audioAttributes, int audioSessionId) { - int streamType = Util.getStreamTypeForAudioUsage(audioAttributes.usage); - if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) { - return new AudioTrack( - streamType, - outputSampleRate, - outputChannelConfig, - outputEncoding, - bufferSize, - AudioTrack.MODE_STREAM); - } else { - // Re-attach to the same audio session. - return new AudioTrack( - streamType, - outputSampleRate, - outputChannelConfig, - outputEncoding, - bufferSize, - AudioTrack.MODE_STREAM, - audioSessionId); - } - } - - @RequiresApi(21) - private static android.media.AudioAttributes getAudioTrackAttributesV21( + private static android.media.AudioAttributes getAudioTrackAttributes( AudioAttributes audioAttributes, boolean tunneling) { if (tunneling) { - return getAudioTrackTunnelingAttributesV21(); + return new android.media.AudioAttributes.Builder() + .setContentType(android.media.AudioAttributes.CONTENT_TYPE_MOVIE) + .setFlags(android.media.AudioAttributes.FLAG_HW_AV_SYNC) + .setUsage(android.media.AudioAttributes.USAGE_MEDIA) + .build(); } else { return audioAttributes.getAudioAttributesV21().audioAttributes; } } - @RequiresApi(21) - private static android.media.AudioAttributes getAudioTrackTunnelingAttributesV21() { - return new android.media.AudioAttributes.Builder() - .setContentType(android.media.AudioAttributes.CONTENT_TYPE_MOVIE) - .setFlags(android.media.AudioAttributes.FLAG_HW_AV_SYNC) - .setUsage(android.media.AudioAttributes.USAGE_MEDIA) - .build(); - } - public boolean outputModeIsOffload() { return outputMode == OUTPUT_MODE_OFFLOAD; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java index e3329d2739..fe06d0ef2e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java @@ -278,8 +278,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media if (!MimeTypes.isAudio(format.sampleMimeType)) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } - @TunnelingSupport - int tunnelingSupport = Util.SDK_INT >= 21 ? TUNNELING_SUPPORTED : TUNNELING_NOT_SUPPORTED; boolean formatHasDrm = format.cryptoType != C.CRYPTO_TYPE_NONE; boolean supportsFormatDrm = supportsFormatDrm(format); @@ -291,7 +289,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media audioOffloadSupport = getAudioOffloadSupport(format); if (audioSink.supportsFormat(format)) { return RendererCapabilities.create( - C.FORMAT_HANDLED, ADAPTIVE_NOT_SEAMLESS, tunnelingSupport, audioOffloadSupport); + C.FORMAT_HANDLED, ADAPTIVE_NOT_SEAMLESS, TUNNELING_SUPPORTED, audioOffloadSupport); } } // If the input is PCM then it will be passed directly to the sink. Hence the sink must support @@ -346,7 +344,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media return RendererCapabilities.create( formatSupport, adaptiveSupport, - tunnelingSupport, + TUNNELING_SUPPORTED, hardwareAccelerationSupport, decoderSupport, audioOffloadSupport); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java index 47af936ef8..be88fe1dda 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java @@ -77,8 +77,11 @@ public final class DrmUtil { */ public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmException( Throwable exception, @ErrorSource int errorSource) { - if (Util.SDK_INT >= 21 && Api21.isMediaDrmStateException(exception)) { - return Api21.mediaDrmStateExceptionToErrorCode(exception); + if (exception instanceof MediaDrm.MediaDrmStateException) { + @Nullable + String diagnosticsInfo = ((MediaDrm.MediaDrmStateException) exception).getDiagnosticInfo(); + int drmErrorCode = Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticsInfo); + return Util.getErrorCodeForMediaDrmErrorCode(drmErrorCode); } else if (Util.SDK_INT >= 23 && Api23.isMediaDrmResetException(exception)) { return PlaybackException.ERROR_CODE_DRM_SYSTEM_ERROR; } else if (exception instanceof NotProvisionedException @@ -128,26 +131,6 @@ public final class DrmUtil { && e.getMessage().contains("Landroid/media/ResourceBusyException;.("); } - // Internal classes. - - @RequiresApi(21) - private static final class Api21 { - - @DoNotInline - public static boolean isMediaDrmStateException(@Nullable Throwable throwable) { - return throwable instanceof MediaDrm.MediaDrmStateException; - } - - @DoNotInline - public static @PlaybackException.ErrorCode int mediaDrmStateExceptionToErrorCode( - Throwable throwable) { - @Nullable - String diagnosticsInfo = ((MediaDrm.MediaDrmStateException) throwable).getDiagnosticInfo(); - int drmErrorCode = Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticsInfo); - return Util.getErrorCodeForMediaDrmErrorCode(drmErrorCode); - } - } - @RequiresApi(23) private static final class Api23 { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkCryptoConfig.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkCryptoConfig.java index 4766202a8b..a74aeb8f56 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkCryptoConfig.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkCryptoConfig.java @@ -53,12 +53,22 @@ public final class FrameworkCryptoConfig implements CryptoConfig { @Deprecated public final boolean forceAllowInsecureDecoderComponents; /** + * Constructs an instance. + * * @param uuid The DRM scheme UUID. * @param sessionId The DRM session id. - * @param forceAllowInsecureDecoderComponents Whether to allow use of insecure decoder components - * even if the underlying platform says otherwise. + */ + @SuppressWarnings("deprecation") // Delegating to deprecated constructor + public FrameworkCryptoConfig(UUID uuid, byte[] sessionId) { + this(uuid, sessionId, /* forceAllowInsecureDecoderComponents= */ false); + } + + /** + * @deprecated Use {@link FrameworkCryptoConfig#FrameworkCryptoConfig(UUID, byte[])} instead, and + * {@link ExoMediaDrm#requiresSecureDecoder} for the secure decoder handling logic. */ @SuppressWarnings("deprecation") // Setting deprecated field + @Deprecated public FrameworkCryptoConfig( UUID uuid, byte[] sessionId, boolean forceAllowInsecureDecoderComponents) { this.uuid = uuid; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java index 873fd2849a..e11b165813 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java @@ -308,7 +308,7 @@ public final class FrameworkMediaDrm implements ExoMediaDrm { } } } - return result && !shouldForceAllowInsecureDecoderComponents(); + return result; } @UnstableApi @@ -389,17 +389,7 @@ public final class FrameworkMediaDrm implements ExoMediaDrm { @UnstableApi @Override public FrameworkCryptoConfig createCryptoConfig(byte[] sessionId) throws MediaCryptoException { - boolean forceAllowInsecureDecoderComponents = shouldForceAllowInsecureDecoderComponents(); - return new FrameworkCryptoConfig( - adjustUuid(uuid), sessionId, forceAllowInsecureDecoderComponents); - } - - // Work around a bug prior to Lollipop where L1 Widevine forced into L3 mode would still - // indicate that it required secure video decoders [Internal ref: b/11428937]. - private boolean shouldForceAllowInsecureDecoderComponents() { - return Util.SDK_INT < 21 - && C.WIDEVINE_UUID.equals(uuid) - && "L3".equals(getPropertyString("securityLevel")); + return new FrameworkCryptoConfig(adjustUuid(uuid), sessionId); } @UnstableApi diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecAdapter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecAdapter.java index 1599c4ae28..9a89bfd3a2 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecAdapter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecAdapter.java @@ -252,7 +252,6 @@ public interface MediaCodecAdapter { * * @see MediaCodec#releaseOutputBuffer(int, long) */ - @RequiresApi(21) void releaseOutputBuffer(int index, long renderTimeStampNs); /** Flushes the adapter and the underlying {@link MediaCodec}. */ @@ -278,7 +277,6 @@ public interface MediaCodecAdapter { * @see MediaCodec.Callback#onOutputBufferAvailable * @return Whether listener was successfully registered. */ - @RequiresApi(21) default boolean registerOnBufferAvailableListener( MediaCodecAdapter.OnBufferAvailableListener listener) { return false; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecDecoderException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecDecoderException.java index 1cc6217bd0..21130c6c46 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecDecoderException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecDecoderException.java @@ -38,22 +38,16 @@ public class MediaCodecDecoderException extends DecoderException { public MediaCodecDecoderException(Throwable cause, @Nullable MediaCodecInfo codecInfo) { super("Decoder failed: " + (codecInfo == null ? null : codecInfo.name), cause); this.codecInfo = codecInfo; - diagnosticInfo = Util.SDK_INT >= 21 ? getDiagnosticInfoV21(cause) : null; + diagnosticInfo = + cause instanceof MediaCodec.CodecException + ? ((MediaCodec.CodecException) cause).getDiagnosticInfo() + : null; errorCode = Util.SDK_INT >= 23 ? getErrorCodeV23(cause) : Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticInfo); } - @RequiresApi(21) - @Nullable - private static String getDiagnosticInfoV21(Throwable cause) { - if (cause instanceof MediaCodec.CodecException) { - return ((MediaCodec.CodecException) cause).getDiagnosticInfo(); - } - return null; - } - @RequiresApi(23) private static int getErrorCodeV23(Throwable cause) { if (cause instanceof MediaCodec.CodecException) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java index 645ef3fd71..7400ab745f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java @@ -258,22 +258,12 @@ public final class MediaCodecInfo { if (format.width <= 0 || format.height <= 0) { return true; } - if (Util.SDK_INT >= 21) { - return isVideoSizeAndRateSupportedV21(format.width, format.height, format.frameRate); - } else { - boolean isFormatSupported = - format.width * format.height <= MediaCodecUtil.maxH264DecodableFrameSize(); - if (!isFormatSupported) { - logNoSupport("legacyFrameSize, " + format.width + "x" + format.height); - } - return isFormatSupported; - } + return isVideoSizeAndRateSupportedV21(format.width, format.height, format.frameRate); } else { // Audio - return Util.SDK_INT < 21 - || ((format.sampleRate == Format.NO_VALUE - || isAudioSampleRateSupportedV21(format.sampleRate)) - && (format.channelCount == Format.NO_VALUE - || isAudioChannelCountSupportedV21(format.channelCount))); + return (format.sampleRate == Format.NO_VALUE + || isAudioSampleRateSupportedV21(format.sampleRate)) + && (format.channelCount == Format.NO_VALUE + || isAudioChannelCountSupportedV21(format.channelCount)); } } @@ -482,7 +472,6 @@ public final class MediaCodecInfo { * Format#NO_VALUE} or any value less than or equal to 0. * @return Whether the decoder supports video with the given width, height and frame rate. */ - @RequiresApi(21) public boolean isVideoSizeAndRateSupportedV21(int width, int height, double frameRate) { if (capabilities == null) { logNoSupport("sizeAndRate.caps"); @@ -506,13 +495,13 @@ public final class MediaCodecInfo { return false; } // If COVERAGE_RESULT_NO_PERFORMANCE_POINTS_UNSUPPORTED then logic falls through - // to API 21+ code below. + // to code below. } - if (!areSizeAndRateSupportedV21(videoCapabilities, width, height, frameRate)) { + if (!areSizeAndRateSupported(videoCapabilities, width, height, frameRate)) { if (width >= height || !needsRotatedVerticalResolutionWorkaround(name) - || !areSizeAndRateSupportedV21(videoCapabilities, height, width, frameRate)) { + || !areSizeAndRateSupported(videoCapabilities, height, width, frameRate)) { logNoSupport("sizeAndRate.support, " + width + "x" + height + "@" + frameRate); return false; } @@ -525,8 +514,6 @@ public final class MediaCodecInfo { * Returns the smallest video size greater than or equal to a specified size that also satisfies * the {@link MediaCodec}'s width and height alignment requirements. * - *

Must not be called if the device SDK version is less than 21. - * * @param width Width in pixels. * @param height Height in pixels. * @return The smallest video size greater than or equal to the specified size that also satisfies @@ -534,7 +521,6 @@ public final class MediaCodecInfo { * codec. */ @Nullable - @RequiresApi(21) public Point alignVideoSizeV21(int width, int height) { if (capabilities == null) { return null; @@ -543,18 +529,15 @@ public final class MediaCodecInfo { if (videoCapabilities == null) { return null; } - return alignVideoSizeV21(videoCapabilities, width, height); + return alignVideoSize(videoCapabilities, width, height); } /** * Whether the decoder supports audio with a given sample rate. * - *

Must not be called if the device SDK version is less than 21. - * * @param sampleRate The sample rate in Hz. * @return Whether the decoder supports audio with the given sample rate. */ - @RequiresApi(21) public boolean isAudioSampleRateSupportedV21(int sampleRate) { if (capabilities == null) { logNoSupport("sampleRate.caps"); @@ -575,12 +558,9 @@ public final class MediaCodecInfo { /** * Whether the decoder supports audio with a given channel count. * - *

Must not be called if the device SDK version is less than 21. - * * @param channelCount The channel count. * @return Whether the decoder supports audio with the given channel count. */ - @RequiresApi(21) public boolean isAudioChannelCountSupportedV21(int channelCount) { if (capabilities == null) { logNoSupport("channelCount.caps"); @@ -674,28 +654,17 @@ public final class MediaCodecInfo { } private static boolean isTunneling(CodecCapabilities capabilities) { - return Util.SDK_INT >= 21 && isTunnelingV21(capabilities); - } - - @RequiresApi(21) - private static boolean isTunnelingV21(CodecCapabilities capabilities) { return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_TunneledPlayback); } private static boolean isSecure(CodecCapabilities capabilities) { - return Util.SDK_INT >= 21 && isSecureV21(capabilities); - } - - @RequiresApi(21) - private static boolean isSecureV21(CodecCapabilities capabilities) { return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback); } - @RequiresApi(21) - private static boolean areSizeAndRateSupportedV21( + private static boolean areSizeAndRateSupported( VideoCapabilities capabilities, int width, int height, double frameRate) { // Don't ever fail due to alignment. See: https://github.com/google/ExoPlayer/issues/6551. - Point alignedSize = alignVideoSizeV21(capabilities, width, height); + Point alignedSize = alignVideoSize(capabilities, width, height); width = alignedSize.x; height = alignedSize.y; @@ -712,8 +681,7 @@ public final class MediaCodecInfo { } } - @RequiresApi(21) - private static Point alignVideoSizeV21(VideoCapabilities capabilities, int width, int height) { + private static Point alignVideoSize(VideoCapabilities capabilities, int width, int height) { int widthAlignment = capabilities.getWidthAlignment(); int heightAlignment = capabilities.getHeightAlignment(); return new Point( diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java index 4463864add..257b0cb9d5 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java @@ -166,12 +166,9 @@ public final class MediaCodecUtil { if (cachedDecoderInfos != null) { return cachedDecoderInfos; } - MediaCodecListCompat mediaCodecList = - Util.SDK_INT >= 21 - ? new MediaCodecListCompatV21(secure, tunneling) - : new MediaCodecListCompatV16(); + MediaCodecListCompat mediaCodecList = new MediaCodecListCompatV21(secure, tunneling); ArrayList decoderInfos = getDecoderInfosInternal(key, mediaCodecList); - if (secure && decoderInfos.isEmpty() && 21 <= Util.SDK_INT && Util.SDK_INT <= 23) { + if (secure && decoderInfos.isEmpty() && Util.SDK_INT <= 23) { // Some devices don't list secure decoders on API level 21 [Internal: b/18678462]. Try the // legacy path. We also try this path on API levels 22 and 23 as a defensive measure. mediaCodecList = new MediaCodecListCompatV16(); @@ -290,9 +287,8 @@ public final class MediaCodecUtil { for (CodecProfileLevel profileLevel : decoderInfo.getProfileLevels()) { result = max(avcLevelToMaxFrameSize(profileLevel.level), result); } - // We assume support for at least 480p (SDK_INT >= 21) or 360p (SDK_INT < 21), which are - // the levels mandated by the Android CDD. - result = max(result, Util.SDK_INT >= 21 ? (720 * 480) : (480 * 360)); + // We assume support for at least 480p, which is the level mandated by the Android CDD. + result = max(result, 720 * 480); } maxH264DecodableFrameSize = result; } @@ -562,17 +558,6 @@ public final class MediaCodecUtil { return false; } - // Work around broken audio decoders. - if (Util.SDK_INT < 21 - && ("CIPAACDecoder".equals(name) - || "CIPMP3Decoder".equals(name) - || "CIPVorbisDecoder".equals(name) - || "CIPAMRNBDecoder".equals(name) - || "AACDecoder".equals(name) - || "MP3Decoder".equals(name))) { - return false; - } - // Work around https://github.com/google/ExoPlayer/issues/3249. if (Util.SDK_INT < 24 && ("OMX.SEC.aac.dec".equals(name) || "OMX.Exynos.AAC.Decoder".equals(name)) @@ -588,26 +573,6 @@ public final class MediaCodecUtil { return false; } - // Work around https://github.com/google/ExoPlayer/issues/548. - // VP8 decoder on Samsung Galaxy S3/S4/S4 Mini/Tab 3/Note 2 does not render video. - if (Util.SDK_INT == 19 - && "OMX.SEC.vp8.dec".equals(name) - && "samsung".equals(Util.MANUFACTURER) - && (Util.DEVICE.startsWith("d2") - || Util.DEVICE.startsWith("serrano") - || Util.DEVICE.startsWith("jflte") - || Util.DEVICE.startsWith("santos") - || Util.DEVICE.startsWith("t0"))) { - return false; - } - - // VP8 decoder on Samsung Galaxy S4 cannot be queried. - if (Util.SDK_INT == 19 - && Util.DEVICE.startsWith("jflte") - && "OMX.qcom.video.decoder.vp8".equals(name)) { - return false; - } - // MTK AC3 decoder doesn't support decoding JOC streams in 2-D. See [Internal: b/69400041]. if (Util.SDK_INT <= 23 && MimeTypes.AUDIO_E_AC3_JOC.equals(mimeType) @@ -663,19 +628,6 @@ public final class MediaCodecUtil { }); } - if (Util.SDK_INT < 21 && decoderInfos.size() > 1) { - String firstCodecName = decoderInfos.get(0).name; - if ("OMX.SEC.mp3.dec".equals(firstCodecName) - || "OMX.SEC.MP3.Decoder".equals(firstCodecName) - || "OMX.brcm.audio.mp3.decoder".equals(firstCodecName)) { - // Prefer OMX.google codecs over OMX.SEC.mp3.dec, OMX.SEC.MP3.Decoder and - // OMX.brcm.audio.mp3.decoder on older devices. See: - // https://github.com/google/ExoPlayer/issues/398 and - // https://github.com/google/ExoPlayer/issues/4519. - sortByScore(decoderInfos, decoderInfo -> decoderInfo.name.startsWith("OMX.google") ? 1 : 0); - } - } - if (Util.SDK_INT < 32 && decoderInfos.size() > 1) { String firstCodecName = decoderInfos.get(0).name; // Prefer anything other than OMX.qti.audio.decoder.flac on older devices. See [Internal @@ -1056,7 +1008,6 @@ public final class MediaCodecUtil { boolean isFeatureRequired(String feature, String mimeType, CodecCapabilities capabilities); } - @RequiresApi(21) private static final class MediaCodecListCompatV21 implements MediaCodecListCompat { private final int codecKind; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/SynchronousMediaCodecAdapter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/SynchronousMediaCodecAdapter.java index 405013fbf9..9b9a707a39 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/SynchronousMediaCodecAdapter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/SynchronousMediaCodecAdapter.java @@ -17,7 +17,6 @@ package androidx.media3.exoplayer.mediacodec; import static androidx.media3.common.util.Assertions.checkNotNull; -import static androidx.media3.common.util.Util.castNonNull; import android.media.MediaCodec; import android.media.MediaFormat; @@ -80,15 +79,9 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter { } private final MediaCodec codec; - @Nullable private ByteBuffer[] inputByteBuffers; - @Nullable private ByteBuffer[] outputByteBuffers; private SynchronousMediaCodecAdapter(MediaCodec mediaCodec) { this.codec = mediaCodec; - if (Util.SDK_INT < 21) { - inputByteBuffers = codec.getInputBuffers(); - outputByteBuffers = codec.getOutputBuffers(); - } } @Override @@ -106,9 +99,6 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter { int index; do { index = codec.dequeueOutputBuffer(bufferInfo, 0); - if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED && Util.SDK_INT < 21) { - outputByteBuffers = codec.getOutputBuffers(); - } } while (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED); return index; @@ -122,21 +112,13 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter { @Override @Nullable public ByteBuffer getInputBuffer(int index) { - if (Util.SDK_INT >= 21) { - return codec.getInputBuffer(index); - } else { - return castNonNull(inputByteBuffers)[index]; - } + return codec.getInputBuffer(index); } @Override @Nullable public ByteBuffer getOutputBuffer(int index) { - if (Util.SDK_INT >= 21) { - return codec.getOutputBuffer(index); - } else { - return castNonNull(outputByteBuffers)[index]; - } + return codec.getOutputBuffer(index); } @Override @@ -158,7 +140,6 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter { } @Override - @RequiresApi(21) public void releaseOutputBuffer(int index, long renderTimeStampNs) { codec.releaseOutputBuffer(index, renderTimeStampNs); } @@ -170,8 +151,6 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter { @Override public void release() { - inputByteBuffers = null; - outputByteBuffers = null; try { if (Util.SDK_INT >= 30 && Util.SDK_INT < 33) { // Stopping the codec before releasing it works around a bug on APIs 30, 31 and 32 where diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/PlatformScheduler.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/PlatformScheduler.java index 4c6dd8ecff..a6901290cb 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/PlatformScheduler.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/PlatformScheduler.java @@ -25,7 +25,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.PersistableBundle; -import androidx.annotation.RequiresApi; import androidx.annotation.RequiresPermission; import androidx.media3.common.util.Log; import androidx.media3.common.util.UnstableApi; @@ -44,7 +43,6 @@ import androidx.media3.common.util.Util; * android:exported="true"/> * } */ -@RequiresApi(21) @UnstableApi public final class PlatformScheduler implements Scheduler { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java index a537bd4866..1fb98e7da5 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java @@ -202,9 +202,7 @@ public final class Requirements implements Parcelable { private boolean isDeviceIdle(Context context) { PowerManager powerManager = (PowerManager) Assertions.checkNotNull(context.getSystemService(Context.POWER_SERVICE)); - return Util.SDK_INT >= 23 - ? powerManager.isDeviceIdleMode() - : Util.SDK_INT >= 20 ? !powerManager.isInteractive() : !powerManager.isScreenOn(); + return Util.SDK_INT >= 23 ? powerManager.isDeviceIdleMode() : !powerManager.isInteractive(); } private boolean isStorageNotLow(Context context) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java index ae9d7f71af..9ca0c39bd8 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java @@ -58,13 +58,10 @@ import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.Executor; -import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -491,7 +488,6 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi private final VideoFrameReleaseControl.FrameReleaseInfo frameReleaseInfo; private @MonotonicNonNull VideoFrameProcessor videoFrameProcessor; - @Nullable private Effect rotationEffect; @Nullable private Format inputFormat; private @InputType int inputType; private long inputStreamStartPositionUs; @@ -611,21 +607,6 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi throw new UnsupportedOperationException("Unsupported input type " + inputType); } videoFrameReleaseControl.setFrameRate(format.frameRate); - // MediaCodec applies rotation after API 21. - if (inputType == INPUT_TYPE_SURFACE - && Util.SDK_INT < 21 - && format.rotationDegrees != Format.NO_VALUE - && format.rotationDegrees != 0) { - // We must apply a rotation effect. - if (rotationEffect == null - || this.inputFormat == null - || this.inputFormat.rotationDegrees != format.rotationDegrees) { - rotationEffect = ScaleAndRotateAccessor.createRotationEffect(format.rotationDegrees); - } // Else, the rotation effect matches the previous format's rotation degrees, keep the same - // instance. - } else { - rotationEffect = null; - } this.inputType = inputType; this.inputFormat = format; @@ -884,9 +865,6 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi } ArrayList effects = new ArrayList<>(); - if (rotationEffect != null) { - effects.add(rotationEffect); - } effects.addAll(videoEffects); Format inputFormat = checkNotNull(this.inputFormat); checkStateNotNull(videoFrameProcessor) @@ -1081,42 +1059,4 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi listener); } } - - private static final class ScaleAndRotateAccessor { - private static @MonotonicNonNull Constructor scaleAndRotateTransformationBuilderConstructor; - private static @MonotonicNonNull Method setRotationMethod; - private static @MonotonicNonNull Method buildScaleAndRotateTransformationMethod; - - public static Effect createRotationEffect(float rotationDegrees) { - try { - prepare(); - Object builder = scaleAndRotateTransformationBuilderConstructor.newInstance(); - setRotationMethod.invoke(builder, rotationDegrees); - return (Effect) checkNotNull(buildScaleAndRotateTransformationMethod.invoke(builder)); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - - @EnsuresNonNull({ - "scaleAndRotateTransformationBuilderConstructor", - "setRotationMethod", - "buildScaleAndRotateTransformationMethod" - }) - private static void prepare() throws NoSuchMethodException, ClassNotFoundException { - if (scaleAndRotateTransformationBuilderConstructor == null - || setRotationMethod == null - || buildScaleAndRotateTransformationMethod == null) { - // TODO: b/284964524 - Add LINT and proguard checks for media3.effect reflection. - Class scaleAndRotateTransformationBuilderClass = - Class.forName("androidx.media3.effect.ScaleAndRotateTransformation$Builder"); - scaleAndRotateTransformationBuilderConstructor = - scaleAndRotateTransformationBuilderClass.getConstructor(); - setRotationMethod = - scaleAndRotateTransformationBuilderClass.getMethod("setRotationDegrees", float.class); - buildScaleAndRotateTransformationMethod = - scaleAndRotateTransformationBuilderClass.getMethod("build"); - } - } - } } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java index fc3dda6290..0360b1694a 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java @@ -18,7 +18,6 @@ package androidx.media3.exoplayer.audio; import static androidx.media3.common.C.FORMAT_HANDLED; import static androidx.media3.exoplayer.RendererCapabilities.ADAPTIVE_NOT_SEAMLESS; import static androidx.media3.exoplayer.RendererCapabilities.DECODER_SUPPORT_PRIMARY; -import static androidx.media3.exoplayer.RendererCapabilities.TUNNELING_NOT_SUPPORTED; import static androidx.media3.exoplayer.RendererCapabilities.TUNNELING_SUPPORTED; import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM; import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.oneByteSample; @@ -96,17 +95,6 @@ public class DecoderAudioRendererTest { audioRenderer.init(/* index= */ 0, PlayerId.UNSET, Clock.DEFAULT); } - @Config(sdk = 19) - @Test - public void supportsFormatAtApi19() { - assertThat(audioRenderer.supportsFormat(FORMAT)) - .isEqualTo( - ADAPTIVE_NOT_SEAMLESS - | TUNNELING_NOT_SUPPORTED - | FORMAT_HANDLED - | DECODER_SUPPORT_PRIMARY); - } - @Config(sdk = 21) @Test public void supportsFormatAtApi21() { diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioSinkTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioSinkTest.java index 5b6295ac81..079e518dc4 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioSinkTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioSinkTest.java @@ -315,20 +315,6 @@ public final class DefaultAudioSinkTest { .isEqualTo(SINK_FORMAT_SUPPORTED_WITH_TRANSCODING); } - @Config(maxSdk = 20) - @Test - public void floatPcmNeedsTranscodingIfFloatOutputEnabledBeforeApi21() { - defaultAudioSink = new DefaultAudioSink.Builder().setEnableFloatOutput(true).build(); - Format floatFormat = - STEREO_44_1_FORMAT - .buildUpon() - .setSampleMimeType(MimeTypes.AUDIO_RAW) - .setPcmEncoding(C.ENCODING_PCM_FLOAT) - .build(); - assertThat(defaultAudioSink.getFormatSupport(floatFormat)) - .isEqualTo(SINK_FORMAT_SUPPORTED_WITH_TRANSCODING); - } - @Config(minSdk = 21) @Test public void floatOutputSupportedIfFloatOutputEnabledFromApi21() { diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4MoovStructure.java b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4MoovStructure.java index 4c38543a38..126f8090bc 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4MoovStructure.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4MoovStructure.java @@ -21,7 +21,6 @@ import android.media.MediaCodec.BufferInfo; import androidx.media3.common.C; import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; -import androidx.media3.common.util.Util; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.nio.ByteBuffer; @@ -218,8 +217,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull; return null; } - Locale locale = - Util.SDK_INT >= 21 ? Locale.forLanguageTag(languageTag) : new Locale(languageTag); + Locale locale = Locale.forLanguageTag(languageTag); return locale.getISO3Language().isEmpty() ? languageTag : locale.getISO3Language(); } diff --git a/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java b/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java index b4460fcd99..f103c9010e 100644 --- a/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java +++ b/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java @@ -366,7 +366,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi } } - if (player.isCommandAvailable(COMMAND_STOP) || Util.SDK_INT < 21) { + if (player.isCommandAvailable(COMMAND_STOP)) { // We must include a cancel intent for pre-L devices. mediaStyle.setCancelButtonIntent( actionFactory.createMediaActionPendingIntent(mediaSession, COMMAND_STOP)); @@ -639,9 +639,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi } private static long getPlaybackStartTimeEpochMs(Player player) { - // Changing "showWhen" causes notification flicker if SDK_INT < 21. - if (Util.SDK_INT >= 21 - && player.isPlaying() + if (player.isPlaying() && !player.isPlayingAd() && !player.isCurrentMediaItemDynamic() && player.getPlaybackParameters().speed == 1f) { diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java index e8c7d50280..26a19bdfda 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java @@ -98,13 +98,8 @@ public abstract class MediaLibraryService extends MediaSessionService { * {@link ControllerInfo#getPackageName()}
for legacy browser * {@link ControllerInfo#getUid()}
for legacy browser * - * {@code SDK_INT < 21} - * Actual package name via {@link Context#getPackageName()} - * Actual UID - * - * * - * {@code 21 <= SDK_INT < 28}
+ * {@code SDK_INT < 28}
* for {@link Callback#onConnect onConnect}
* and {@link Callback#onGetLibraryRoot onGetLibraryRoot} * @@ -113,7 +108,7 @@ public abstract class MediaLibraryService extends MediaSessionService { * * * - * {@code 21 <= SDK_INT < 28}
+ * {@code SDK_INT < 28}
* for other {@link Callback callbacks} * * {@link ControllerInfo#LEGACY_CONTROLLER_PACKAGE_NAME} diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java b/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java index f8f79e4de4..573c958fd2 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java @@ -210,14 +210,11 @@ import java.util.concurrent.TimeoutException; MediaSession session, MediaNotification mediaNotification, boolean startInForegroundRequired) { - if (Util.SDK_INT >= 21) { - // Call Notification.MediaStyle#setMediaSession() indirectly. - android.media.session.MediaSession.Token fwkToken = - (android.media.session.MediaSession.Token) - session.getSessionCompat().getSessionToken().getToken(); - mediaNotification.notification.extras.putParcelable( - Notification.EXTRA_MEDIA_SESSION, fwkToken); - } + // Call Notification.MediaStyle#setMediaSession() indirectly. + android.media.session.MediaSession.Token fwkToken = + (android.media.session.MediaSession.Token) + session.getSessionCompat().getSessionToken().getToken(); + mediaNotification.notification.extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, fwkToken); this.mediaNotification = mediaNotification; if (startInForegroundRequired) { startForeground(mediaNotification); @@ -379,9 +376,7 @@ import java.util.concurrent.TimeoutException; if (Util.SDK_INT >= 24) { Api24.stopForeground(mediaSessionService, removeNotifications); } else { - // For pre-L devices, we must call Service.stopForeground(true) anyway as a workaround - // that prevents the media notification from being undismissable. - mediaSessionService.stopForeground(removeNotifications || Util.SDK_INT < 21); + mediaSessionService.stopForeground(removeNotifications); } startedInForeground = false; } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java index 7f19a7a7f2..56f1529113 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java @@ -25,7 +25,6 @@ import static androidx.media3.session.SessionResult.RESULT_SUCCESS; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; @@ -524,25 +523,7 @@ public class MediaSession { return interfaceVersion; } - /** - * Returns the package name. Can be {@link #LEGACY_CONTROLLER_PACKAGE_NAME} for - * interoperability. - * - *

Interoperability: Package name may not be precisely obtained for legacy controller API on - * older device. Here are details. - * - * - * - * - * - * - * - * - * - *
Summary when package name isn't precise
SDK version when package name isn't precise{@code ControllerInfo#getPackageName()} for legacy controller
{@code SDK_INT < 21}Actual package name via {@link PackageManager#getNameForUid} with UID.
- * It's sufficient for most cases, but doesn't precisely distinguish caller if it - * uses shared user ID.
{@code 21 <= SDK_INT < 24}{@link #LEGACY_CONTROLLER_PACKAGE_NAME}
- */ + /** Returns the package name, or {@link #LEGACY_CONTROLLER_PACKAGE_NAME} on API ≤ 24. */ public String getPackageName() { return remoteUserInfo.getPackageName(); } @@ -550,8 +531,8 @@ public class MediaSession { /** * Returns the UID of the controller. Can be a negative value for interoperability. * - *

Interoperability: If {@code 21 <= SDK_INT < 28}, then UID would be a negative value - * because it cannot be obtained. + *

Interoperability: If {@code SDK_INT < 28}, then UID would be a negative value because it + * cannot be obtained. */ public int getUid() { return remoteUserInfo.getUid(); @@ -1221,7 +1202,6 @@ public class MediaSession { * android.media.session.MediaSession} created internally by this session. */ @SuppressWarnings("UnnecessarilyFullyQualified") // Avoiding clash with Media3 token. - @RequiresApi(21) @UnstableApi public final android.media.session.MediaSession.Token getPlatformToken() { return (android.media.session.MediaSession.Token) diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java index b14554b65f..3c16e25e04 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java @@ -53,11 +53,9 @@ import android.os.SystemClock; import android.view.KeyEvent; import android.view.ViewConfiguration; import androidx.annotation.CheckResult; -import androidx.annotation.DoNotInline; import androidx.annotation.FloatRange; import androidx.annotation.GuardedBy; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.media3.common.AudioAttributes; import androidx.media3.common.DeviceInfo; import androidx.media3.common.MediaItem; @@ -1213,7 +1211,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } // Double tap detection. int keyCode = keyEvent.getKeyCode(); - boolean isTvApp = Util.SDK_INT >= 21 && Api21.isTvApp(context); + boolean isTvApp = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK); boolean doubleTapCompleted = false; switch (keyCode) { case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: @@ -1910,12 +1908,4 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } } } - - @RequiresApi(21) - private static final class Api21 { - @DoNotInline - public static boolean isTvApp(Context context) { - return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK); - } - } } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java index e787973cd7..9fbbd04e80 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java @@ -35,7 +35,6 @@ import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkStateNotNull; import static androidx.media3.common.util.Util.castNonNull; import static androidx.media3.common.util.Util.postOrRun; -import static androidx.media3.session.MediaUtils.TRANSACTION_SIZE_LIMIT_IN_BYTES; import static androidx.media3.session.SessionCommand.COMMAND_CODE_CUSTOM; import static androidx.media3.session.SessionError.ERROR_UNKNOWN; import static androidx.media3.session.SessionResult.RESULT_INFO_SKIPPED; @@ -1226,7 +1225,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; () -> { int completedBitmapFutureCount = resultCount.incrementAndGet(); if (completedBitmapFutureCount == mediaItemList.size()) { - handleBitmapFuturesAllCompletedAndSetQueue(bitmapFutures, timeline, mediaItemList); + handleBitmapFuturesAllCompletedAndSetQueue(bitmapFutures, mediaItemList); } }; @@ -1247,9 +1246,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; } private void handleBitmapFuturesAllCompletedAndSetQueue( - List<@NullableType ListenableFuture> bitmapFutures, - Timeline timeline, - List mediaItems) { + List<@NullableType ListenableFuture> bitmapFutures, List mediaItems) { List queueItemList = new ArrayList<>(); for (int i = 0; i < bitmapFutures.size(); i++) { @Nullable ListenableFuture future = bitmapFutures.get(i); @@ -1264,22 +1261,9 @@ import org.checkerframework.checker.initialization.qual.Initialized; queueItemList.add(LegacyConversions.convertToQueueItem(mediaItems.get(i), i, bitmap)); } - if (Util.SDK_INT < 21) { - // In order to avoid TransactionTooLargeException for below API 21, we need to - // cut the list so that it doesn't exceed the binder transaction limit. - List truncatedList = - MediaUtils.truncateListBySize(queueItemList, TRANSACTION_SIZE_LIMIT_IN_BYTES); - if (truncatedList.size() != timeline.getWindowCount()) { - Log.i( - TAG, - "Sending " + truncatedList.size() + " items out of " + timeline.getWindowCount()); - } - setQueue(sessionCompat, truncatedList); - } else { - // Framework MediaSession#setQueue() uses ParceledListSlice, - // which means we can safely send long lists. - setQueue(sessionCompat, queueItemList); - } + // Framework MediaSession#setQueue() uses ParceledListSlice, + // which means we can safely send long lists. + setQueue(sessionCompat, queueItemList); } @Override diff --git a/libraries/session/src/main/java/androidx/media3/session/SessionToken.java b/libraries/session/src/main/java/androidx/media3/session/SessionToken.java index a6bdce02d1..ea86649dc6 100644 --- a/libraries/session/src/main/java/androidx/media3/session/SessionToken.java +++ b/libraries/session/src/main/java/androidx/media3/session/SessionToken.java @@ -300,8 +300,7 @@ public final class SessionToken { private static MediaSessionCompat.Token createCompatToken( Parcelable platformOrLegacyCompatToken) { - if (Util.SDK_INT >= 21 - && platformOrLegacyCompatToken instanceof android.media.session.MediaSession.Token) { + if (platformOrLegacyCompatToken instanceof android.media.session.MediaSession.Token) { return MediaSessionCompat.Token.fromToken(platformOrLegacyCompatToken); } // Assume this is an android.support.v4.media.session.MediaSessionCompat.Token. diff --git a/libraries/test_exoplayer_playback/src/androidTest/java/androidx/media3/test/exoplayer/playback/gts/DashStreamingTest.java b/libraries/test_exoplayer_playback/src/androidTest/java/androidx/media3/test/exoplayer/playback/gts/DashStreamingTest.java index 2fd7536a4a..4eaa724909 100644 --- a/libraries/test_exoplayer_playback/src/androidTest/java/androidx/media3/test/exoplayer/playback/gts/DashStreamingTest.java +++ b/libraries/test_exoplayer_playback/src/androidTest/java/androidx/media3/test/exoplayer/playback/gts/DashStreamingTest.java @@ -666,7 +666,7 @@ public final class DashStreamingTest { MediaCodecUtil.getDecoderInfo( MimeTypes.VIDEO_H264, /* secure= */ false, /* tunneling= */ false); assertThat(decoderInfo).isNotNull(); - assertThat(Util.SDK_INT < 21 || decoderInfo.adaptive).isTrue(); + assertThat(decoderInfo.adaptive).isTrue(); } @Test diff --git a/libraries/test_exoplayer_playback/src/androidTest/java/androidx/media3/test/exoplayer/playback/gts/DebugRenderersFactory.java b/libraries/test_exoplayer_playback/src/androidTest/java/androidx/media3/test/exoplayer/playback/gts/DebugRenderersFactory.java index e7a7315a0d..e676b76101 100644 --- a/libraries/test_exoplayer_playback/src/androidTest/java/androidx/media3/test/exoplayer/playback/gts/DebugRenderersFactory.java +++ b/libraries/test_exoplayer_playback/src/androidTest/java/androidx/media3/test/exoplayer/playback/gts/DebugRenderersFactory.java @@ -17,13 +17,11 @@ package androidx.media3.test.exoplayer.playback.gts; import static java.lang.Math.max; -import android.annotation.SuppressLint; import android.content.Context; import android.media.MediaCrypto; import android.media.MediaFormat; import android.os.Handler; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.media3.common.C; import androidx.media3.common.Format; import androidx.media3.decoder.DecoderInputBuffer; @@ -244,8 +242,6 @@ import java.util.ArrayList; super.renderOutputBuffer(codec, index, presentationTimeUs); } - @SuppressLint("UseSdkSuppress") // Not a test class or method. - @RequiresApi(21) @Override protected void renderOutputBufferV21( MediaCodecAdapter codec, int index, long presentationTimeUs, long releaseTimeNs) { diff --git a/libraries/test_exoplayer_playback/src/androidTest/java/androidx/media3/test/exoplayer/playback/gts/EnumerateDecodersTest.java b/libraries/test_exoplayer_playback/src/androidTest/java/androidx/media3/test/exoplayer/playback/gts/EnumerateDecodersTest.java index 742e835e1f..480c8059a1 100644 --- a/libraries/test_exoplayer_playback/src/androidTest/java/androidx/media3/test/exoplayer/playback/gts/EnumerateDecodersTest.java +++ b/libraries/test_exoplayer_playback/src/androidTest/java/androidx/media3/test/exoplayer/playback/gts/EnumerateDecodersTest.java @@ -115,9 +115,7 @@ public class EnumerateDecodersTest { boolean isAudio = MimeTypes.isAudio(requestedMimeType); StringBuilder result = new StringBuilder(); result.append("[requestedMimeType=").append(requestedMimeType); - if (Util.SDK_INT >= 21) { - result.append(", mimeType=").append(codecCapabilities.getMimeType()); - } + result.append(", mimeType=").append(codecCapabilities.getMimeType()); result.append(", profileLevels="); appendProfileLevels(codecCapabilities.profileLevels, result); if (Util.SDK_INT >= 23) { @@ -125,23 +123,19 @@ public class EnumerateDecodersTest { .append(", maxSupportedInstances=") .append(codecCapabilities.getMaxSupportedInstances()); } - if (Util.SDK_INT >= 21) { - if (isVideo) { - result.append(", videoCapabilities="); - appendVideoCapabilities(codecCapabilities.getVideoCapabilities(), result); - result.append(", colorFormats=").append(Arrays.toString(codecCapabilities.colorFormats)); - } else if (isAudio) { - result.append(", audioCapabilities="); - appendAudioCapabilities(codecCapabilities.getAudioCapabilities(), result); - } + if (isVideo) { + result.append(", videoCapabilities="); + appendVideoCapabilities(codecCapabilities.getVideoCapabilities(), result); + result.append(", colorFormats=").append(Arrays.toString(codecCapabilities.colorFormats)); + } else if (isAudio) { + result.append(", audioCapabilities="); + appendAudioCapabilities(codecCapabilities.getAudioCapabilities(), result); } if (isVideo && codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback)) { result.append(", FEATURE_AdaptivePlayback"); } - if (Util.SDK_INT >= 21 - && isVideo - && codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback)) { + if (isVideo && codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback)) { result.append(", FEATURE_SecurePlayback"); } if (Util.SDK_INT >= 26 @@ -149,8 +143,7 @@ public class EnumerateDecodersTest { && codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_PartialFrame)) { result.append(", FEATURE_PartialFrame"); } - if (Util.SDK_INT >= 21 - && (isVideo || isAudio) + if ((isVideo || isAudio) && codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_TunneledPlayback)) { result.append(", FEATURE_TunneledPlayback"); } diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java index 32f1d22555..72998dc4be 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java @@ -610,7 +610,7 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { session.setPlayer(playerConfigToUpdate); // In API 21 and 22, onAudioInfoChanged is not called when playback is changed to local. - if (Util.SDK_INT == 21 || Util.SDK_INT == 22) { + if (Util.SDK_INT <= 22) { PollingCheck.waitFor( TIMEOUT_MS, () -> { @@ -657,21 +657,13 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { session.setPlayer(playerConfig); - // In API 21+, onAudioInfoChanged() is not called when playbackType is not changed. - if (Util.SDK_INT >= 21) { - PollingCheck.waitFor( - TIMEOUT_MS, - () -> { - MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo(); - return info.getPlaybackType() == legacyPlaybackType - && info.getAudioAttributes().getLegacyStreamType() == legacyStream; - }); - } else { - assertThat(playbackInfoNotified.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo(); - assertThat(info.getPlaybackType()).isEqualTo(legacyPlaybackType); - assertThat(info.getAudioAttributes().getLegacyStreamType()).isEqualTo(legacyStream); - } + PollingCheck.waitFor( + TIMEOUT_MS, + () -> { + MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo(); + return info.getPlaybackType() == legacyPlaybackType + && info.getAudioAttributes().getLegacyStreamType() == legacyStream; + }); } @Test @@ -709,23 +701,14 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { session.setPlayer(playerConfigToUpdate); - // In API 21+, onAudioInfoChanged() is not called when playbackType is not changed. - if (Util.SDK_INT >= 21) { - PollingCheck.waitFor( - TIMEOUT_MS, - () -> { - MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo(); - return info.getPlaybackType() == legacyPlaybackTypeToUpdate - && info.getMaxVolume() == deviceInfoToUpdate.maxVolume - && info.getCurrentVolume() == deviceVolumeToUpdate; - }); - } else { - assertThat(playbackInfoNotified.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo(); - assertThat(info.getPlaybackType()).isEqualTo(legacyPlaybackTypeToUpdate); - assertThat(info.getMaxVolume()).isEqualTo(deviceInfoToUpdate.maxVolume); - assertThat(info.getCurrentVolume()).isEqualTo(deviceVolumeToUpdate); - } + PollingCheck.waitFor( + TIMEOUT_MS, + () -> { + MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo(); + return info.getPlaybackType() == legacyPlaybackTypeToUpdate + && info.getMaxVolume() == deviceInfoToUpdate.maxVolume + && info.getCurrentVolume() == deviceVolumeToUpdate; + }); } @Test @@ -1555,15 +1538,8 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { assertThat(latch.await(LONG_TIMEOUT_MS, MILLISECONDS)).isTrue(); List queueFromParam = queueRef.get(); List queueFromGetter = controllerCompat.getQueue(); - if (Util.SDK_INT >= 21) { - assertThat(queueFromParam).hasSize(listSize); - assertThat(queueFromGetter).hasSize(listSize); - } else { - // Below API 21, only the initial part of the playlist is sent to the - // MediaControllerCompat when the list is too long. - assertThat(queueFromParam.size() < listSize).isTrue(); - assertThat(queueFromGetter).hasSize(queueFromParam.size()); - } + assertThat(queueFromParam).hasSize(listSize); + assertThat(queueFromGetter).hasSize(listSize); for (int i = 0; i < queueFromParam.size(); i++) { assertThat(queueFromParam.get(i).getDescription().getMediaId()) .isEqualTo(TestUtils.getMediaIdInFakeTimeline(i)); diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerWithMediaSessionCompatTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerWithMediaSessionCompatTest.java index dc128f6093..faf37a909c 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerWithMediaSessionCompatTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerWithMediaSessionCompatTest.java @@ -347,7 +347,7 @@ public class MediaControllerListenerWithMediaSessionCompatTest { // We need to trigger MediaControllerCompat.Callback.onAudioInfoChanged in order to raise the // onAudioAttributesChanged() callback. In API 21 and 22, onAudioInfoChanged is not called when // playback is changed to local. - assumeTrue(Util.SDK_INT != 21 && Util.SDK_INT != 22); + assumeTrue(Util.SDK_INT > 22); session.setPlaybackToRemote( /* volumeControl= */ VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE, diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerMediaSessionCompatCallbackAggregationTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerMediaSessionCompatCallbackAggregationTest.java index cfc7147d25..a4fa6e90b0 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerMediaSessionCompatCallbackAggregationTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerMediaSessionCompatCallbackAggregationTest.java @@ -49,7 +49,6 @@ import androidx.media3.common.Player.PositionInfo; import androidx.media3.common.Timeline; import androidx.media3.common.Timeline.Window; import androidx.media3.common.util.BitmapLoader; -import androidx.media3.common.util.Util; import androidx.media3.datasource.DataSourceBitmapLoader; import androidx.media3.test.session.common.HandlerThreadTestRule; import androidx.test.core.app.ApplicationProvider; @@ -200,24 +199,6 @@ public class MediaControllerMediaSessionCompatCallbackAggregationTest { MediaItem mediaItem = timelineRef.get().getWindow(i, new Window()).mediaItem; MediaItem expectedMediaItem = (i == testMediaItemIndex) ? testCurrentMediaItem : testMediaItems.get(i); - if (Util.SDK_INT < 21) { - // Bitmap conversion and back gives not exactly the same byte array below API 21 - MediaMetadata mediaMetadata = - mediaItem - .mediaMetadata - .buildUpon() - .setArtworkData(/* artworkData= */ null, /* artworkDataType= */ null) - .build(); - MediaMetadata expectedMediaMetadata = - expectedMediaItem - .mediaMetadata - .buildUpon() - .setArtworkData(/* artworkData= */ null, /* artworkDataType= */ null) - .build(); - mediaItem = mediaItem.buildUpon().setMediaMetadata(mediaMetadata).build(); - expectedMediaItem = - expectedMediaItem.buildUpon().setMediaMetadata(expectedMediaMetadata).build(); - } assertThat(mediaItem).isEqualTo(expectedMediaItem); } assertThat(timelineChangeReasonRef.get()).isEqualTo(TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED); diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerWithFrameworkMediaSessionTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerWithFrameworkMediaSessionTest.java index 0af19ddefb..118febc3a6 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerWithFrameworkMediaSessionTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerWithFrameworkMediaSessionTest.java @@ -27,7 +27,6 @@ import android.media.session.PlaybackState; import android.os.HandlerThread; import androidx.media3.common.Player; import androidx.media3.common.Player.State; -import androidx.media3.common.util.Util; import androidx.media3.test.session.common.MainLooperTestRule; import androidx.media3.test.session.common.TestHandler; import androidx.test.core.app.ApplicationProvider; @@ -37,7 +36,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.junit.After; -import org.junit.Assume; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; @@ -57,9 +55,6 @@ public class MediaControllerWithFrameworkMediaSessionTest { @Before public void setUp() { - if (Util.SDK_INT < 21) { - return; - } context = ApplicationProvider.getApplicationContext(); HandlerThread handlerThread = new HandlerThread(TAG); @@ -79,7 +74,6 @@ public class MediaControllerWithFrameworkMediaSessionTest { @SuppressWarnings("UnnecessarilyFullyQualified") // Intentionally fully qualified for fwk session. @Test public void createController() throws Exception { - Assume.assumeTrue(Util.SDK_INT >= 21); // For framework MediaSession. MediaSession fwkSession = new android.media.session.MediaSession(context, TAG); fwkSession.setActive(true); fwkSession.setFlags( @@ -100,7 +94,6 @@ public class MediaControllerWithFrameworkMediaSessionTest { @SuppressWarnings("UnnecessarilyFullyQualified") // Intentionally fully qualified for fwk session. @Test public void onPlaybackStateChanged_isNotifiedByFwkSessionChanges() throws Exception { - Assume.assumeTrue(Util.SDK_INT >= 21); // For framework MediaSession. MediaSession fwkSession = new android.media.session.MediaSession(context, TAG); fwkSession.setActive(true); fwkSession.setFlags( diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerWithMediaSessionCompatTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerWithMediaSessionCompatTest.java index e9ffd8639f..3d27ff60b0 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerWithMediaSessionCompatTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerWithMediaSessionCompatTest.java @@ -571,11 +571,8 @@ public class MediaControllerWithMediaSessionCompatTest { assertThat(TextUtils.equals(metadata.subtitle, testSubtitle)).isTrue(); assertThat(TextUtils.equals(metadata.description, testDescription)).isTrue(); assertThat(metadata.artworkUri).isEqualTo(testIconUri); - if (Util.SDK_INT >= 21) { - // Bitmap conversion and back gives not exactly the same byte array below API 21 - assertThat(metadata.artworkData).isEqualTo(testArtworkData); - } - if (Util.SDK_INT < 21 || Util.SDK_INT >= 23) { + assertThat(metadata.artworkData).isEqualTo(testArtworkData); + if (Util.SDK_INT >= 23) { // TODO(b/199055952): Test mediaUri for all API levels once the bug is fixed. assertThat(mediaItem.requestMetadata.mediaUri).isEqualTo(testMediaUri); } @@ -909,19 +906,6 @@ public class MediaControllerWithMediaSessionCompatTest { threadTestRule.getHandler().postAndSync(controller::getMediaMetadata); assertThat(mediaMetadata.artworkData).isNotNull(); - if (Util.SDK_INT < 21) { - // Bitmap conversion and back gives not exactly the same byte array below API 21 - mediaMetadata = - mediaMetadata - .buildUpon() - .setArtworkData(/* artworkData= */ null, /* artworkDataType= */ null) - .build(); - testMediaMetadata = - testMediaMetadata - .buildUpon() - .setArtworkData(/* artworkData= */ null, /* artworkDataType= */ null) - .build(); - } assertThat(mediaMetadata.artworkData).isEqualTo(testMediaMetadata.artworkData); } @@ -1573,7 +1557,7 @@ public class MediaControllerWithMediaSessionCompatTest { @Test public void setPlaybackToLocal_notifiesDeviceInfoAndVolume() throws Exception { - if (Util.SDK_INT == 21 || Util.SDK_INT == 22) { + if (Util.SDK_INT <= 22) { // In API 21 and 22, onAudioInfoChanged is not called. return; } diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackWithMediaControllerCompatTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackWithMediaControllerCompatTest.java index 4a8a29de90..9b603d1dc1 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackWithMediaControllerCompatTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackWithMediaControllerCompatTest.java @@ -91,7 +91,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest { private static final String TEST_URI = "http://test.test"; private static final String EXPECTED_CONTROLLER_PACKAGE_NAME = - (Util.SDK_INT < 21 || Util.SDK_INT >= 24) ? SUPPORT_APP_PACKAGE_NAME : LEGACY_CONTROLLER; + Util.SDK_INT >= 24 ? SUPPORT_APP_PACKAGE_NAME : LEGACY_CONTROLLER; @ClassRule public static MainLooperTestRule mainLooperTestRule = new MainLooperTestRule(); @@ -1200,7 +1200,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest { @Test public void setVolumeWithLocalVolume() throws Exception { - if (Util.SDK_INT >= 21 && audioManager.isVolumeFixed()) { + if (audioManager.isVolumeFixed()) { // This test is not eligible for this device. return; } @@ -1250,7 +1250,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest { @Test public void adjustVolumeWithLocalVolume() throws Exception { - if (Util.SDK_INT >= 21 && audioManager.isVolumeFixed()) { + if (audioManager.isVolumeFixed()) { // This test is not eligible for this device. return; } diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCompatCallbackWithMediaControllerTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCompatCallbackWithMediaControllerTest.java index 6a0be4b042..dce851bd39 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCompatCallbackWithMediaControllerTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCompatCallbackWithMediaControllerTest.java @@ -541,7 +541,7 @@ public class MediaSessionCompatCallbackWithMediaControllerTest { @Test public void setDeviceVolume_forLocalPlayback_setsStreamVolume() throws Exception { - if (Util.SDK_INT >= 21 && audioManager.isVolumeFixed()) { + if (audioManager.isVolumeFixed()) { // This test is not eligible for this device. return; } @@ -570,7 +570,7 @@ public class MediaSessionCompatCallbackWithMediaControllerTest { @Test public void increaseDeviceVolume_forLocalPlayback_increasesStreamVolume() throws Exception { - if (Util.SDK_INT >= 21 && audioManager.isVolumeFixed()) { + if (audioManager.isVolumeFixed()) { // This test is not eligible for this device. return; } @@ -600,7 +600,7 @@ public class MediaSessionCompatCallbackWithMediaControllerTest { @Test public void decreaseDeviceVolume_forLocalPlayback_decreasesStreamVolume() throws Exception { - if (Util.SDK_INT >= 21 && audioManager.isVolumeFixed()) { + if (audioManager.isVolumeFixed()) { // This test is not eligible for this device. return; } diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionKeyEventTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionKeyEventTest.java index 9f20f6aeb7..149237e3a6 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionKeyEventTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionKeyEventTest.java @@ -45,7 +45,6 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import org.junit.After; -import org.junit.Assume; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; @@ -79,9 +78,6 @@ public class MediaSessionKeyEventTest { @Before public void setUp() throws Exception { - if (Util.SDK_INT < 21) { - return; - } Context context = ApplicationProvider.getApplicationContext(); audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); handler = threadTestRule.getHandler(); @@ -128,9 +124,6 @@ public class MediaSessionKeyEventTest { @After public void tearDown() throws Exception { - if (Util.SDK_INT < 21) { - return; - } handler.postAndSync( () -> { if (mediaPlayer != null) { @@ -143,7 +136,6 @@ public class MediaSessionKeyEventTest { @Test public void playKeyEvent() throws Exception { - Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19. dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY, false); player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS); @@ -151,7 +143,6 @@ public class MediaSessionKeyEventTest { @Test public void pauseKeyEvent() throws Exception { - Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19. dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PAUSE, false); player.awaitMethodCalled(MockPlayer.METHOD_PAUSE, TIMEOUT_MS); @@ -159,7 +150,6 @@ public class MediaSessionKeyEventTest { @Test public void nextKeyEvent() throws Exception { - Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19. dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_NEXT, false); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_NEXT, TIMEOUT_MS); @@ -167,7 +157,6 @@ public class MediaSessionKeyEventTest { @Test public void previousKeyEvent() throws Exception { - Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19. dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PREVIOUS, false); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_PREVIOUS, TIMEOUT_MS); @@ -177,7 +166,6 @@ public class MediaSessionKeyEventTest { public void fastForwardKeyEvent_mediaNotificationControllerConnected_callFromNotificationController() throws Exception { - Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19. MediaController controller = connectMediaNotificationController(); dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, /* doubleTap= */ false); @@ -204,8 +192,6 @@ public class MediaSessionKeyEventTest { public void fastForwardKeyEvent_mediaNotificationControllerNotConnected_callFromLegacyFallbackController() throws Exception { - Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19. - dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, false); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_FORWARD, TIMEOUT_MS); @@ -220,7 +206,6 @@ public class MediaSessionKeyEventTest { @Test public void rewindKeyEvent_mediaNotificationControllerConnected_callFromNotificationController() throws Exception { - Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19. MediaController controller = connectMediaNotificationController(); dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_REWIND, false); @@ -246,8 +231,6 @@ public class MediaSessionKeyEventTest { public void rewindKeyEvent_mediaNotificationControllerNotConnected_callFromLegacyFallbackController() throws Exception { - Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19. - dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_REWIND, false); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_BACK, TIMEOUT_MS); @@ -261,7 +244,6 @@ public class MediaSessionKeyEventTest { @Test public void stopKeyEvent() throws Exception { - Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19. dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_STOP, false); player.awaitMethodCalled(MockPlayer.METHOD_STOP, TIMEOUT_MS); @@ -320,7 +302,6 @@ public class MediaSessionKeyEventTest { @Test public void playPauseKeyEvent_playing_pause() throws Exception { - Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19. handler.postAndSync( () -> { player.playWhenReady = true; @@ -334,7 +315,6 @@ public class MediaSessionKeyEventTest { @Test public void playPauseKeyEvent_doubleTapOnPlayPause_seekNext() throws Exception { - Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19. handler.postAndSync( () -> { player.playWhenReady = true; @@ -348,7 +328,6 @@ public class MediaSessionKeyEventTest { @Test public void playPauseKeyEvent_doubleTapOnHeadsetHook_seekNext() throws Exception { - Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19. handler.postAndSync( () -> { player.playWhenReady = true; @@ -390,10 +369,8 @@ public class MediaSessionKeyEventTest { return SUPPORT_APP_PACKAGE_NAME; } // Legacy controllers - if (Util.SDK_INT < 21 || Util.SDK_INT >= 28) { + if (Util.SDK_INT >= 28) { // Above API 28: package of the app using AudioManager. - // Below 21: package of the owner of the session. Note: This is specific to this test setup - // where `ApplicationProvider.getContext().packageName == SUPPORT_APP_PACKAGE_NAME`. return SUPPORT_APP_PACKAGE_NAME; } else if (Util.SDK_INT >= 24) { // API 24 - 27: KeyEvent from system service has the package name "android". diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionTest.java index 3ed6b5fc9c..5d3e2e0fc7 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionTest.java @@ -1014,8 +1014,7 @@ public class MediaSessionTest { *

Calling this method should only be required to test legacy behaviour. */ private static String getControllerCallerPackageName(ControllerInfo controllerInfo) { - return (Util.SDK_INT < 21 - || Util.SDK_INT > 23 + return (Util.SDK_INT > 23 || controllerInfo.getControllerVersion() != ControllerInfo.LEGACY_CONTROLLER_VERSION) ? ApplicationProvider.getApplicationContext().getPackageName() : MediaSessionManager.RemoteUserInfo.LEGACY_CONTROLLER; diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/SessionTokenTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/SessionTokenTest.java index f0efaea3de..e8889938ca 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/SessionTokenTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/SessionTokenTest.java @@ -20,7 +20,6 @@ import static androidx.media3.test.session.common.CommonConstants.MOCK_MEDIA3_SE import static androidx.media3.test.session.common.CommonConstants.SUPPORT_APP_PACKAGE_NAME; import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assume.assumeTrue; import android.content.ComponentName; import android.content.Context; @@ -28,7 +27,6 @@ import android.os.Bundle; import android.os.Process; import android.support.v4.media.session.MediaSessionCompat; import androidx.media3.common.MediaLibraryInfo; -import androidx.media3.common.util.Util; import androidx.media3.test.session.common.HandlerThreadTestRule; import androidx.media3.test.session.common.MainLooperTestRule; import androidx.media3.test.session.common.TestUtils; @@ -117,8 +115,6 @@ public class SessionTokenTest { @Test public void createSessionToken_withPlatformTokenFromMedia1Session_returnsTokenForLegacySession() throws Exception { - assumeTrue(Util.SDK_INT >= 21); - MediaSessionCompat sessionCompat = sessionTestRule.ensureReleaseAfterTest( new MediaSessionCompat(context, "createSessionToken_withLegacyToken")); diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/AssetContentProvider.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/AssetContentProvider.java index eab496509f..21500b7056 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/AssetContentProvider.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/AssetContentProvider.java @@ -29,7 +29,6 @@ import android.system.ErrnoException; import android.system.OsConstants; import androidx.annotation.Nullable; import androidx.media3.common.util.UnstableApi; -import androidx.media3.common.util.Util; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -148,8 +147,7 @@ public final class AssetContentProvider extends ContentProvider } private static boolean isBrokenPipe(IOException e) { - return Util.SDK_INT >= 21 - && e.getCause() instanceof ErrnoException + return e.getCause() instanceof ErrnoException && ((ErrnoException) e.getCause()).errno == OsConstants.EPIPE; } } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingRenderersFactory.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingRenderersFactory.java index f847880d3a..ce753e93d3 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingRenderersFactory.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingRenderersFactory.java @@ -331,7 +331,6 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa dequeuedOutputBuffers.delete(index); } - @RequiresApi(21) @Override public void releaseOutputBuffer(int index, long renderTimeStampNs) { MediaCodec.BufferInfo bufferInfo = checkNotNull(dequeuedOutputBuffers.get(index)); diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DecodeOneFrameUtil.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DecodeOneFrameUtil.java index 27821dce5e..3c918457ba 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DecodeOneFrameUtil.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DecodeOneFrameUtil.java @@ -204,9 +204,6 @@ public final class DecodeOneFrameUtil { */ @Nullable private static String getSupportedDecoderName(MediaFormat format) { - if (Util.SDK_INT < 21) { - throw new UnsupportedOperationException("Unable to detect decoder support under API 21."); - } // TODO(b/266923205): De-duplicate logic from EncoderUtil.java#findCodecForFormat(). MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); // Format must not include KEY_FRAME_RATE on API21. diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/SsimHelper.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/SsimHelper.java index d248f5a8a7..2c398a626c 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/SsimHelper.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/SsimHelper.java @@ -24,7 +24,6 @@ import android.content.Context; import android.media.Image; import android.media.MediaCodec; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.media3.common.util.UnstableApi; import java.io.IOException; import java.nio.ByteBuffer; @@ -43,7 +42,6 @@ import java.nio.ByteBuffer; * channel (Y') because the {@linkplain MediaCodec decoder} decodes to luma. */ @UnstableApi -@RequiresApi(21) public final class SsimHelper { /** The default comparison interval. */ diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoDecodingWrapper.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoDecodingWrapper.java index 832fefa91a..d99f44b053 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoDecodingWrapper.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoDecodingWrapper.java @@ -32,7 +32,6 @@ import android.media.MediaFormat; import android.net.Uri; import android.os.Handler; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.ConditionVariable; @@ -55,7 +54,6 @@ import java.util.List; * ImageReader} for use in CPU test utility functions. */ @UnstableApi -@RequiresApi(21) public final class VideoDecodingWrapper implements AutoCloseable { private static final String TAG = "VideoDecodingWrapper"; diff --git a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/ShadowMediaCodecConfig.java b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/ShadowMediaCodecConfig.java index 5f83aaefec..a252543fda 100644 --- a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/ShadowMediaCodecConfig.java +++ b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/ShadowMediaCodecConfig.java @@ -25,7 +25,6 @@ import android.media.MediaFormat; import androidx.media3.common.C; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.UnstableApi; -import androidx.media3.common.util.Util; import androidx.media3.exoplayer.mediacodec.MediaCodecUtil; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -77,10 +76,6 @@ public final class ShadowMediaCodecConfig extends ExternalResource { @Override protected void before() throws Throwable { - if (Util.SDK_INT == 19) { - // Codec config not supported with Robolectric on API == 19. Skip rule set up step. - return; - } configureCodecs(supportedMimeTypes); } @@ -88,10 +83,6 @@ public final class ShadowMediaCodecConfig extends ExternalResource { protected void after() { supportedMimeTypes.clear(); MediaCodecUtil.clearDecoderInfoCache(); - if (Util.SDK_INT == 19) { - // Codec config not supported with Robolectric on API == 19. Skip rule tear down step. - return; - } ShadowMediaCodecList.reset(); ShadowMediaCodec.clearCodecs(); } diff --git a/libraries/ui/src/main/java/androidx/media3/ui/CaptionStyleCompat.java b/libraries/ui/src/main/java/androidx/media3/ui/CaptionStyleCompat.java index 834e1e4063..ce9b81b458 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/CaptionStyleCompat.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/CaptionStyleCompat.java @@ -23,9 +23,7 @@ import android.view.accessibility.CaptioningManager; import android.view.accessibility.CaptioningManager.CaptionStyle; import androidx.annotation.IntDef; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.media3.common.util.UnstableApi; -import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -116,17 +114,13 @@ public final class CaptionStyleCompat { */ public static CaptionStyleCompat createFromCaptionStyle( CaptioningManager.CaptionStyle captionStyle) { - if (Util.SDK_INT >= 21) { - return createFromCaptionStyleV21(captionStyle); - } else { - return new CaptionStyleCompat( - captionStyle.foregroundColor, - captionStyle.backgroundColor, - Color.TRANSPARENT, - captionStyle.edgeType, - captionStyle.edgeColor, - captionStyle.getTypeface()); - } + return new CaptionStyleCompat( + captionStyle.hasForegroundColor() ? captionStyle.foregroundColor : DEFAULT.foregroundColor, + captionStyle.hasBackgroundColor() ? captionStyle.backgroundColor : DEFAULT.backgroundColor, + captionStyle.hasWindowColor() ? captionStyle.windowColor : DEFAULT.windowColor, + captionStyle.hasEdgeType() ? captionStyle.edgeType : DEFAULT.edgeType, + captionStyle.hasEdgeColor() ? captionStyle.edgeColor : DEFAULT.edgeColor, + captionStyle.getTypeface()); } /** @@ -151,17 +145,4 @@ public final class CaptionStyleCompat { this.edgeColor = edgeColor; this.typeface = typeface; } - - @RequiresApi(21) - @SuppressWarnings("ResourceType") - private static CaptionStyleCompat createFromCaptionStyleV21( - CaptioningManager.CaptionStyle captionStyle) { - return new CaptionStyleCompat( - captionStyle.hasForegroundColor() ? captionStyle.foregroundColor : DEFAULT.foregroundColor, - captionStyle.hasBackgroundColor() ? captionStyle.backgroundColor : DEFAULT.backgroundColor, - captionStyle.hasWindowColor() ? captionStyle.windowColor : DEFAULT.windowColor, - captionStyle.hasEdgeType() ? captionStyle.edgeType : DEFAULT.edgeType, - captionStyle.hasEdgeColor() ? captionStyle.edgeColor : DEFAULT.edgeColor, - captionStyle.getTypeface()); - } } diff --git a/libraries/ui/src/main/java/androidx/media3/ui/DefaultTimeBar.java b/libraries/ui/src/main/java/androidx/media3/ui/DefaultTimeBar.java index f8350f8b45..33cc65b7cf 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/DefaultTimeBar.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/DefaultTimeBar.java @@ -759,13 +759,8 @@ public class DefaultTimeBar extends View implements TimeBar { if (duration <= 0) { return; } - if (Util.SDK_INT >= 21) { - info.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD); - info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD); - } else { - info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); - info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); - } + info.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD); + info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD); } @Override diff --git a/libraries/ui/src/main/java/androidx/media3/ui/DefaultTrackNameProvider.java b/libraries/ui/src/main/java/androidx/media3/ui/DefaultTrackNameProvider.java index d83fc54195..e05e39bdbc 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/DefaultTrackNameProvider.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/DefaultTrackNameProvider.java @@ -115,8 +115,7 @@ public class DefaultTrackNameProvider implements TrackNameProvider { if (TextUtils.isEmpty(language) || C.LANGUAGE_UNDETERMINED.equals(language)) { return ""; } - Locale languageLocale = - Util.SDK_INT >= 21 ? Locale.forLanguageTag(language) : new Locale(language); + Locale languageLocale = Locale.forLanguageTag(language); Locale displayLocale = Util.getDefaultDisplayLocale(); String languageName = languageLocale.getDisplayName(displayLocale); if (TextUtils.isEmpty(languageName)) { diff --git a/libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerControlView.java b/libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerControlView.java index adecd7df5c..f8088dd036 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerControlView.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerControlView.java @@ -47,9 +47,7 @@ import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; -import androidx.annotation.DoNotInline; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.media3.common.C; import androidx.media3.common.MediaLibraryInfo; import androidx.media3.common.Player; @@ -857,19 +855,15 @@ public class LegacyPlayerControlView extends FrameLayout { boolean requestPlayPauseAccessibilityFocus = false; boolean shouldShowPlayButton = Util.shouldShowPlayButton(player, showPlayButtonIfSuppressed); if (playButton != null) { - requestPlayPauseFocus |= !shouldShowPlayButton && playButton.isFocused(); - requestPlayPauseAccessibilityFocus |= - Util.SDK_INT < 21 - ? requestPlayPauseFocus - : (!shouldShowPlayButton && Api21.isAccessibilityFocused(playButton)); + requestPlayPauseFocus = !shouldShowPlayButton && playButton.isFocused(); + requestPlayPauseAccessibilityFocus = + (!shouldShowPlayButton && playButton.isAccessibilityFocused()); playButton.setVisibility(shouldShowPlayButton ? VISIBLE : GONE); } if (pauseButton != null) { requestPlayPauseFocus |= shouldShowPlayButton && pauseButton.isFocused(); requestPlayPauseAccessibilityFocus |= - Util.SDK_INT < 21 - ? requestPlayPauseFocus - : (shouldShowPlayButton && Api21.isAccessibilityFocused(pauseButton)); + shouldShowPlayButton && pauseButton.isAccessibilityFocused(); pauseButton.setVisibility(shouldShowPlayButton ? GONE : VISIBLE); } if (requestPlayPauseFocus) { @@ -1357,12 +1351,4 @@ public class LegacyPlayerControlView extends FrameLayout { } } } - - @RequiresApi(21) - private static final class Api21 { - @DoNotInline - public static boolean isAccessibilityFocused(View view) { - return view.isAccessibilityFocused(); - } - } } diff --git a/libraries/ui/src/main/java/androidx/media3/ui/PlayerNotificationManager.java b/libraries/ui/src/main/java/androidx/media3/ui/PlayerNotificationManager.java index fd65683c4c..778e54177b 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/PlayerNotificationManager.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/PlayerNotificationManager.java @@ -55,7 +55,6 @@ import androidx.annotation.DrawableRes; import androidx.annotation.IntDef; import androidx.annotation.IntRange; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.core.app.NotificationBuilderWithBuilderAccessor; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; @@ -1013,11 +1012,10 @@ public class PlayerNotificationManager { * @deprecated Use {@link #setMediaSessionToken(MediaSession.Token)} and pass in {@code * (MediaSession.Token) compatToken.getToken()}. */ + // TODO: b/333355694 - Remove the dependency on androidx.media when this method is removed. @Deprecated public final void setMediaSessionToken(MediaSessionCompat.Token compatToken) { - if (Util.SDK_INT >= 21) { - setMediaSessionToken((MediaSession.Token) compatToken.getToken()); - } + setMediaSessionToken((MediaSession.Token) compatToken.getToken()); } /** @@ -1028,7 +1026,6 @@ public class PlayerNotificationManager { * * @param token The {@link MediaSession.Token}. */ - @RequiresApi(21) public final void setMediaSessionToken(MediaSession.Token token) { if (!Util.areEqual(this.mediaSessionToken, token)) { mediaSessionToken = token; @@ -1247,8 +1244,7 @@ public class PlayerNotificationManager { * Creates the notification given the current player state. * * @param player The player for which state to build a notification. - * @param builder The builder used to build the last notification, or {@code null}. Re-using the - * builder when possible can prevent notification flicker when {@code Util#SDK_INT} < 21. + * @param builder The builder used to build the last notification, or {@code null}. * @param ongoing Whether the notification should be ongoing. * @param largeIcon The large icon to be used. * @return The {@link NotificationCompat.Builder} on which to call {@link @@ -1291,17 +1287,7 @@ public class PlayerNotificationManager { } int[] actionIndicesForCompactView = getActionIndicesForCompactView(actionNames, player); - if (Util.SDK_INT >= 21) { - builder.setStyle(new MediaStyle(mediaSessionToken, actionIndicesForCompactView)); - } else { - // TODO: b/333355694 - Remove dependency on androidx.media once this logic is gone. - androidx.media.app.NotificationCompat.MediaStyle mediaStyle = - new androidx.media.app.NotificationCompat.MediaStyle(); - mediaStyle.setShowActionsInCompactView(actionIndicesForCompactView); - mediaStyle.setShowCancelButton(!ongoing); - mediaStyle.setCancelButtonIntent(dismissPendingIntent); - builder.setStyle(mediaStyle); - } + builder.setStyle(new MediaStyle(mediaSessionToken, actionIndicesForCompactView)); // Set intent which is sent if the user selects 'clear all' builder.setDeleteIntent(dismissPendingIntent); @@ -1317,9 +1303,7 @@ public class PlayerNotificationManager { .setPriority(priority) .setDefaults(defaults); - // Changing "showWhen" causes notification flicker if SDK_INT < 21. - if (Util.SDK_INT >= 21 - && useChronometer + if (useChronometer && player.isCommandAvailable(COMMAND_GET_CURRENT_MEDIA_ITEM) && player.isPlaying() && !player.isPlayingAd() @@ -1628,7 +1612,6 @@ public class PlayerNotificationManager { } } - @RequiresApi(21) private static final class MediaStyle extends androidx.core.app.NotificationCompat.Style { private final int[] actionsToShowInCompact;