Remove DownloadAction.createRemoveAction

PiperOrigin-RevId: 240557315
This commit is contained in:
eguven 2019-03-27 14:53:14 +00:00 committed by Toni
parent d4f5c9c721
commit facd32e65e
14 changed files with 215 additions and 354 deletions

View File

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.offline;
import com.google.android.exoplayer2.offline.DownloadAction.UnsupportedActionException;
import com.google.android.exoplayer2.util.AtomicFile;
import com.google.android.exoplayer2.util.Util;
import java.io.DataInputStream;
@ -22,12 +23,14 @@ import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
/**
* Stores and loads {@link DownloadAction}s to/from a file.
*/
public final class ActionFile {
private static final String TAG = "ActionFile";
/* package */ static final int VERSION = 0;
private final AtomicFile atomicFile;
@ -58,11 +61,15 @@ public final class ActionFile {
throw new IOException("Unsupported action file version: " + version);
}
int actionCount = dataInputStream.readInt();
DownloadAction[] actions = new DownloadAction[actionCount];
ArrayList<DownloadAction> actions = new ArrayList<>();
for (int i = 0; i < actionCount; i++) {
actions[i] = DownloadAction.deserializeFromStream(dataInputStream);
try {
actions.add(DownloadAction.deserializeFromStream(dataInputStream));
} catch (UnsupportedActionException e) {
// remove DownloadAction is not supported. Ignore the exception and continue loading rest.
}
return actions;
}
return actions.toArray(new DownloadAction[0]);
} finally {
Util.closeQuietly(inputStream);
}

View File

