Remove deprecated ActionFile and ActionFileUpgradeUtil
#minor-release PiperOrigin-RevId: 426868933
This commit is contained in:
parent
ce4225f864
commit
4e3d15be87
@ -16,7 +16,6 @@
|
|||||||
package androidx.media3.demo.main;
|
package androidx.media3.demo.main;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.media3.common.util.Log;
|
|
||||||
import androidx.media3.database.DatabaseProvider;
|
import androidx.media3.database.DatabaseProvider;
|
||||||
import androidx.media3.database.StandaloneDatabaseProvider;
|
import androidx.media3.database.StandaloneDatabaseProvider;
|
||||||
import androidx.media3.datasource.DataSource;
|
import androidx.media3.datasource.DataSource;
|
||||||
@ -31,12 +30,9 @@ import androidx.media3.datasource.cronet.CronetDataSource;
|
|||||||
import androidx.media3.datasource.cronet.CronetUtil;
|
import androidx.media3.datasource.cronet.CronetUtil;
|
||||||
import androidx.media3.exoplayer.DefaultRenderersFactory;
|
import androidx.media3.exoplayer.DefaultRenderersFactory;
|
||||||
import androidx.media3.exoplayer.RenderersFactory;
|
import androidx.media3.exoplayer.RenderersFactory;
|
||||||
import androidx.media3.exoplayer.offline.ActionFileUpgradeUtil;
|
|
||||||
import androidx.media3.exoplayer.offline.DefaultDownloadIndex;
|
|
||||||
import androidx.media3.exoplayer.offline.DownloadManager;
|
import androidx.media3.exoplayer.offline.DownloadManager;
|
||||||
import androidx.media3.exoplayer.offline.DownloadNotificationHelper;
|
import androidx.media3.exoplayer.offline.DownloadNotificationHelper;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.CookieHandler;
|
import java.net.CookieHandler;
|
||||||
import java.net.CookieManager;
|
import java.net.CookieManager;
|
||||||
import java.net.CookiePolicy;
|
import java.net.CookiePolicy;
|
||||||
@ -60,8 +56,6 @@ public final class DemoUtil {
|
|||||||
private static final boolean USE_CRONET_FOR_NETWORKING = true;
|
private static final boolean USE_CRONET_FOR_NETWORKING = true;
|
||||||
|
|
||||||
private static final String TAG = "DemoUtil";
|
private static final String TAG = "DemoUtil";
|
||||||
private static final String DOWNLOAD_ACTION_FILE = "actions";
|
|
||||||
private static final String DOWNLOAD_TRACKER_ACTION_FILE = "tracked_actions";
|
|
||||||
private static final String DOWNLOAD_CONTENT_DIRECTORY = "downloads";
|
private static final String DOWNLOAD_CONTENT_DIRECTORY = "downloads";
|
||||||
|
|
||||||
private static DataSource.@MonotonicNonNull Factory dataSourceFactory;
|
private static DataSource.@MonotonicNonNull Factory dataSourceFactory;
|
||||||
@ -155,14 +149,6 @@ public final class DemoUtil {
|
|||||||
|
|
||||||
private static synchronized void ensureDownloadManagerInitialized(Context context) {
|
private static synchronized void ensureDownloadManagerInitialized(Context context) {
|
||||||
if (downloadManager == null) {
|
if (downloadManager == null) {
|
||||||
DefaultDownloadIndex downloadIndex = new DefaultDownloadIndex(getDatabaseProvider(context));
|
|
||||||
upgradeActionFile(
|
|
||||||
context, DOWNLOAD_ACTION_FILE, downloadIndex, /* addNewDownloadsAsCompleted= */ false);
|
|
||||||
upgradeActionFile(
|
|
||||||
context,
|
|
||||||
DOWNLOAD_TRACKER_ACTION_FILE,
|
|
||||||
downloadIndex,
|
|
||||||
/* addNewDownloadsAsCompleted= */ true);
|
|
||||||
downloadManager =
|
downloadManager =
|
||||||
new DownloadManager(
|
new DownloadManager(
|
||||||
context,
|
context,
|
||||||
@ -175,23 +161,6 @@ public final class DemoUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static synchronized void upgradeActionFile(
|
|
||||||
Context context,
|
|
||||||
String fileName,
|
|
||||||
DefaultDownloadIndex downloadIndex,
|
|
||||||
boolean addNewDownloadsAsCompleted) {
|
|
||||||
try {
|
|
||||||
ActionFileUpgradeUtil.upgradeAndDelete(
|
|
||||||
new File(getDownloadDirectory(context), fileName),
|
|
||||||
/* downloadIdProvider= */ null,
|
|
||||||
downloadIndex,
|
|
||||||
/* deleteOnFailure= */ true,
|
|
||||||
addNewDownloadsAsCompleted);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, "Failed to upgrade action file: " + fileName, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static synchronized DatabaseProvider getDatabaseProvider(Context context) {
|
private static synchronized DatabaseProvider getDatabaseProvider(Context context) {
|
||||||
if (databaseProvider == null) {
|
if (databaseProvider == null) {
|
||||||
databaseProvider = new StandaloneDatabaseProvider(context);
|
databaseProvider = new StandaloneDatabaseProvider(context);
|
||||||
|
@ -1,189 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 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 androidx.media3.exoplayer.offline;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.media3.common.MimeTypes;
|
|
||||||
import androidx.media3.common.StreamKey;
|
|
||||||
import androidx.media3.common.util.AtomicFile;
|
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import androidx.media3.exoplayer.offline.DownloadRequest.UnsupportedRequestException;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads {@link DownloadRequest DownloadRequests} from legacy action files.
|
|
||||||
*
|
|
||||||
* @deprecated Legacy action files should be merged into download indices using {@link
|
|
||||||
* ActionFileUpgradeUtil}.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
/* package */ final class ActionFile {
|
|
||||||
|
|
||||||
private static final int VERSION = 0;
|
|
||||||
private static final String DOWNLOAD_TYPE_PROGRESSIVE = "progressive";
|
|
||||||
private static final String DOWNLOAD_TYPE_DASH = "dash";
|
|
||||||
private static final String DOWNLOAD_TYPE_HLS = "hls";
|
|
||||||
private static final String DOWNLOAD_TYPE_SS = "ss";
|
|
||||||
|
|
||||||
private final AtomicFile atomicFile;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param actionFile The file from which {@link DownloadRequest DownloadRequests} will be loaded.
|
|
||||||
*/
|
|
||||||
public ActionFile(File actionFile) {
|
|
||||||
atomicFile = new AtomicFile(actionFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns whether the file or its backup exists. */
|
|
||||||
public boolean exists() {
|
|
||||||
return atomicFile.exists();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Deletes the action file and its backup. */
|
|
||||||
public void delete() {
|
|
||||||
atomicFile.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads {@link DownloadRequest DownloadRequests} from the file.
|
|
||||||
*
|
|
||||||
* @return The loaded {@link DownloadRequest DownloadRequests}, or an empty array if the file does
|
|
||||||
* not exist.
|
|
||||||
* @throws IOException If there is an error reading the file.
|
|
||||||
*/
|
|
||||||
public DownloadRequest[] load() throws IOException {
|
|
||||||
if (!exists()) {
|
|
||||||
return new DownloadRequest[0];
|
|
||||||
}
|
|
||||||
@Nullable InputStream inputStream = null;
|
|
||||||
try {
|
|
||||||
inputStream = atomicFile.openRead();
|
|
||||||
DataInputStream dataInputStream = new DataInputStream(inputStream);
|
|
||||||
int version = dataInputStream.readInt();
|
|
||||||
if (version > VERSION) {
|
|
||||||
throw new IOException("Unsupported action file version: " + version);
|
|
||||||
}
|
|
||||||
int actionCount = dataInputStream.readInt();
|
|
||||||
ArrayList<DownloadRequest> actions = new ArrayList<>();
|
|
||||||
for (int i = 0; i < actionCount; i++) {
|
|
||||||
try {
|
|
||||||
actions.add(readDownloadRequest(dataInputStream));
|
|
||||||
} catch (UnsupportedRequestException e) {
|
|
||||||
// remove DownloadRequest is not supported. Ignore and continue loading rest.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return actions.toArray(new DownloadRequest[0]);
|
|
||||||
} finally {
|
|
||||||
Util.closeQuietly(inputStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DownloadRequest readDownloadRequest(DataInputStream input) throws IOException {
|
|
||||||
String downloadType = input.readUTF();
|
|
||||||
int version = input.readInt();
|
|
||||||
|
|
||||||
Uri uri = Uri.parse(input.readUTF());
|
|
||||||
boolean isRemoveAction = input.readBoolean();
|
|
||||||
|
|
||||||
int dataLength = input.readInt();
|
|
||||||
@Nullable byte[] data;
|
|
||||||
if (dataLength != 0) {
|
|
||||||
data = new byte[dataLength];
|
|
||||||
input.readFully(data);
|
|
||||||
} else {
|
|
||||||
data = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialized version 0 progressive actions did not contain keys.
|
|
||||||
boolean isLegacyProgressive = version == 0 && DOWNLOAD_TYPE_PROGRESSIVE.equals(downloadType);
|
|
||||||
List<StreamKey> keys = new ArrayList<>();
|
|
||||||
if (!isLegacyProgressive) {
|
|
||||||
int keyCount = input.readInt();
|
|
||||||
for (int i = 0; i < keyCount; i++) {
|
|
||||||
keys.add(readKey(downloadType, version, input));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialized version 0 and 1 DASH/HLS/SS actions did not contain a custom cache key.
|
|
||||||
boolean isLegacySegmented =
|
|
||||||
version < 2
|
|
||||||
&& (DOWNLOAD_TYPE_DASH.equals(downloadType)
|
|
||||||
|| DOWNLOAD_TYPE_HLS.equals(downloadType)
|
|
||||||
|| DOWNLOAD_TYPE_SS.equals(downloadType));
|
|
||||||
@Nullable String customCacheKey = null;
|
|
||||||
if (!isLegacySegmented) {
|
|
||||||
customCacheKey = input.readBoolean() ? input.readUTF() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialized version 0, 1 and 2 did not contain an id. We need to generate one.
|
|
||||||
String id = version < 3 ? generateDownloadId(uri, customCacheKey) : input.readUTF();
|
|
||||||
|
|
||||||
if (isRemoveAction) {
|
|
||||||
// Remove actions are not supported anymore.
|
|
||||||
throw new UnsupportedRequestException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DownloadRequest.Builder(id, uri)
|
|
||||||
.setMimeType(inferMimeType(downloadType))
|
|
||||||
.setStreamKeys(keys)
|
|
||||||
.setCustomCacheKey(customCacheKey)
|
|
||||||
.setData(data)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StreamKey readKey(String type, int version, DataInputStream input)
|
|
||||||
throws IOException {
|
|
||||||
int periodIndex;
|
|
||||||
int groupIndex;
|
|
||||||
int trackIndex;
|
|
||||||
|
|
||||||
// Serialized version 0 HLS/SS actions did not contain a period index.
|
|
||||||
if ((DOWNLOAD_TYPE_HLS.equals(type) || DOWNLOAD_TYPE_SS.equals(type)) && version == 0) {
|
|
||||||
periodIndex = 0;
|
|
||||||
groupIndex = input.readInt();
|
|
||||||
trackIndex = input.readInt();
|
|
||||||
} else {
|
|
||||||
periodIndex = input.readInt();
|
|
||||||
groupIndex = input.readInt();
|
|
||||||
trackIndex = input.readInt();
|
|
||||||
}
|
|
||||||
return new StreamKey(periodIndex, groupIndex, trackIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String inferMimeType(String downloadType) {
|
|
||||||
switch (downloadType) {
|
|
||||||
case DOWNLOAD_TYPE_DASH:
|
|
||||||
return MimeTypes.APPLICATION_MPD;
|
|
||||||
case DOWNLOAD_TYPE_HLS:
|
|
||||||
return MimeTypes.APPLICATION_M3U8;
|
|
||||||
case DOWNLOAD_TYPE_SS:
|
|
||||||
return MimeTypes.APPLICATION_SS;
|
|
||||||
case DOWNLOAD_TYPE_PROGRESSIVE:
|
|
||||||
default:
|
|
||||||
return MimeTypes.VIDEO_UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String generateDownloadId(Uri uri, @Nullable String customCacheKey) {
|
|
||||||
return customCacheKey != null ? customCacheKey : uri.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,122 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2019 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 androidx.media3.exoplayer.offline;
|
|
||||||
|
|
||||||
import static androidx.media3.exoplayer.offline.Download.STATE_QUEUED;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.WorkerThread;
|
|
||||||
import androidx.media3.common.C;
|
|
||||||
import androidx.media3.common.util.UnstableApi;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/** Utility class for upgrading legacy action files into {@link DefaultDownloadIndex}. */
|
|
||||||
@UnstableApi
|
|
||||||
public final class ActionFileUpgradeUtil {
|
|
||||||
|
|
||||||
/** Provides download IDs during action file upgrade. */
|
|
||||||
public interface DownloadIdProvider {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a download id for given request.
|
|
||||||
*
|
|
||||||
* @param downloadRequest The request for which an ID is required.
|
|
||||||
* @return A corresponding download ID.
|
|
||||||
*/
|
|
||||||
String getId(DownloadRequest downloadRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ActionFileUpgradeUtil() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Merges {@link DownloadRequest DownloadRequests} contained in a legacy action file into a {@link
|
|
||||||
* DefaultDownloadIndex}, deleting the action file if the merge is successful or if {@code
|
|
||||||
* deleteOnFailure} is {@code true}.
|
|
||||||
*
|
|
||||||
* <p>This method must not be called while the {@link DefaultDownloadIndex} is being used by a
|
|
||||||
* {@link DownloadManager}.
|
|
||||||
*
|
|
||||||
* <p>This method may be slow and shouldn't normally be called on the main thread.
|
|
||||||
*
|
|
||||||
* @param actionFilePath The action file path.
|
|
||||||
* @param downloadIdProvider A download ID provider, or {@code null}. If {@code null} then ID of
|
|
||||||
* each download will be its custom cache key if one is specified, or else its URL.
|
|
||||||
* @param downloadIndex The index into which the requests will be merged.
|
|
||||||
* @param deleteOnFailure Whether to delete the action file if the merge fails.
|
|
||||||
* @param addNewDownloadsAsCompleted Whether to add new downloads as completed.
|
|
||||||
* @throws IOException If an error occurs loading or merging the requests.
|
|
||||||
*/
|
|
||||||
@WorkerThread
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public static void upgradeAndDelete(
|
|
||||||
File actionFilePath,
|
|
||||||
@Nullable DownloadIdProvider downloadIdProvider,
|
|
||||||
DefaultDownloadIndex downloadIndex,
|
|
||||||
boolean deleteOnFailure,
|
|
||||||
boolean addNewDownloadsAsCompleted)
|
|
||||||
throws IOException {
|
|
||||||
ActionFile actionFile = new ActionFile(actionFilePath);
|
|
||||||
if (actionFile.exists()) {
|
|
||||||
boolean success = false;
|
|
||||||
try {
|
|
||||||
long nowMs = System.currentTimeMillis();
|
|
||||||
for (DownloadRequest request : actionFile.load()) {
|
|
||||||
if (downloadIdProvider != null) {
|
|
||||||
request = request.copyWithId(downloadIdProvider.getId(request));
|
|
||||||
}
|
|
||||||
mergeRequest(request, downloadIndex, addNewDownloadsAsCompleted, nowMs);
|
|
||||||
}
|
|
||||||
success = true;
|
|
||||||
} finally {
|
|
||||||
if (success || deleteOnFailure) {
|
|
||||||
actionFile.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Merges a {@link DownloadRequest} into a {@link DefaultDownloadIndex}.
|
|
||||||
*
|
|
||||||
* @param request The request to be merged.
|
|
||||||
* @param downloadIndex The index into which the request will be merged.
|
|
||||||
* @param addNewDownloadAsCompleted Whether to add new downloads as completed.
|
|
||||||
* @throws IOException If an error occurs merging the request.
|
|
||||||
*/
|
|
||||||
/* package */ static void mergeRequest(
|
|
||||||
DownloadRequest request,
|
|
||||||
DefaultDownloadIndex downloadIndex,
|
|
||||||
boolean addNewDownloadAsCompleted,
|
|
||||||
long nowMs)
|
|
||||||
throws IOException {
|
|
||||||
@Nullable Download download = downloadIndex.getDownload(request.id);
|
|
||||||
if (download != null) {
|
|
||||||
download = DownloadManager.mergeRequest(download, request, download.stopReason, nowMs);
|
|
||||||
} else {
|
|
||||||
download =
|
|
||||||
new Download(
|
|
||||||
request,
|
|
||||||
addNewDownloadAsCompleted ? Download.STATE_COMPLETED : STATE_QUEUED,
|
|
||||||
/* startTimeMs= */ nowMs,
|
|
||||||
/* updateTimeMs= */ nowMs,
|
|
||||||
/* contentLength= */ C.LENGTH_UNSET,
|
|
||||||
Download.STOP_REASON_NONE,
|
|
||||||
Download.FAILURE_REASON_NONE);
|
|
||||||
}
|
|
||||||
downloadIndex.putDownload(download);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,133 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 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 androidx.media3.exoplayer.offline;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import androidx.media3.common.MimeTypes;
|
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import androidx.media3.test.utils.TestUtil;
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
/** Unit tests for {@link ActionFile}. */
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class ActionFileTest {
|
|
||||||
|
|
||||||
private File tempFile;
|
|
||||||
private DownloadRequest expectedAction1;
|
|
||||||
private DownloadRequest expectedAction2;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
tempFile = Util.createTempFile(ApplicationProvider.getApplicationContext(), "ExoPlayerTest");
|
|
||||||
expectedAction1 =
|
|
||||||
buildExpectedRequest(Uri.parse("http://test1.uri"), TestUtil.buildTestData(16));
|
|
||||||
expectedAction2 =
|
|
||||||
buildExpectedRequest(Uri.parse("http://test2.uri"), TestUtil.buildTestData(32));
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() throws Exception {
|
|
||||||
tempFile.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loadNoDataThrowsIOException() throws Exception {
|
|
||||||
ActionFile actionFile = getActionFile("media/offline/action_file_no_data.exi");
|
|
||||||
try {
|
|
||||||
actionFile.load();
|
|
||||||
Assert.fail();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Expected exception.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loadIncompleteHeaderThrowsIOException() throws Exception {
|
|
||||||
ActionFile actionFile = getActionFile("media/offline/action_file_incomplete_header.exi");
|
|
||||||
try {
|
|
||||||
actionFile.load();
|
|
||||||
Assert.fail();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Expected exception.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loadZeroActions() throws Exception {
|
|
||||||
ActionFile actionFile = getActionFile("media/offline/action_file_zero_actions.exi");
|
|
||||||
DownloadRequest[] actions = actionFile.load();
|
|
||||||
assertThat(actions).isNotNull();
|
|
||||||
assertThat(actions).hasLength(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loadOneAction() throws Exception {
|
|
||||||
ActionFile actionFile = getActionFile("media/offline/action_file_one_action.exi");
|
|
||||||
DownloadRequest[] actions = actionFile.load();
|
|
||||||
assertThat(actions).hasLength(1);
|
|
||||||
assertThat(actions[0]).isEqualTo(expectedAction1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loadTwoActions() throws Exception {
|
|
||||||
ActionFile actionFile = getActionFile("media/offline/action_file_two_actions.exi");
|
|
||||||
DownloadRequest[] actions = actionFile.load();
|
|
||||||
assertThat(actions).hasLength(2);
|
|
||||||
assertThat(actions[0]).isEqualTo(expectedAction1);
|
|
||||||
assertThat(actions[1]).isEqualTo(expectedAction2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loadUnsupportedVersion() throws Exception {
|
|
||||||
ActionFile actionFile = getActionFile("media/offline/action_file_unsupported_version.exi");
|
|
||||||
try {
|
|
||||||
actionFile.load();
|
|
||||||
Assert.fail();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Expected exception.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ActionFile getActionFile(String fileName) throws IOException {
|
|
||||||
// Copy the test data from the asset to where the ActionFile expects it to be.
|
|
||||||
byte[] actionFileBytes =
|
|
||||||
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), fileName);
|
|
||||||
try (FileOutputStream output = new FileOutputStream(tempFile)) {
|
|
||||||
output.write(actionFileBytes);
|
|
||||||
}
|
|
||||||
// Load the action file.
|
|
||||||
return new ActionFile(tempFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DownloadRequest buildExpectedRequest(Uri uri, byte[] data) {
|
|
||||||
return new DownloadRequest.Builder(/* id= */ uri.toString(), uri)
|
|
||||||
.setMimeType(MimeTypes.VIDEO_UNKNOWN)
|
|
||||||
.setData(data)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,309 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2019 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 androidx.media3.exoplayer.offline;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import androidx.media3.common.MimeTypes;
|
|
||||||
import androidx.media3.common.StreamKey;
|
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import androidx.media3.database.StandaloneDatabaseProvider;
|
|
||||||
import androidx.media3.test.utils.TestUtil;
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
/** Unit tests for {@link ActionFileUpgradeUtil}. */
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class ActionFileUpgradeUtilTest {
|
|
||||||
|
|
||||||
private static final long NOW_MS = 1234;
|
|
||||||
|
|
||||||
private File tempFile;
|
|
||||||
private StandaloneDatabaseProvider databaseProvider;
|
|
||||||
private DefaultDownloadIndex downloadIndex;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
tempFile = Util.createTempFile(ApplicationProvider.getApplicationContext(), "ExoPlayerTest");
|
|
||||||
databaseProvider = new StandaloneDatabaseProvider(ApplicationProvider.getApplicationContext());
|
|
||||||
downloadIndex = new DefaultDownloadIndex(databaseProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
databaseProvider.close();
|
|
||||||
tempFile.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void upgradeAndDelete_progressiveActionFile_createsDownloads() throws IOException {
|
|
||||||
byte[] actionFileBytes =
|
|
||||||
TestUtil.getByteArray(
|
|
||||||
ApplicationProvider.getApplicationContext(),
|
|
||||||
"media/offline/action_file_for_download_index_upgrade_progressive.exi");
|
|
||||||
try (FileOutputStream output = new FileOutputStream(tempFile)) {
|
|
||||||
output.write(actionFileBytes);
|
|
||||||
}
|
|
||||||
DownloadRequest expectedRequest1 =
|
|
||||||
new DownloadRequest.Builder(
|
|
||||||
/* id= */ "http://www.test.com/1/video.mp4",
|
|
||||||
Uri.parse("http://www.test.com/1/video.mp4"))
|
|
||||||
.setMimeType(MimeTypes.VIDEO_UNKNOWN)
|
|
||||||
.build();
|
|
||||||
DownloadRequest expectedRequest2 =
|
|
||||||
new DownloadRequest.Builder(
|
|
||||||
/* id= */ "customCacheKey", Uri.parse("http://www.test.com/2/video.mp4"))
|
|
||||||
.setMimeType(MimeTypes.VIDEO_UNKNOWN)
|
|
||||||
.setCustomCacheKey("customCacheKey")
|
|
||||||
.setData(new byte[] {0, 1, 2, 3})
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ActionFileUpgradeUtil.upgradeAndDelete(
|
|
||||||
tempFile,
|
|
||||||
/* downloadIdProvider= */ null,
|
|
||||||
downloadIndex,
|
|
||||||
/* deleteOnFailure= */ true,
|
|
||||||
/* addNewDownloadsAsCompleted= */ false);
|
|
||||||
|
|
||||||
assertThat(tempFile.exists()).isFalse();
|
|
||||||
assertDownloadIndexContainsRequest(expectedRequest1, Download.STATE_QUEUED);
|
|
||||||
assertDownloadIndexContainsRequest(expectedRequest2, Download.STATE_QUEUED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void upgradeAndDelete_dashActionFile_createsDownloads() throws IOException {
|
|
||||||
byte[] actionFileBytes =
|
|
||||||
TestUtil.getByteArray(
|
|
||||||
ApplicationProvider.getApplicationContext(),
|
|
||||||
"media/offline/action_file_for_download_index_upgrade_dash.exi");
|
|
||||||
try (FileOutputStream output = new FileOutputStream(tempFile)) {
|
|
||||||
output.write(actionFileBytes);
|
|
||||||
}
|
|
||||||
DownloadRequest expectedRequest1 =
|
|
||||||
new DownloadRequest.Builder(
|
|
||||||
/* id= */ "http://www.test.com/1/manifest.mpd",
|
|
||||||
Uri.parse("http://www.test.com/1/manifest.mpd"))
|
|
||||||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
|
||||||
.build();
|
|
||||||
DownloadRequest expectedRequest2 =
|
|
||||||
new DownloadRequest.Builder(
|
|
||||||
/* id= */ "http://www.test.com/2/manifest.mpd",
|
|
||||||
Uri.parse("http://www.test.com/2/manifest.mpd"))
|
|
||||||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
|
||||||
.setStreamKeys(
|
|
||||||
ImmutableList.of(
|
|
||||||
new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0),
|
|
||||||
new StreamKey(/* groupIndex= */ 1, /* trackIndex= */ 1)))
|
|
||||||
.setData(new byte[] {0, 1, 2, 3})
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ActionFileUpgradeUtil.upgradeAndDelete(
|
|
||||||
tempFile,
|
|
||||||
/* downloadIdProvider= */ null,
|
|
||||||
downloadIndex,
|
|
||||||
/* deleteOnFailure= */ true,
|
|
||||||
/* addNewDownloadsAsCompleted= */ false);
|
|
||||||
|
|
||||||
assertThat(tempFile.exists()).isFalse();
|
|
||||||
assertDownloadIndexContainsRequest(expectedRequest1, Download.STATE_QUEUED);
|
|
||||||
assertDownloadIndexContainsRequest(expectedRequest2, Download.STATE_QUEUED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void upgradeAndDelete_hlsActionFile_createsDownloads() throws IOException {
|
|
||||||
byte[] actionFileBytes =
|
|
||||||
TestUtil.getByteArray(
|
|
||||||
ApplicationProvider.getApplicationContext(),
|
|
||||||
"media/offline/action_file_for_download_index_upgrade_hls.exi");
|
|
||||||
try (FileOutputStream output = new FileOutputStream(tempFile)) {
|
|
||||||
output.write(actionFileBytes);
|
|
||||||
}
|
|
||||||
DownloadRequest expectedRequest1 =
|
|
||||||
new DownloadRequest.Builder(
|
|
||||||
/* id= */ "http://www.test.com/1/manifest.m3u8",
|
|
||||||
Uri.parse("http://www.test.com/1/manifest.m3u8"))
|
|
||||||
.setMimeType(MimeTypes.APPLICATION_M3U8)
|
|
||||||
.build();
|
|
||||||
DownloadRequest expectedRequest2 =
|
|
||||||
new DownloadRequest.Builder(
|
|
||||||
/* id= */ "http://www.test.com/2/manifest.m3u8",
|
|
||||||
Uri.parse("http://www.test.com/2/manifest.m3u8"))
|
|
||||||
.setMimeType(MimeTypes.APPLICATION_M3U8)
|
|
||||||
.setStreamKeys(
|
|
||||||
ImmutableList.of(
|
|
||||||
new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0),
|
|
||||||
new StreamKey(/* groupIndex= */ 1, /* trackIndex= */ 1)))
|
|
||||||
.setData(new byte[] {0, 1, 2, 3})
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ActionFileUpgradeUtil.upgradeAndDelete(
|
|
||||||
tempFile,
|
|
||||||
/* downloadIdProvider= */ null,
|
|
||||||
downloadIndex,
|
|
||||||
/* deleteOnFailure= */ true,
|
|
||||||
/* addNewDownloadsAsCompleted= */ false);
|
|
||||||
|
|
||||||
assertThat(tempFile.exists()).isFalse();
|
|
||||||
assertDownloadIndexContainsRequest(expectedRequest1, Download.STATE_QUEUED);
|
|
||||||
assertDownloadIndexContainsRequest(expectedRequest2, Download.STATE_QUEUED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void upgradeAndDelete_smoothStreamingActionFile_createsDownloads() throws IOException {
|
|
||||||
byte[] actionFileBytes =
|
|
||||||
TestUtil.getByteArray(
|
|
||||||
ApplicationProvider.getApplicationContext(),
|
|
||||||
"media/offline/action_file_for_download_index_upgrade_ss.exi");
|
|
||||||
try (FileOutputStream output = new FileOutputStream(tempFile)) {
|
|
||||||
output.write(actionFileBytes);
|
|
||||||
}
|
|
||||||
DownloadRequest expectedRequest1 =
|
|
||||||
new DownloadRequest.Builder(
|
|
||||||
/* id= */ "http://www.test.com/1/video.ism/manifest",
|
|
||||||
Uri.parse("http://www.test.com/1/video.ism/manifest"))
|
|
||||||
.setMimeType(MimeTypes.APPLICATION_SS)
|
|
||||||
.build();
|
|
||||||
DownloadRequest expectedRequest2 =
|
|
||||||
new DownloadRequest.Builder(
|
|
||||||
/* id= */ "http://www.test.com/2/video.ism/manifest",
|
|
||||||
Uri.parse("http://www.test.com/2/video.ism/manifest"))
|
|
||||||
.setMimeType(MimeTypes.APPLICATION_SS)
|
|
||||||
.setStreamKeys(
|
|
||||||
ImmutableList.of(
|
|
||||||
new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0),
|
|
||||||
new StreamKey(/* groupIndex= */ 1, /* trackIndex= */ 1)))
|
|
||||||
.setData(new byte[] {0, 1, 2, 3})
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ActionFileUpgradeUtil.upgradeAndDelete(
|
|
||||||
tempFile,
|
|
||||||
/* downloadIdProvider= */ null,
|
|
||||||
downloadIndex,
|
|
||||||
/* deleteOnFailure= */ true,
|
|
||||||
/* addNewDownloadsAsCompleted= */ false);
|
|
||||||
|
|
||||||
assertThat(tempFile.exists()).isFalse();
|
|
||||||
assertDownloadIndexContainsRequest(expectedRequest1, Download.STATE_QUEUED);
|
|
||||||
assertDownloadIndexContainsRequest(expectedRequest2, Download.STATE_QUEUED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void mergeRequest_nonExistingDownload_createsNewDownload() throws IOException {
|
|
||||||
DownloadRequest request =
|
|
||||||
new DownloadRequest.Builder(/* id= */ "id", Uri.parse("https://www.test.com/download"))
|
|
||||||
.setStreamKeys(
|
|
||||||
ImmutableList.of(
|
|
||||||
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2),
|
|
||||||
new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5)))
|
|
||||||
.setKeySetId(new byte[] {1, 2, 3, 4})
|
|
||||||
.setCustomCacheKey("key123")
|
|
||||||
.setData(new byte[] {1, 2, 3, 4})
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ActionFileUpgradeUtil.mergeRequest(
|
|
||||||
request, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
|
|
||||||
|
|
||||||
assertDownloadIndexContainsRequest(request, Download.STATE_QUEUED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void mergeRequest_existingDownload_createsMergedDownload() throws IOException {
|
|
||||||
StreamKey streamKey1 =
|
|
||||||
new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5);
|
|
||||||
StreamKey streamKey2 =
|
|
||||||
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
|
|
||||||
DownloadRequest request1 =
|
|
||||||
new DownloadRequest.Builder(/* id= */ "id", Uri.parse("https://www.test.com/download1"))
|
|
||||||
.setStreamKeys(ImmutableList.of(streamKey1))
|
|
||||||
.setKeySetId(new byte[] {1, 2, 3, 4})
|
|
||||||
.setCustomCacheKey("key123")
|
|
||||||
.setData(new byte[] {1, 2, 3, 4})
|
|
||||||
.build();
|
|
||||||
DownloadRequest request2 =
|
|
||||||
new DownloadRequest.Builder(/* id= */ "id", Uri.parse("https://www.test.com/download2"))
|
|
||||||
.setMimeType(MimeTypes.APPLICATION_MP4)
|
|
||||||
.setStreamKeys(ImmutableList.of(streamKey2))
|
|
||||||
.setKeySetId(new byte[] {5, 4, 3, 2, 1})
|
|
||||||
.setCustomCacheKey("key345")
|
|
||||||
.setData(new byte[] {5, 4, 3, 2, 1})
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ActionFileUpgradeUtil.mergeRequest(
|
|
||||||
request1, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
|
|
||||||
ActionFileUpgradeUtil.mergeRequest(
|
|
||||||
request2, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
|
|
||||||
Download download = downloadIndex.getDownload(request2.id);
|
|
||||||
|
|
||||||
assertThat(download).isNotNull();
|
|
||||||
assertThat(download.request.mimeType).isEqualTo(MimeTypes.APPLICATION_MP4);
|
|
||||||
assertThat(download.request.customCacheKey).isEqualTo(request2.customCacheKey);
|
|
||||||
assertThat(download.request.data).isEqualTo(request2.data);
|
|
||||||
assertThat(download.request.uri).isEqualTo(request2.uri);
|
|
||||||
assertThat(download.request.streamKeys).containsExactly(streamKey1, streamKey2);
|
|
||||||
assertThat(download.request.keySetId).isEqualTo(request2.keySetId);
|
|
||||||
assertThat(download.state).isEqualTo(Download.STATE_QUEUED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void mergeRequest_addNewDownloadAsCompleted() throws IOException {
|
|
||||||
StreamKey streamKey1 =
|
|
||||||
new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5);
|
|
||||||
StreamKey streamKey2 =
|
|
||||||
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
|
|
||||||
DownloadRequest request1 =
|
|
||||||
new DownloadRequest.Builder(/* id= */ "id1", Uri.parse("https://www.test.com/download1"))
|
|
||||||
.setStreamKeys(ImmutableList.of(streamKey1))
|
|
||||||
.setKeySetId(new byte[] {1, 2, 3, 4})
|
|
||||||
.setCustomCacheKey("key123")
|
|
||||||
.setData(new byte[] {1, 2, 3, 4})
|
|
||||||
.build();
|
|
||||||
DownloadRequest request2 =
|
|
||||||
new DownloadRequest.Builder(/* id= */ "id2", Uri.parse("https://www.test.com/download2"))
|
|
||||||
.setStreamKeys(ImmutableList.of(streamKey2))
|
|
||||||
.setKeySetId(new byte[] {5, 4, 3, 2, 1})
|
|
||||||
.setCustomCacheKey("key456")
|
|
||||||
.setData(new byte[] {5, 4, 3, 2, 1})
|
|
||||||
.build();
|
|
||||||
ActionFileUpgradeUtil.mergeRequest(
|
|
||||||
request1, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
|
|
||||||
|
|
||||||
// Merging existing download, keeps it queued.
|
|
||||||
ActionFileUpgradeUtil.mergeRequest(
|
|
||||||
request1, downloadIndex, /* addNewDownloadAsCompleted= */ true, NOW_MS);
|
|
||||||
assertThat(downloadIndex.getDownload(request1.id).state).isEqualTo(Download.STATE_QUEUED);
|
|
||||||
|
|
||||||
// New download is merged as completed.
|
|
||||||
ActionFileUpgradeUtil.mergeRequest(
|
|
||||||
request2, downloadIndex, /* addNewDownloadAsCompleted= */ true, NOW_MS);
|
|
||||||
assertThat(downloadIndex.getDownload(request2.id).state).isEqualTo(Download.STATE_COMPLETED);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertDownloadIndexContainsRequest(DownloadRequest request, int state)
|
|
||||||
throws IOException {
|
|
||||||
Download download = downloadIndex.getDownload(request.id);
|
|
||||||
assertThat(download.request).isEqualTo(request);
|
|
||||||
assertThat(download.state).isEqualTo(state);
|
|
||||||
}
|
|
||||||
}
|
|
@ -41,8 +41,7 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
/** Unit tests for {@link ActionFile}. */
|
/** Unit tests for {@link ProgressiveDownloader}. */
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class ProgressiveDownloaderTest {
|
public class ProgressiveDownloaderTest {
|
||||||
|
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user