diff --git a/library/core/build.gradle b/library/core/build.gradle
index 81e1a3d543..bebd62beb9 100644
--- a/library/core/build.gradle
+++ b/library/core/build.gradle
@@ -48,6 +48,7 @@ dependencies {
androidTestImplementation(project(modulePrefix + 'testutils')) {
exclude module: modulePrefix.substring(1) + 'library-core'
}
+ androidTestImplementation 'com.squareup.okhttp3:mockwebserver:' + okhttpVersion
testImplementation 'com.squareup.okhttp3:mockwebserver:' + okhttpVersion
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
testImplementation project(modulePrefix + 'testutils')
diff --git a/library/core/src/androidTest/AndroidManifest.xml b/library/core/src/androidTest/AndroidManifest.xml
index 4ffc92ac24..db8d89e66e 100644
--- a/library/core/src/androidTest/AndroidManifest.xml
+++ b/library/core/src/androidTest/AndroidManifest.xml
@@ -19,11 +19,13 @@
package="com.google.android.exoplayer2.core.test">
+
+ tools:ignore="MissingApplicationIcon,HardcodedDebugMode"
+ android:usesCleartextTraffic="true">
diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/drm/DrmPlaybackTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/drm/DrmPlaybackTest.java
new file mode 100644
index 0000000000..c342e9a1a7
--- /dev/null
+++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/drm/DrmPlaybackTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.exoplayer2.drm;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.ExoPlayer;
+import com.google.android.exoplayer2.MediaItem;
+import com.google.android.exoplayer2.PlaybackException;
+import com.google.android.exoplayer2.Player;
+import com.google.android.exoplayer2.SimpleExoPlayer;
+import com.google.android.exoplayer2.util.ConditionVariable;
+import java.util.concurrent.atomic.AtomicReference;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Instrumentation tests for DRM playback. */
+@RunWith(AndroidJUnit4.class)
+public final class DrmPlaybackTest {
+
+ /** The license response needed to play the {@code drm/sample_fragmented_clearkey.mp4} file. */
+ // NOTE: Changing this response *should* make the test fail, but it seems the clearkey CDM
+ // implementation is quite robust. This means an 'invalid' response means it just incorrectly
+ // decrypts the content, resulting in invalid data fed to the decoder and no video shown on the
+ // screen (but no error thrown that's detectable by this test).
+ private static final String CLEARKEY_RESPONSE =
+ "{\"keys\":"
+ + "[{"
+ + "\"kty\":\"oct\","
+ + "\"k\":\"Y8tfcYTdS2iaXF_xHuajKA\","
+ + "\"kid\":\"zX65_4jzTK6wYYWwACTkwg\""
+ + "}],"
+ + "\"type\":\"temporary\"}";
+
+ @Test
+ public void clearkeyPlayback() throws Exception {
+ MockWebServer mockWebServer = new MockWebServer();
+ mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(CLEARKEY_RESPONSE));
+ mockWebServer.start();
+
+ MediaItem mediaItem =
+ new MediaItem.Builder()
+ .setUri("asset:///media/drm/sample_fragmented_clearkey.mp4")
+ .setDrmConfiguration(
+ new MediaItem.DrmConfiguration.Builder(C.CLEARKEY_UUID)
+ .setLicenseUri(mockWebServer.url("license").toString())
+ .build())
+ .build();
+ AtomicReference player = new AtomicReference<>();
+ ConditionVariable playbackComplete = new ConditionVariable();
+ AtomicReference playbackException = new AtomicReference<>();
+ getInstrumentation()
+ .runOnMainSync(
+ () -> {
+ player.set(new ExoPlayer.Builder(getInstrumentation().getContext()).build());
+ player
+ .get()
+ .addListener(
+ new Player.Listener() {
+ @Override
+ public void onPlaybackStateChanged(@Player.State int playbackState) {
+ if (playbackState == Player.STATE_ENDED) {
+ playbackComplete.open();
+ }
+ }
+
+ @Override
+ public void onPlayerError(PlaybackException error) {
+ playbackException.set(error);
+ playbackComplete.open();
+ }
+ });
+ player.get().setMediaItem(mediaItem);
+ player.get().prepare();
+ player.get().play();
+ });
+
+ playbackComplete.block();
+ getInstrumentation().runOnMainSync(() -> player.get().release());
+ getInstrumentation().waitForIdleSync();
+ assertThat(playbackException.get()).isNull();
+ }
+}
diff --git a/testdata/src/test/assets/media/drm/sample_fragmented_clearkey.mp4 b/testdata/src/test/assets/media/drm/sample_fragmented_clearkey.mp4
new file mode 100644
index 0000000000..1efacc5d22
Binary files /dev/null and b/testdata/src/test/assets/media/drm/sample_fragmented_clearkey.mp4 differ