@ -17,7 +17,6 @@ package com.google.android.exoplayer2.offline;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@ -31,9 +30,12 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/** Contains the necessary parameters for a download or remove action. */
/** Contains the necessary parameters for a download action. */
public final class DownloadAction {
/** Thrown when the encoded action data belongs to an unsupported DownloadAction type. */
public static class UnsupportedActionException extends IOException {}
/** Type for progressive downloads. */
public static final String TYPE_PROGRESSIVE = "progressive";
/** Type for DASH downloads. */
@ -51,6 +53,8 @@ public final class DownloadAction {
* @param data The action data to deserialize.
* @return The deserialized action.
* @throws IOException If the data could not be deserialized.
* @throws UnsupportedActionException If the data belongs to an unsupported {@link DownloadAction}
* type. Input read position is set to the end of the data.
*/
public static DownloadAction fromByteArray(byte[] data) throws IOException {
ByteArrayInputStream input = new ByteArrayInputStream(data);
@ -67,6 +71,8 @@ public final class DownloadAction {
* @return The deserialized action.
* @throws IOException If there is an IO error reading from {@code input}, or if the data could
* not be deserialized.
* @throws UnsupportedActionException If the data belongs to an unsupported {@link DownloadAction}
* type. Input read position is set to the end of the data.
*/
public static DownloadAction deserializeFromStream(InputStream input) throws IOException {
return readFromStream(new DataInputStream(input));
@ -108,41 +114,16 @@ public final class DownloadAction {
List<StreamKey> keys,
@Nullable String customCacheKey,
@Nullable byte[] data) {
return new DownloadAction(
id, type, uri, /* isRemoveAction= */ false, keys, customCacheKey, data);
}
/**
* Creates a DASH remove action.
*
* @param type The type of the action.
* @param uri The URI of the media to be removed.
* @param customCacheKey A custom key for cache indexing, or null.
*/
public static DownloadAction createRemoveAction(
String type, Uri uri, @Nullable String customCacheKey) {
return new DownloadAction(
generateId(uri, customCacheKey),
type,
uri,
/* isRemoveAction= */ true,
Collections.emptyList(),
customCacheKey,
/* data= */ null);
return new DownloadAction(id, type, uri, keys, customCacheKey, data);
}
/** The unique content id. */
public final String id;
/** The type of the action. */
public final String type;
/** The uri being downloaded or removed. */
/** The uri being downloaded. */
public final Uri uri;
/** Whether this is a remove action. If false, this is a download action. */
public final boolean isRemoveAction;
/**
* Keys of streams to be downloaded. If empty, all streams will be downloaded. Empty if this
* action is a remove action.
*/
/** Keys of streams to be downloaded. If empty, all streams will be downloaded. */
public final List<StreamKey> keys;
/** A custom key for cache indexing, or null. */
@Nullable public final String customCacheKey;
@ -152,38 +133,27 @@ public final class DownloadAction {
/**
* @param id The content id.
* @param type The type of the action.
* @param uri The uri being downloaded or removed.
* @param isRemoveAction Whether this is a remove action. If false, this is a download action.
* @param keys Keys of streams to be downloaded. If empty, all streams will be downloaded. Empty
* if this action is a remove action.
* @param uri The uri being downloaded.
* @param keys Keys of streams to be downloaded. If empty, all streams will be downloaded.
* @param customCacheKey A custom key for cache indexing, or null.
* @param data Custom data for this action. Null if this action is a remove action.
* @param data Custom data for this action.
*/
private DownloadAction(
String id,
String type,
Uri uri,
boolean isRemoveAction,
List<StreamKey> keys,
@Nullable String customCacheKey,
@Nullable byte[] data) {
this.id = id;
this.type = type;
this.uri = uri;
this.isRemoveAction = isRemoveAction;
this.customCacheKey = customCacheKey;
if (isRemoveAction) {
Assertions.checkArgument(keys.isEmpty());
Assertions.checkArgument(data == null);
this.keys = Collections.emptyList();
this.data = Util.EMPTY_BYTE_ARRAY;
} else {
ArrayList<StreamKey> mutableKeys = new ArrayList<>(keys);
Collections.sort(mutableKeys);
this.keys = Collections.unmodifiableList(mutableKeys);
this.data = data != null ? Arrays.copyOf(data, data.length) : Util.EMPTY_BYTE_ARRAY;
}
}
/** Serializes itself into a byte array. */
public byte[] toByteArray() {
@ -211,7 +181,6 @@ public final class DownloadAction {
return id.equals(that.id)
&& type.equals(that.type)
&& uri.equals(that.uri)
&& isRemoveAction == that.isRemoveAction
&& keys.equals(that.keys)
&& Util.areEqual(customCacheKey, that.customCacheKey)
&& Arrays.equals(data, that.data);
@ -222,7 +191,6 @@ public final class DownloadAction {
int result = type.hashCode();
result = 31 * result + id.hashCode();
result = 31 * result + uri.hashCode();
result = 31 * result + (isRemoveAction ? 1 : 0);
result = 31 * result + keys.hashCode();
result = 31 * result + (customCacheKey != null ? customCacheKey.hashCode() : 0);
result = 31 * result + Arrays.hashCode(data);
@ -242,7 +210,7 @@ public final class DownloadAction {
dataOutputStream.writeUTF(type);
dataOutputStream.writeInt(VERSION);
dataOutputStream.writeUTF(uri.toString());
dataOutputStream.writeBoolean(isRemoveAction);
dataOutputStream.writeBoolean(false);
dataOutputStream.writeInt(data.length);
dataOutputStream.write(data);
dataOutputStream.writeInt(keys.size());
@ -271,10 +239,6 @@ public final class DownloadAction {
if (dataLength != 0) {
data = new byte[dataLength];
input.readFully(data);
if (isRemoveAction) {
// Remove actions are no longer permitted to have data.
data = null;
}
} else {
data = null;
}
@ -297,8 +261,12 @@ public final class DownloadAction {
customCacheKey = input.readBoolean() ? input.readUTF() : null;
}
if (isRemoveAction) {
// Remove actions are not supported anymore.
throw new UnsupportedActionException();
}
return new DownloadAction(
generateId(uri, customCacheKey), type, uri, isRemoveAction, keys, customCacheKey, data);
generateId(uri, customCacheKey), type, uri, keys, customCacheKey, data);
}
private static String generateId(Uri uri, @Nullable String customCacheKey) {

View File

@ -342,11 +342,11 @@ public final class DownloadManager {
}
/**
* Handles the given action.
* Adds a download defined by the given action.
*
* @param action The action to be executed.
* @param action The download action.
*/
public void handleAction(DownloadAction action) {
public void addDownload(DownloadAction action) {
Assertions.checkState(!released);
dowloadUpdateQueue.add(
new DownloadUpdater(action.id) {
@ -392,6 +392,8 @@ public final class DownloadManager {
DownloadState onLoad(@Nullable DownloadState downloadState) {
if (downloadState != null) {
downloadState = downloadState.setRemoveState();
} else {
logd("Can't remove download. No download with id: " + id);
}
return downloadState;
}
@ -541,9 +543,7 @@ public final class DownloadManager {
}
private void processDownloadUpdateQueue() {
if (loadingDownload || dowloadUpdateQueue.isEmpty()) {
return;
}
while (!loadingDownload && !dowloadUpdateQueue.isEmpty()) {
DownloadUpdater downloadUpdater = dowloadUpdateQueue.remove();
Download download = getDownload(downloadUpdater.id);
if (download != null) {
@ -552,6 +552,7 @@ public final class DownloadManager {
loadDownload(downloadUpdater);
}
}
}
private void loadDownload(DownloadUpdater callback) {
loadingDownload = true;

View File

@ -270,11 +270,14 @@ public abstract class DownloadService extends Service {
* @param context A {@link Context}.
* @param clazz The concrete download service being targeted by the intent.
* @param id The content id.
* @param foreground Whether this intent will be used to start the service in the foreground.
* @return Created Intent.
*/
public static Intent buildRemoveDownloadIntent(
Context context, Class<? extends DownloadService> clazz, String id) {
return getIntent(context, clazz, ACTION_REMOVE).putExtra(KEY_CONTENT_ID, id);
Context context, Class<? extends DownloadService> clazz, String id, boolean foreground) {
return getIntent(context, clazz, ACTION_REMOVE)
.putExtra(KEY_CONTENT_ID, id)
.putExtra(KEY_FOREGROUND, foreground);
}
/**
@ -308,7 +311,7 @@ public abstract class DownloadService extends Service {
*/
public static void startWithRemoveDownload(
Context context, Class<? extends DownloadService> clazz, String id, boolean foreground) {
Intent intent = buildRemoveDownloadIntent(context, clazz, id);
Intent intent = buildRemoveDownloadIntent(context, clazz, id, foreground);
if (foreground) {
Util.startForegroundService(context, intent);
} else {
@ -393,7 +396,7 @@ public abstract class DownloadService extends Service {
Log.e(TAG, "Ignored ADD action: Failed to deserialize download_action extra", e);
}
if (downloadAction != null) {
downloadManager.handleAction(downloadAction);
downloadManager.addDownload(downloadAction);
}
}
break;

View File

@ -166,7 +166,7 @@ public final class DownloadState {
action.type,
action.uri,
action.customCacheKey,
/* state= */ action.isRemoveAction ? STATE_REMOVING : STATE_QUEUED,
/* state= */ STATE_QUEUED,
/* downloadPercentage= */ C.PERCENTAGE_UNSET,
/* downloadedBytes= */ 0,
/* totalBytes= */ C.LENGTH_UNSET,
@ -231,8 +231,7 @@ public final class DownloadState {
type,
action.uri,
action.customCacheKey,
getNextState(
state, manualStopReason != 0 || notMetRequirements != 0, action.isRemoveAction),
getNextState(state, manualStopReason != 0 || notMetRequirements != 0),
/* downloadPercentage= */ C.PERCENTAGE_UNSET,
downloadedBytes,
/* totalBytes= */ C.LENGTH_UNSET,
@ -252,7 +251,7 @@ public final class DownloadState {
type,
uri,
cacheKey,
getNextState(state, manualStopReason != 0 || notMetRequirements != 0, true),
STATE_REMOVING,
/* downloadPercentage= */ C.PERCENTAGE_UNSET,
downloadedBytes,
/* totalBytes= */ C.LENGTH_UNSET,
@ -265,25 +264,19 @@ public final class DownloadState {
customMetadata);
}
private static int getNextState(int currentState, boolean isStopped, boolean remove) {
int nextState;
if (remove) {
nextState = STATE_REMOVING;
} else {
private static int getNextState(int currentState, boolean isStopped) {
if (currentState == STATE_REMOVING || currentState == STATE_RESTARTING) {
nextState = STATE_RESTARTING;
return STATE_RESTARTING;
} else if (isStopped) {
nextState = STATE_STOPPED;
return STATE_STOPPED;
} else {
nextState = STATE_QUEUED;
return STATE_QUEUED;
}
}
return nextState;
}
private static StreamKey[] mergeStreamKeys(DownloadState downloadState, DownloadAction action) {
StreamKey[] streamKeys = downloadState.streamKeys;
if (!action.isRemoveAction && streamKeys.length > 0) {
if (streamKeys.length > 0) {
if (action.keys.isEmpty()) {
streamKeys = new StreamKey[0];
} else {

View File

@ -20,10 +20,13 @@ import static com.google.android.exoplayer2.offline.DownloadAction.TYPE_HLS;
import static com.google.android.exoplayer2.offline.DownloadAction.TYPE_PROGRESSIVE;
import static com.google.android.exoplayer2.offline.DownloadAction.TYPE_SS;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import android.net.Uri;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.offline.DownloadAction.UnsupportedActionException;
import com.google.android.exoplayer2.testutil.TestUtil;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@ -53,92 +56,73 @@ public class DownloadActionTest {
data = TestUtil.buildTestData(32);
}
@Test
public void testDownloadActionIsNotRemoveAction() {
DownloadAction action = createDownloadAction(uri1);
assertThat(action.isRemoveAction).isFalse();
}
@Test
public void testRemoveActionIsRemoveAction() {
DownloadAction action2 = createRemoveAction(uri1);
assertThat(action2.isRemoveAction).isTrue();
}
@Test
public void testSameUri_hasSameId() {
DownloadAction action1 = createDownloadAction(uri1);
DownloadAction action2 = createDownloadAction(uri1);
DownloadAction action1 = createAction(uri1);
DownloadAction action2 = createAction(uri1);
assertThat(action1.id.equals(action2.id)).isTrue();
}
@Test
public void testSameUriDifferentAction_hasSameId() {
DownloadAction action1 = createDownloadAction(uri1);
DownloadAction action2 = createRemoveAction(uri1);
DownloadAction action1 = createAction(uri1);
DownloadAction action2 = createAction(uri1);
assertThat(action1.id.equals(action2.id)).isTrue();
}
@Test
public void testDifferentUri_IsNotSameMedia() {
DownloadAction action1 = createDownloadAction(uri1);
DownloadAction action2 = createDownloadAction(uri2);
DownloadAction action1 = createAction(uri1);
DownloadAction action2 = createAction(uri2);
assertThat(action1.id.equals(action2.id)).isFalse();
}
@Test
public void testSameCacheKeyDifferentUri_hasSameId() {
DownloadAction action1 = DownloadAction.createRemoveAction(TYPE_DASH, uri1, "key123");
DownloadAction action2 = DownloadAction.createRemoveAction(TYPE_DASH, uri2, "key123");
DownloadAction action1 = createAction(uri1, "key123");
DownloadAction action2 = createAction(uri2, "key123");
assertThat(action1.id.equals(action2.id)).isTrue();
}
@Test
public void testDifferentCacheKeyDifferentUri_hasDifferentId() {
DownloadAction action1 = DownloadAction.createRemoveAction(TYPE_DASH, uri1, "key123");
DownloadAction action2 = DownloadAction.createRemoveAction(TYPE_DASH, uri2, "key456");
DownloadAction action1 = createAction(uri1, "key123");
DownloadAction action2 = createAction(uri2, "key456");
assertThat(action1.id.equals(action2.id)).isFalse();
}
@SuppressWarnings("EqualsWithItself")
@Test
public void testEquals() {
DownloadAction action1 = createRemoveAction(uri1);
DownloadAction action1 = createAction(uri1);
assertThat(action1.equals(action1)).isTrue();
DownloadAction action2 = createRemoveAction(uri1);
DownloadAction action3 = createRemoveAction(uri1);
DownloadAction action2 = createAction(uri1);
DownloadAction action3 = createAction(uri1);
assertEqual(action2, action3);
DownloadAction action4 = createRemoveAction(uri1);
DownloadAction action5 = createDownloadAction(uri1);
assertNotEqual(action4, action5);
DownloadAction action6 = createDownloadAction(uri1);
DownloadAction action7 = createDownloadAction(uri1, new StreamKey(0, 0, 0));
DownloadAction action6 = createAction(uri1);
DownloadAction action7 = createAction(uri1, new StreamKey(0, 0, 0));
assertNotEqual(action6, action7);
DownloadAction action8 = createDownloadAction(uri1, new StreamKey(0, 1, 1));
DownloadAction action9 = createDownloadAction(uri1, new StreamKey(0, 0, 0));
DownloadAction action8 = createAction(uri1, new StreamKey(0, 1, 1));
DownloadAction action9 = createAction(uri1, new StreamKey(0, 0, 0));
assertNotEqual(action8, action9);
DownloadAction action10 = createRemoveAction(uri1);
DownloadAction action11 = createRemoveAction(uri2);
DownloadAction action10 = createAction(uri1);
DownloadAction action11 = createAction(uri2);
assertNotEqual(action10, action11);
DownloadAction action12 =
createDownloadAction(uri1, new StreamKey(0, 0, 0), new StreamKey(0, 1, 1));
DownloadAction action13 =
createDownloadAction(uri1, new StreamKey(0, 1, 1), new StreamKey(0, 0, 0));
DownloadAction action12 = createAction(uri1, new StreamKey(0, 0, 0), new StreamKey(0, 1, 1));
DownloadAction action13 = createAction(uri1, new StreamKey(0, 1, 1), new StreamKey(0, 0, 0));
assertEqual(action12, action13);
DownloadAction action14 = createDownloadAction(uri1, new StreamKey(0, 0, 0));
DownloadAction action15 =
createDownloadAction(uri1, new StreamKey(0, 1, 1), new StreamKey(0, 0, 0));
DownloadAction action14 = createAction(uri1, new StreamKey(0, 0, 0));
DownloadAction action15 = createAction(uri1, new StreamKey(0, 1, 1), new StreamKey(0, 0, 0));
assertNotEqual(action14, action15);
DownloadAction action16 = createDownloadAction(uri1);
DownloadAction action17 = createDownloadAction(uri1);
DownloadAction action16 = createAction(uri1);
DownloadAction action17 = createAction(uri1);
assertEqual(action16, action17);
}
@ -151,8 +135,7 @@ public class DownloadActionTest {
toList(new StreamKey(0, 1, 2), new StreamKey(3, 4, 5)),
"key123",
data));
assertStreamSerializationRoundTrip(
DownloadAction.createRemoveAction(TYPE_DASH, uri1, "key123"));
assertStreamSerializationRoundTrip(createAction(uri1, "key123"));
}
@Test
@ -164,7 +147,7 @@ public class DownloadActionTest {
toList(new StreamKey(0, 1, 2), new StreamKey(3, 4, 5)),
"key123",
data));
assertArraySerializationRoundTrip(DownloadAction.createRemoveAction(TYPE_DASH, uri1, "key123"));
assertArraySerializationRoundTrip(createAction(uri1, "key123"));
}
@Test
@ -173,9 +156,7 @@ public class DownloadActionTest {
"progressive-download-v0",
DownloadAction.createDownloadAction(
TYPE_PROGRESSIVE, uri1, Collections.emptyList(), "key123", data));
assertDeserialization(
"progressive-remove-v0",
DownloadAction.createRemoveAction(TYPE_PROGRESSIVE, uri1, "key123"));
assertUnsupportedAction("progressive-remove-v0");
}
@Test
@ -188,9 +169,7 @@ public class DownloadActionTest {
toList(new StreamKey(0, 1, 2), new StreamKey(3, 4, 5)),
/* customCacheKey= */ null,
data));
assertDeserialization(
"dash-remove-v0",
DownloadAction.createRemoveAction(TYPE_DASH, uri1, /* customCacheKey= */ null));
assertUnsupportedAction("dash-remove-v0");
}
@Test
@ -203,9 +182,7 @@ public class DownloadActionTest {
toList(new StreamKey(0, 1), new StreamKey(2, 3)),
/* customCacheKey= */ null,
data));
assertDeserialization(
"hls-remove-v0",
DownloadAction.createRemoveAction(TYPE_HLS, uri1, /* customCacheKey= */ null));
assertUnsupportedAction("hls-remove-v0");
}
@Test
@ -218,9 +195,7 @@ public class DownloadActionTest {
toList(new StreamKey(0, 1, 2), new StreamKey(3, 4, 5)),
/* customCacheKey= */ null,
data));
assertDeserialization(
"hls-remove-v1",
DownloadAction.createRemoveAction(TYPE_HLS, uri1, /* customCacheKey= */ null));
assertUnsupportedAction("hls-remove-v1");
}
@Test
@ -233,9 +208,7 @@ public class DownloadActionTest {
toList(new StreamKey(0, 1), new StreamKey(2, 3)),
/* customCacheKey= */ null,
data));
assertDeserialization(
"ss-remove-v0",
DownloadAction.createRemoveAction(TYPE_SS, uri1, /* customCacheKey= */ null));
assertUnsupportedAction("ss-remove-v0");
}
@Test
@ -248,18 +221,17 @@ public class DownloadActionTest {
toList(new StreamKey(0, 1, 2), new StreamKey(3, 4, 5)),
/* customCacheKey= */ null,
data));
assertDeserialization(
"ss-remove-v1",
DownloadAction.createRemoveAction(TYPE_SS, uri1, /* customCacheKey= */ null));
assertUnsupportedAction("ss-remove-v1");
}
private DownloadAction createDownloadAction(Uri uri, StreamKey... keys) {
private DownloadAction createAction(Uri uri, StreamKey... keys) {
return DownloadAction.createDownloadAction(
TYPE_DASH, uri, toList(keys), /* customCacheKey= */ null, data);
}
private DownloadAction createRemoveAction(Uri uri) {
return DownloadAction.createRemoveAction(TYPE_DASH, uri, /* customCacheKey= */ null);
private DownloadAction createAction(Uri uri, @Nullable String customCacheKey) {
return DownloadAction.createDownloadAction(
DownloadAction.TYPE_DASH, uri, Collections.emptyList(), customCacheKey, /* data= */ null);
}
private static void assertNotEqual(DownloadAction action1, DownloadAction action2) {
@ -298,6 +270,18 @@ public class DownloadActionTest {
assertEqual(deserializedAction, expectedAction);
}
private static void assertUnsupportedAction(String fileName) throws IOException {
InputStream input =
TestUtil.getInputStream(
ApplicationProvider.getApplicationContext(), "download-actions/" + fileName);
try {
DownloadAction.deserializeFromStream(input);
fail();
} catch (UnsupportedActionException e) {
// Expected exception.
}
}
private static List<StreamKey> toList(StreamKey... keys) {
ArrayList<StreamKey> keysList = new ArrayList<>();
Collections.addAll(keysList, keys);

View File

@ -397,7 +397,6 @@ public class DownloadHelperTest {
assertThat(downloadAction.type).isEqualTo(TEST_DOWNLOAD_TYPE);
assertThat(downloadAction.uri).isEqualTo(testUri);
assertThat(downloadAction.customCacheKey).isEqualTo(TEST_CACHE_KEY);
assertThat(downloadAction.isRemoveAction).isFalse();
assertThat(downloadAction.data).isEqualTo(data);
assertThat(downloadAction.keys)
.containsExactly(

View File

@ -127,16 +127,11 @@ public class DownloadIndexUtilTest {
/* customCacheKey= */ "key234",
new byte[] {5, 4, 3, 2, 1});
actionFile.store(action1, action2);
DownloadAction action3 =
DownloadAction.createRemoveAction(
TYPE_DASH, Uri.parse("https://www.test.com/download3"), /* customCacheKey= */ "key345");
actionFile.store(action1, action2, action3);
DownloadIndexUtil.upgradeActionFile(actionFile, downloadIndex, /* downloadIdProvider= */ null);
assertDownloadIndexContainsAction(action1, DownloadState.STATE_QUEUED);
assertDownloadIndexContainsAction(action2, DownloadState.STATE_QUEUED);
assertDownloadIndexContainsAction(action3, DownloadState.STATE_REMOVING);
}
private void assertDownloadIndexContainsAction(DownloadAction action, int state)

View File

@ -16,7 +16,6 @@
package com.google.android.exoplayer2.offline;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import android.net.Uri;
import androidx.test.core.app.ApplicationProvider;
@ -33,7 +32,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.After;
@ -101,21 +99,6 @@ public class DownloadManagerTest {
assertThat(exceptionThrown).isTrue();
}
@Test
public void downloadRunner_handleActionReturnsDifferentTaskId_throwsException() {
DownloadRunner runner = new DownloadRunner(uri1).postDownloadAction();
TaskWrapper task = runner.getTask();
runner.setTask(new TaskWrapper(task.taskId + 10000));
boolean exceptionThrown = false;
try {
runner.postDownloadAction();
// can't put fail() here as it would be caught in the catch below.
} catch (Throwable e) {
exceptionThrown = true;
}
assertThat(exceptionThrown).isTrue();
}
@Test
public void multipleActionsForTheSameContent_executedOnTheSameTask() {
// Two download actions on first task
@ -150,11 +133,11 @@ public class DownloadManagerTest {
@Test
public void postRemoveAction_removes() throws Throwable {
DownloadRunner runner = new DownloadRunner(uri1);
TaskWrapper task = runner.postRemoveAction().getTask();
TaskWrapper task = runner.postDownloadAction().postRemoveAction().getTask();
task.assertRemoving();
runner.getDownloader(0).unblock().assertReleased().assertStartCount(1);
runner.getDownloader(1).unblock().assertReleased().assertStartCount(1);
task.assertRemoved();
runner.assertCreatedDownloaderCount(1);
runner.assertCreatedDownloaderCount(2);
downloadManagerListener.blockUntilTasksCompleteAndThrowAnyDownloadError();
}
@ -224,29 +207,29 @@ public class DownloadManagerTest {
@Test
public void downloadNotCancelRemove() throws Throwable {
DownloadRunner runner = new DownloadRunner(uri1);
FakeDownloader downloader1 = runner.getDownloader(0);
FakeDownloader downloader1 = runner.getDownloader(1);
runner.postRemoveAction();
runner.postDownloadAction().postRemoveAction();
downloader1.assertStarted();
runner.postDownloadAction();
downloader1.unblock().assertNotCanceled();
runner.getDownloader(1).unblock().assertNotCanceled();
runner.getDownloader(2).unblock().assertNotCanceled();
downloadManagerListener.blockUntilTasksCompleteAndThrowAnyDownloadError();
}
@Test
public void secondSameRemoveActionIgnored() throws Throwable {
DownloadRunner runner = new DownloadRunner(uri1);
FakeDownloader downloader1 = runner.getDownloader(0);
FakeDownloader downloader1 = runner.getDownloader(1);
runner.postRemoveAction();
runner.postDownloadAction().postRemoveAction();
downloader1.assertStarted();
runner.postRemoveAction();
downloader1.unblock().assertNotCanceled();
runner.getTask().assertRemoved();
runner.assertCreatedDownloaderCount(1);
runner.assertCreatedDownloaderCount(2);
downloadManagerListener.blockUntilTasksCompleteAndThrowAnyDownloadError();
}
@ -316,7 +299,7 @@ public class DownloadManagerTest {
throws Throwable {
setUpDownloadManager(1);
DownloadRunner runner1 = new DownloadRunner(uri1).postDownloadAction();
DownloadRunner runner2 = new DownloadRunner(uri2).postRemoveAction();
DownloadRunner runner2 = new DownloadRunner(uri2).postDownloadAction().postRemoveAction();
FakeDownloader downloader1 = runner1.getDownloader(0);
FakeDownloader downloader2 = runner2.getDownloader(0);
@ -334,7 +317,8 @@ public class DownloadManagerTest {
public void downloadActionFollowingRemove_ifMaxDownloadIs1_isNotStarted() throws Throwable {
setUpDownloadManager(1);
DownloadRunner runner1 = new DownloadRunner(uri1).postDownloadAction();
DownloadRunner runner2 = new DownloadRunner(uri2).postRemoveAction().postDownloadAction();
DownloadRunner runner2 = new DownloadRunner(uri2).postDownloadAction().postRemoveAction();
runner2.postDownloadAction();
FakeDownloader downloader1 = runner1.getDownloader(0);
FakeDownloader downloader2 = runner2.getDownloader(0);
FakeDownloader downloader3 = runner2.getDownloader(1);
@ -356,7 +340,7 @@ public class DownloadManagerTest {
public void getTasks_returnTasks() {
TaskWrapper task1 = new DownloadRunner(uri1).postDownloadAction().getTask();
TaskWrapper task2 = new DownloadRunner(uri2).postDownloadAction().getTask();
TaskWrapper task3 = new DownloadRunner(uri3).postRemoveAction().getTask();
TaskWrapper task3 = new DownloadRunner(uri3).postDownloadAction().postRemoveAction().getTask();
task3.assertRemoving();
DownloadState[] states = downloadManager.getAllDownloadStates();
@ -374,7 +358,7 @@ public class DownloadManagerTest {
DownloadRunner runner3 = new DownloadRunner(uri3);
runner1.postDownloadAction().getTask().assertDownloading();
runner2.postRemoveAction().getTask().assertRemoving();
runner2.postDownloadAction().postRemoveAction().getTask().assertRemoving();
runner2.postDownloadAction();
runOnMainThread(() -> downloadManager.stopDownloads());
@ -382,10 +366,10 @@ public class DownloadManagerTest {
runner1.getTask().assertStopped();
// remove actions aren't stopped.
runner2.getDownloader(0).unblock().assertReleased();
runner2.getDownloader(1).unblock().assertReleased();
runner2.getTask().assertStopped();
// Although remove2 is finished, download2 doesn't start.
runner2.getDownloader(1).assertDoesNotStart();
runner2.getDownloader(2).assertDoesNotStart();
// When a new remove action is added, it cancels stopped download actions with the same media.
runner1.postRemoveAction();
@ -397,7 +381,7 @@ public class DownloadManagerTest {
runOnMainThread(() -> downloadManager.startDownloads());
runner2.getDownloader(1).assertStarted().unblock();
runner2.getDownloader(2).assertStarted().unblock();
runner3.getDownloader(0).assertStarted().unblock();
downloadManagerListener.blockUntilTasksCompleteAndThrowAnyDownloadError();
@ -446,14 +430,14 @@ public class DownloadManagerTest {
DownloadRunner runner3 = new DownloadRunner(uri3);
runner1.postDownloadAction().getTask().assertDownloading();
runner2.postRemoveAction().getTask().assertRemoving();
runner2.postDownloadAction().postRemoveAction().getTask().assertRemoving();
runOnMainThread(() -> downloadManager.stopDownload(runner1.getTask().taskId));
runner1.getTask().assertStopped();
// Other downloads aren't affected.
runner2.getDownloader(0).unblock().assertReleased();
runner2.getDownloader(1).unblock().assertReleased();
// New download actions can be added and they start.
runner3.postDownloadAction().getDownloader(0).assertStarted().unblock();
@ -501,33 +485,36 @@ public class DownloadManagerTest {
private final class DownloadRunner {
private final Uri uri;
private final String id;
private final ArrayList<FakeDownloader> downloaders;
private int createdDownloaderCount = 0;
private FakeDownloader downloader;
private TaskWrapper taskWrapper;
private final TaskWrapper taskWrapper;
private DownloadRunner(Uri uri) {
this.uri = uri;
id = uri.toString();
downloaders = new ArrayList<>();
downloader = addDownloader();
downloaderFactory.registerDownloadRunner(this);
taskWrapper = new TaskWrapper(id);
}
private DownloadRunner postRemoveAction() {
return postAction(createRemoveAction(uri));
runOnMainThread(() -> downloadManager.removeDownload(id));
return this;
}
private DownloadRunner postDownloadAction(StreamKey... keys) {
return postAction(createDownloadAction(uri, keys));
}
private DownloadRunner postAction(DownloadAction action) {
runOnMainThread(() -> downloadManager.handleAction(action));
if (taskWrapper == null) {
taskWrapper = new TaskWrapper(action.id);
} else {
assertThat(action.id).isEqualTo(taskWrapper.taskId);
}
DownloadAction downloadAction =
DownloadAction.createDownloadAction(
id,
DownloadAction.TYPE_PROGRESSIVE,
uri,
Arrays.asList(keys),
/* customCacheKey= */ null,
/* data= */ null);
runOnMainThread(() -> downloadManager.addDownload(downloadAction));
return this;
}
@ -554,10 +541,6 @@ public class DownloadManagerTest {
return taskWrapper;
}
public void setTask(TaskWrapper taskWrapper) {
this.taskWrapper = taskWrapper;
}
private void assertCreatedDownloaderCount(int count) {
assertThat(createdDownloaderCount).isEqualTo(count);
}
@ -599,36 +582,9 @@ public class DownloadManagerTest {
}
private TaskWrapper assertState(@State int expectedState) {
ArrayList<Integer> receivedStates = new ArrayList<>();
while (true) {
Integer state = null;
try {
state = downloadManagerListener.pollStateChange(taskId, ASSERT_TRUE_TIMEOUT);
} catch (InterruptedException e) {
fail(e.getMessage());
}
if (state != null) {
if (expectedState == state) {
downloadManagerListener.assertState(taskId, expectedState, ASSERT_TRUE_TIMEOUT);
return this;
}
receivedStates.add(state);
} else {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < receivedStates.size(); i++) {
if (i > 0) {
sb.append(',');
}
sb.append(DownloadState.getStateString(receivedStates.get(i)));
}
fail(
String.format(
Locale.US,
"expected:<%s> but was:<%s>",
DownloadState.getStateString(expectedState),
sb));
}
}
}
@Override
public boolean equals(Object o) {
@ -647,20 +603,6 @@ public class DownloadManagerTest {
}
}
private static DownloadAction createDownloadAction(Uri uri, StreamKey... keys) {
return DownloadAction.createDownloadAction(
DownloadAction.TYPE_PROGRESSIVE,
uri,
Arrays.asList(keys),
/* customCacheKey= */ null,
/* data= */ null);
}
private static DownloadAction createRemoveAction(Uri uri) {
return DownloadAction.createRemoveAction(
DownloadAction.TYPE_PROGRESSIVE, uri, /* customCacheKey= */ null);
}
private static final class FakeDownloaderFactory implements DownloaderFactory {
private final HashMap<Uri, DownloadRunner> downloaders;

View File

@ -118,20 +118,6 @@ public class DownloadStateTest {
assertEqual(mergedDownloadState, expectedDownloadState);
}
@Test
public void mergeAction_queuedDownloadRemoveAction_stateBecomesRemoving() {
DownloadAction downloadAction = createRemoveAction();
DownloadStateBuilder downloadStateBuilder =
new DownloadStateBuilder(downloadAction).setState(DownloadState.STATE_QUEUED);
DownloadState downloadState = downloadStateBuilder.build();
DownloadState mergedDownloadState = downloadState.mergeAction(downloadAction);
DownloadState expectedDownloadState =
downloadStateBuilder.setState(DownloadState.STATE_REMOVING).build();
assertEqual(mergedDownloadState, expectedDownloadState);
}
@Test
public void mergeAction_removingDownloadDownloadAction_stateBecomesRestarting() {
DownloadAction downloadAction = createDownloadAction();
@ -179,22 +165,6 @@ public class DownloadStateTest {
assertEqual(mergedDownloadState, downloadState);
}
@Test
public void mergeAction_stoppedDownloadRemoveAction_stateBecomesRemoving() {
DownloadAction downloadAction = createRemoveAction();
DownloadStateBuilder downloadStateBuilder =
new DownloadStateBuilder(downloadAction)
.setState(DownloadState.STATE_STOPPED)
.setManualStopReason(DownloadState.MANUAL_STOP_REASON_UNDEFINED);
DownloadState downloadState = downloadStateBuilder.build();
DownloadState mergedDownloadState = downloadState.mergeAction(downloadAction);
DownloadState expectedDownloadState =
downloadStateBuilder.setState(DownloadState.STATE_REMOVING).build();
assertEqual(mergedDownloadState, expectedDownloadState);
}
@Test
public void mergeAction_manualStopReasonSetButNotInStoppedState_stateBecomesStopped() {
DownloadAction downloadAction = createDownloadAction();
@ -227,20 +197,6 @@ public class DownloadStateTest {
assertEqual(mergedDownloadState, expectedDownloadState);
}
@Test
public void mergeAction_restartingDownloadRemoveAction_stateBecomesRemoving() {
DownloadAction downloadAction = createRemoveAction();
DownloadStateBuilder downloadStateBuilder =
new DownloadStateBuilder(downloadAction).setState(DownloadState.STATE_RESTARTING);
DownloadState downloadState = downloadStateBuilder.build();
DownloadState mergedDownloadState = downloadState.mergeAction(downloadAction);
DownloadState expectedDownloadState =
downloadStateBuilder.setState(DownloadState.STATE_REMOVING).build();
assertEqual(mergedDownloadState, expectedDownloadState);
}
@Test
public void mergeAction_returnsMergedKeys() {
StreamKey streamKey1 = new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0);
@ -340,9 +296,4 @@ public class DownloadStateTest {
/* customCacheKey= */ null,
/* data= */ null);
}
private DownloadAction createRemoveAction() {
return DownloadAction.createRemoveAction(
DownloadAction.TYPE_DASH, testUri, /* customCacheKey= */ null);
}
}

View File

@ -22,7 +22,8 @@ import java.nio.charset.Charset;
/** Data for DASH downloading tests. */
/* package */ interface DashDownloadTestData {
Uri TEST_MPD_URI = Uri.parse("test.mpd");
String TEST_ID = "test.mpd";
Uri TEST_MPD_URI = Uri.parse(TEST_ID);
byte[] TEST_MPD =
("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"

View File

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.source.dash.offline;
import static com.google.android.exoplayer2.source.dash.offline.DashDownloadTestData.TEST_ID;
import static com.google.android.exoplayer2.source.dash.offline.DashDownloadTestData.TEST_MPD;
import static com.google.android.exoplayer2.source.dash.offline.DashDownloadTestData.TEST_MPD_URI;
import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCacheEmpty;
@ -22,9 +23,7 @@ import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedDa
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.net.Uri;
import android.os.ConditionVariable;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.offline.DefaultDownloadIndex;
@ -230,11 +229,16 @@ public class DownloadManagerDashTest {
}
private void handleDownloadAction(StreamKey... keys) {
downloadManager.handleAction(newAction(TEST_MPD_URI, false, null, keys));
ArrayList<StreamKey> keysList = new ArrayList<>();
Collections.addAll(keysList, keys);
DownloadAction action =
DownloadAction.createDownloadAction(
DownloadAction.TYPE_DASH, TEST_MPD_URI, keysList, /* customCacheKey= */ null, null);
downloadManager.addDownload(action);
}
private void handleRemoveAction() {
downloadManager.handleAction(newAction(TEST_MPD_URI, true, null));
downloadManager.removeDownload(TEST_ID);
}
private void createDownloadManager() {
@ -257,20 +261,4 @@ public class DownloadManagerDashTest {
});
}
private static DownloadAction newAction(
Uri uri, boolean isRemoveAction, @Nullable byte[] data, StreamKey... keys) {
ArrayList<StreamKey> keysList = new ArrayList<>();
Collections.addAll(keysList, keys);
DownloadAction result;
if (isRemoveAction) {
result =
DownloadAction.createRemoveAction(
DownloadAction.TYPE_DASH, uri, /* customCacheKey= */ null);
} else {
result =
DownloadAction.createDownloadAction(
DownloadAction.TYPE_DASH, uri, keysList, /* customCacheKey= */ null, data);
}
return result;
}
}

View File

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.source.dash.offline;
import static com.google.android.exoplayer2.source.dash.offline.DashDownloadTestData.TEST_ID;
import static com.google.android.exoplayer2.source.dash.offline.DashDownloadTestData.TEST_MPD;
import static com.google.android.exoplayer2.source.dash.offline.DashDownloadTestData.TEST_MPD_URI;
import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCacheEmpty;
@ -22,7 +23,6 @@ import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedDa
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@ -187,37 +187,29 @@ public class DownloadServiceDashTest {
assertCacheEmpty(cache);
}
private void removeAll() throws Throwable {
callDownloadServiceOnStart(newAction(TEST_MPD_URI, true, null));
}
private void downloadKeys(StreamKey... keys) {
callDownloadServiceOnStart(newAction(TEST_MPD_URI, false, null, keys));
}
private void callDownloadServiceOnStart(final DownloadAction action) {
private void removeAll() {
dummyMainThread.runOnMainThread(
() -> {
Intent startIntent =
DownloadService.buildAddActionIntent(context, DownloadService.class, action, false);
DownloadService.buildRemoveDownloadIntent(
context, DownloadService.class, TEST_ID, /* foreground= */ false);
dashDownloadService.onStartCommand(startIntent, 0, 0);
});
}
private static DownloadAction newAction(
Uri uri, boolean isRemoveAction, @Nullable byte[] data, StreamKey... keys) {
private void downloadKeys(StreamKey... keys) {
ArrayList<StreamKey> keysList = new ArrayList<>();
Collections.addAll(keysList, keys);
DownloadAction result;
if (isRemoveAction) {
result =
DownloadAction.createRemoveAction(
DownloadAction.TYPE_DASH, uri, /* customCacheKey= */ null);
} else {
result =
DownloadAction action =
DownloadAction.createDownloadAction(
DownloadAction.TYPE_DASH, uri, keysList, /* customCacheKey= */ null, data);
}
return result;
DownloadAction.TYPE_DASH, TEST_MPD_URI, keysList, /* customCacheKey= */ null, null);
dummyMainThread.runOnMainThread(
() -> {
Intent startIntent =
DownloadService.buildAddActionIntent(
context, DownloadService.class, action, /* foreground= */ false);
dashDownloadService.onStartCommand(startIntent, 0, 0);
});
}
}

View File

@ -16,11 +16,15 @@
package com.google.android.exoplayer2.testutil;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import android.os.ConditionVariable;
import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloadState;
import com.google.android.exoplayer2.offline.DownloadState.State;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@ -115,4 +119,37 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen
return actionStates.get(taskId);
}
}
public void assertState(String taskId, @State int expectedState, int timeoutMs) {
ArrayList<Integer> receivedStates = new ArrayList<>();
while (true) {
Integer state = null;
try {
state = pollStateChange(taskId, timeoutMs);
} catch (InterruptedException e) {
fail(e.getMessage());
}
if (state != null) {
if (expectedState == state) {
return;
}
receivedStates.add(state);
} else {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < receivedStates.size(); i++) {
if (i > 0) {
sb.append(',');
}
sb.append(DownloadState.getStateString(receivedStates.get(i)));
}
fail(
String.format(
Locale.US,
"for download (%s) expected:<%s> but was:<%s>",
taskId,
DownloadState.getStateString(expectedState),
sb));
}
}
}
}