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;