Commit ec77f737 by eguven Committed by Oliver Woodman

Make DownloadManager watch requirements directly

PiperOrigin-RevId: 229544734
parent 8adc16a6
......@@ -101,10 +101,12 @@ public class DemoApplication extends Application {
new DownloaderConstructorHelper(getDownloadCache(), buildHttpDataSourceFactory());
downloadManager =
new DownloadManager(
this,
new File(getDownloadDirectory(), DOWNLOAD_ACTION_FILE),
new DefaultDownloaderFactory(downloaderConstructorHelper),
MAX_SIMULTANEOUS_DOWNLOADS,
DownloadManager.DEFAULT_MIN_RETRY_COUNT);
DownloadManager.DEFAULT_MIN_RETRY_COUNT,
DownloadManager.DEFAULT_REQUIREMENTS);
downloadTracker =
new DownloadTracker(
/* context= */ this,
......
......@@ -41,6 +41,7 @@ import com.google.android.exoplayer2.offline.DownloadService;
import com.google.android.exoplayer2.offline.DownloadState;
import com.google.android.exoplayer2.offline.ProgressiveDownloadHelper;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.scheduler.Requirements;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.dash.offline.DashDownloadHelper;
import com.google.android.exoplayer2.source.hls.offline.HlsDownloadHelper;
......@@ -159,6 +160,14 @@ public class DownloadTracker implements DownloadManager.Listener {
// Do nothing.
}
@Override
public void onRequirementsStateChanged(
DownloadManager downloadManager,
Requirements requirements,
@Requirements.RequirementFlags int notMetRequirements) {
// Do nothing.
}
// Internal methods
private void loadTrackedActions() {
......
......@@ -28,12 +28,15 @@ import static com.google.android.exoplayer2.offline.DownloadState.STATE_STOPPED;
import static com.google.android.exoplayer2.offline.DownloadState.STOP_FLAG_DOWNLOAD_MANAGER_NOT_READY;
import static com.google.android.exoplayer2.offline.DownloadState.STOP_FLAG_STOPPED;
import android.content.Context;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.scheduler.Requirements;
import com.google.android.exoplayer2.scheduler.RequirementsWatcher;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
import java.io.File;
......@@ -74,18 +77,35 @@ public final class DownloadManager {
* @param downloadManager The reporting instance.
*/
void onIdle(DownloadManager downloadManager);
/**
* Called when the download requirements state changed.
*
* @param downloadManager The reporting instance.
* @param requirements Requirements needed to be met to start downloads.
* @param notMetRequirements {@link Requirements.RequirementFlags RequirementFlags} that are not
* met, or 0.
*/
void onRequirementsStateChanged(
DownloadManager downloadManager,
Requirements requirements,
@Requirements.RequirementFlags int notMetRequirements);
}
/** The default maximum number of simultaneous downloads. */
public static final int DEFAULT_MAX_SIMULTANEOUS_DOWNLOADS = 1;
/** The default minimum number of times a download must be retried before failing. */
public static final int DEFAULT_MIN_RETRY_COUNT = 5;
/** The default requirement is that the device has network connectivity. */
public static final Requirements DEFAULT_REQUIREMENTS =
new Requirements(Requirements.NETWORK_TYPE_ANY, false, false);
private static final String TAG = "DownloadManager";
private static final boolean DEBUG = false;
private final int maxActiveDownloads;
private final int minRetryCount;
private final Context context;
private final ActionFile actionFile;
private final DownloaderFactory downloaderFactory;
private final ArrayList<Download> downloads;
......@@ -99,31 +119,43 @@ public final class DownloadManager {
private boolean initialized;
private boolean released;
@DownloadState.StopFlags private int stickyStopFlags;
private RequirementsWatcher requirementsWatcher;
/**
* Constructs a {@link DownloadManager}.
*
* @param context Any context.
* @param actionFile The file in which active actions are saved.
* @param downloaderFactory A factory for creating {@link Downloader}s.
*/
public DownloadManager(File actionFile, DownloaderFactory downloaderFactory) {
public DownloadManager(Context context, File actionFile, DownloaderFactory downloaderFactory) {
this(
actionFile, downloaderFactory, DEFAULT_MAX_SIMULTANEOUS_DOWNLOADS, DEFAULT_MIN_RETRY_COUNT);
context,
actionFile,
downloaderFactory,
DEFAULT_MAX_SIMULTANEOUS_DOWNLOADS,
DEFAULT_MIN_RETRY_COUNT,
DEFAULT_REQUIREMENTS);
}
/**
* Constructs a {@link DownloadManager}.
*
* @param context Any context.
* @param actionFile The file in which active actions are saved.
* @param downloaderFactory A factory for creating {@link Downloader}s.
* @param maxSimultaneousDownloads The maximum number of simultaneous downloads.
* @param minRetryCount The minimum number of times a download must be retried before failing.
* @param requirements The requirements needed to be met to start downloads.
*/
public DownloadManager(
Context context,
File actionFile,
DownloaderFactory downloaderFactory,
int maxSimultaneousDownloads,
int minRetryCount) {
int minRetryCount,
Requirements requirements) {
this.context = context.getApplicationContext();
this.actionFile = new ActionFile(actionFile);
this.downloaderFactory = downloaderFactory;
this.maxActiveDownloads = maxSimultaneousDownloads;
......@@ -146,11 +178,31 @@ public final class DownloadManager {
listeners = new CopyOnWriteArraySet<>();
actionQueue = new ArrayDeque<>();
watchRequirements(requirements);
loadActions();
logd("Created");
}
/**
* Sets the requirements needed to be met to start downloads.
*
* @param requirements Need to be met to start downloads.
*/
public void setRequirements(Requirements requirements) {
Assertions.checkState(!released);
if (requirements.equals(requirementsWatcher.getRequirements())) {
return;
}
requirementsWatcher.stop();
notifyListenersRequirementsStateChange(watchRequirements(requirements));
}
/** Returns the requirements needed to be met to start downloads. */
public Requirements getRequirements() {
return requirementsWatcher.getRequirements();
}
/**
* Adds a {@link Listener}.
*
* @param listener The listener to be added.
......@@ -278,6 +330,9 @@ public final class DownloadManager {
}
setStopFlags(STOP_FLAG_DOWNLOAD_MANAGER_NOT_READY);
released = true;
if (requirementsWatcher != null) {
requirementsWatcher.stop();
}
final ConditionVariable fileIOFinishedCondition = new ConditionVariable();
fileIOHandler.post(fileIOFinishedCondition::open);
fileIOFinishedCondition.block();
......@@ -346,6 +401,15 @@ public final class DownloadManager {
}
}
private void notifyListenersRequirementsStateChange(
@Requirements.RequirementFlags int notMetRequirements) {
logdFlags("Not met requirements are changed", notMetRequirements);
for (Listener listener : listeners) {
listener.onRequirementsStateChanged(
DownloadManager.this, requirementsWatcher.getRequirements(), notMetRequirements);
}
}
private void loadActions() {
fileIOHandler.post(
() -> {
......@@ -420,6 +484,18 @@ public final class DownloadManager {
}
}
@Requirements.RequirementFlags
private int watchRequirements(Requirements requirements) {
requirementsWatcher = new RequirementsWatcher(context, new RequirementListener(), requirements);
@Requirements.RequirementFlags int notMetRequirements = requirementsWatcher.start();
if (notMetRequirements == 0) {
startDownloads();
} else {
stopDownloads();
}
return notMetRequirements;
}
private static final class Download {
private final String id;
......@@ -693,4 +769,20 @@ public final class DownloadManager {
return Math.min((errorCount - 1) * 1000, 5000);
}
}
private class RequirementListener implements RequirementsWatcher.Listener {
@Override
public void requirementsMet(RequirementsWatcher requirementsWatcher) {
startDownloads();
notifyListenersRequirementsStateChange(0);
}
@Override
public void requirementsNotMet(
RequirementsWatcher requirementsWatcher,
@Requirements.RequirementFlags int notMetRequirements) {
stopDownloads();
notifyListenersRequirementsStateChange(notMetRequirements);
}
}
}
......@@ -155,16 +155,14 @@ public final class Requirements {
* @return Whether the requirements are met.
*/
public boolean checkRequirements(Context context) {
return checkNetworkRequirements(context)
&& checkChargingRequirement(context)
&& checkIdleRequirement(context);
return getNotMetRequirements(context) == 0;
}
/**
* Returns the requirement flags that are not met, or 0.
* Returns {@link RequirementFlags} that are not met, or 0.
*
* @param context Any context.
* @return The requirement flags that are not met, or 0.
* @return RequirementFlags that are not met, or 0.
*/
@RequirementFlags
public int getNotMetRequirements(Context context) {
......@@ -285,4 +283,20 @@ public final class Requirements {
+ (isIdleRequired() ? ",idle" : "")
+ '}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
return requirements == ((Requirements) o).requirements;
}
@Override
public int hashCode() {
return requirements;
}
}
......@@ -55,8 +55,12 @@ public final class RequirementsWatcher {
* requirements are not met.
*
* @param requirementsWatcher Calling instance.
* @param notMetRequirements {@link Requirements.RequirementFlags RequirementFlags} that are not
* met, or 0.
*/
void requirementsNotMet(RequirementsWatcher requirementsWatcher);
void requirementsNotMet(
RequirementsWatcher requirementsWatcher,
@Requirements.RequirementFlags int notMetRequirements);
}
private static final String TAG = "RequirementsWatcher";
......@@ -66,7 +70,7 @@ public final class RequirementsWatcher {
private final Requirements requirements;
private DeviceStatusChangeReceiver receiver;
private int notMetRequirements;
@Requirements.RequirementFlags private int notMetRequirements;
private CapabilityValidatedCallback networkCallback;
private Handler handler;
......@@ -85,8 +89,11 @@ public final class RequirementsWatcher {
/**
* Starts watching for changes. Must be called from a thread that has an associated {@link
* Looper}. Listener methods are called on the caller thread.
*
* @return Initial {@link Requirements.RequirementFlags RequirementFlags} that are not met, or 0.
*/
public void start() {
@Requirements.RequirementFlags
public int start() {
Assertions.checkNotNull(Looper.myLooper());
handler = new Handler();
......@@ -115,6 +122,7 @@ public final class RequirementsWatcher {
receiver = new DeviceStatusChangeReceiver();
context.registerReceiver(receiver, filter, null, handler);
logd(this + " started");
return notMetRequirements;
}
/** Stops watching for changes. */
......@@ -162,6 +170,7 @@ public final class RequirementsWatcher {
}
private void checkRequirements() {
@Requirements.RequirementFlags
int notMetRequirements = requirements.getNotMetRequirements(context);
if (this.notMetRequirements == notMetRequirements) {
logd("notMetRequirements hasn't changed: " + notMetRequirements);
......@@ -173,7 +182,7 @@ public final class RequirementsWatcher {
listener.requirementsMet(this);
} else {
logd("stop job");
listener.requirementsNotMet(this);
listener.requirementsNotMet(this, notMetRequirements);
}
}
......
......@@ -21,6 +21,7 @@ import static org.junit.Assert.fail;
import android.net.Uri;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.offline.DownloadState.State;
import com.google.android.exoplayer2.scheduler.Requirements;
import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.RobolectricUtil;
import com.google.android.exoplayer2.testutil.TestDownloadManagerListener;
......@@ -426,7 +427,12 @@ public class DownloadManagerTest {
() -> {
downloadManager =
new DownloadManager(
actionFile, downloaderFactory, maxActiveDownloadTasks, MIN_RETRY_COUNT);
RuntimeEnvironment.application,
actionFile,
downloaderFactory,
maxActiveDownloadTasks,
MIN_RETRY_COUNT,
new Requirements(0));
downloadManagerListener =
new TestDownloadManagerListener(downloadManager, dummyMainThread);
downloadManager.startDownloads();
......
......@@ -30,6 +30,7 @@ import com.google.android.exoplayer2.offline.DownloadAction;
import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.scheduler.Requirements;
import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.FakeDataSet;
import com.google.android.exoplayer2.testutil.FakeDataSource;
......@@ -241,11 +242,13 @@ public class DownloadManagerDashTest {
Factory fakeDataSourceFactory = new FakeDataSource.Factory().setFakeDataSet(fakeDataSet);
downloadManager =
new DownloadManager(
RuntimeEnvironment.application,
actionFile,
new DefaultDownloaderFactory(
new DownloaderConstructorHelper(cache, fakeDataSourceFactory)),
/* maxSimultaneousDownloads= */ 1,
/* minRetryCount= */ 3);
/* minRetryCount= */ 3,
new Requirements(0));
downloadManagerListener =
new TestDownloadManagerListener(downloadManager, dummyMainThread);
......
......@@ -30,6 +30,7 @@ import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloadService;
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.scheduler.Requirements;
import com.google.android.exoplayer2.scheduler.Scheduler;
import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.FakeDataSet;
......@@ -117,11 +118,13 @@ public class DownloadServiceDashTest {
actionFile.delete();
final DownloadManager dashDownloadManager =
new DownloadManager(
RuntimeEnvironment.application,
actionFile,
new DefaultDownloaderFactory(
new DownloaderConstructorHelper(cache, fakeDataSourceFactory)),
/* maxSimultaneousDownloads= */ 1,
/* minRetryCount= */ 3);
/* minRetryCount= */ 3,
new Requirements(0));
downloadManagerListener =
new TestDownloadManagerListener(dashDownloadManager, dummyMainThread);
dashDownloadManager.startDownloads();
......
......@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.os.ConditionVariable;
import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloadState;
import com.google.android.exoplayer2.scheduler.Requirements;
import java.util.HashMap;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
......@@ -82,6 +83,12 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen
}
}
@Override
public void onRequirementsStateChanged(
DownloadManager downloadManager, Requirements requirements, int notMetRequirements) {
// Do nothing.
}
/**
* Blocks until all remove and download tasks are complete and throws an exception if there was an
* error.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment