Commit 56520b7c by olly Committed by Oliver Woodman

Move DownloadManager internal logic into isolated inner class

There are no logic changes here. It's just moving code around and removing
the "internal" part of names where no longer required.

PiperOrigin-RevId: 245407238
parent d187d9ec
......@@ -160,38 +160,21 @@ public final class DownloadManager {
private final Context context;
private final WritableDownloadIndex downloadIndex;
private final DownloaderFactory downloaderFactory;
private final Handler mainHandler;
private final HandlerThread internalThread;
private final Handler internalHandler;
private final InternalHandler internalHandler;
private final RequirementsWatcher.Listener requirementsListener;
private final Object releaseLock;
// Collections that are accessed on the main thread.
private final CopyOnWriteArraySet<Listener> listeners;
private final ArrayList<Download> downloads;
// Collections that are accessed on the internal thread.
private final ArrayList<DownloadInternal> downloadInternals;
private final HashMap<String, DownloadThread> downloadThreads;
// Mutable fields that are accessed on the main thread.
private int pendingMessages;
private int activeDownloadCount;
private boolean initialized;
private boolean released;
private boolean downloadsPaused;
private int maxParallelDownloads;
private int minRetryCount;
private RequirementsWatcher requirementsWatcher;
// Mutable fields that are accessed on the internal thread.
@Requirements.RequirementFlags private int notMetRequirements;
private boolean downloadsPausedInternal;
private int maxParallelDownloadsInternal;
private int minRetryCountInternal;
private int parallelDownloads;
/**
* Constructs a {@link DownloadManager}.
*
......@@ -221,31 +204,29 @@ public final class DownloadManager {
Context context, WritableDownloadIndex downloadIndex, DownloaderFactory downloaderFactory) {
this.context = context.getApplicationContext();
this.downloadIndex = downloadIndex;
this.downloaderFactory = downloaderFactory;
maxParallelDownloads = DEFAULT_MAX_PARALLEL_DOWNLOADS;
maxParallelDownloadsInternal = DEFAULT_MAX_PARALLEL_DOWNLOADS;
minRetryCount = DEFAULT_MIN_RETRY_COUNT;
minRetryCountInternal = DEFAULT_MIN_RETRY_COUNT;
downloadsPaused = true;
downloadsPausedInternal = true;
downloadInternals = new ArrayList<>();
downloads = new ArrayList<>();
downloadThreads = new HashMap<>();
listeners = new CopyOnWriteArraySet<>();
releaseLock = new Object();
requirementsListener = this::onRequirementsStateChanged;
mainHandler = new Handler(Util.getLooper(), this::handleMainMessage);
internalThread = new HandlerThread("DownloadManager file i/o");
internalThread.start();
internalHandler = new Handler(internalThread.getLooper(), this::handleInternalMessage);
requirementsWatcher =
new RequirementsWatcher(context, requirementsListener, DEFAULT_REQUIREMENTS);
int notMetRequirements = requirementsWatcher.start();
mainHandler = new Handler(Util.getLooper(), this::handleMainMessage);
HandlerThread internalThread = new HandlerThread("DownloadManager file i/o");
internalThread.start();
internalHandler =
new InternalHandler(
internalThread,
downloadIndex,
downloaderFactory,
mainHandler,
maxParallelDownloads,
minRetryCount,
downloadsPaused);
pendingMessages = 1;
internalHandler
.obtainMessage(MSG_INITIALIZE, notMetRequirements, /* unused */ 0)
......@@ -464,15 +445,15 @@ public final class DownloadManager {
* download index. The manager must not be accessed after this method has been called.
*/
public void release() {
synchronized (releaseLock) {
if (released) {
synchronized (internalHandler) {
if (internalHandler.released) {
return;
}
internalHandler.sendEmptyMessage(MSG_RELEASE);
boolean wasInterrupted = false;
while (!released) {
while (!internalHandler.released) {
try {
releaseLock.wait();
internalHandler.wait();
} catch (InterruptedException e) {
wasInterrupted = true;
}
......@@ -581,383 +562,411 @@ public final class DownloadManager {
return C.INDEX_UNSET;
}
// Internal thread message handling.
private boolean handleInternalMessage(Message message) {
boolean processedExternalMessage = true;
switch (message.what) {
case MSG_INITIALIZE:
int notMetRequirements = message.arg1;
initializeInternal(notMetRequirements);
break;
case MSG_SET_DOWNLOADS_PAUSED:
boolean downloadsPaused = message.arg1 != 0;
setDownloadsPausedInternal(downloadsPaused);
break;
case MSG_SET_NOT_MET_REQUIREMENTS:
notMetRequirements = message.arg1;
setNotMetRequirementsInternal(notMetRequirements);
break;
case MSG_SET_STOP_REASON:
String id = (String) message.obj;
int stopReason = message.arg1;
setStopReasonInternal(id, stopReason);
break;
case MSG_SET_MAX_PARALLEL_DOWNLOADS:
int maxParallelDownloads = message.arg1;
setMaxParallelDownloadsInternal(maxParallelDownloads);
break;
case MSG_SET_MIN_RETRY_COUNT:
int minRetryCount = message.arg1;
setMinRetryCountInternal(minRetryCount);
break;
case MSG_ADD_DOWNLOAD:
DownloadRequest request = (DownloadRequest) message.obj;
stopReason = message.arg1;
addDownloadInternal(request, stopReason);
break;
case MSG_REMOVE_DOWNLOAD:
id = (String) message.obj;
removeDownloadInternal(id);
break;
case MSG_DOWNLOAD_THREAD_STOPPED:
DownloadThread downloadThread = (DownloadThread) message.obj;
onDownloadThreadStoppedInternal(downloadThread);
processedExternalMessage = false; // This message is posted internally.
break;
case MSG_CONTENT_LENGTH_CHANGED:
downloadThread = (DownloadThread) message.obj;
onDownloadThreadContentLengthChangedInternal(downloadThread);
processedExternalMessage = false; // This message is posted internally.
break;
case MSG_RELEASE:
releaseInternal();
return true; // Don't post back to mainHandler on release.
default:
throw new IllegalStateException();
/* package */ static Download mergeRequest(
Download download, DownloadRequest request, int stopReason) {
@Download.State int state = download.state;
if (state == STATE_REMOVING || state == STATE_RESTARTING) {
state = STATE_RESTARTING;
} else if (stopReason != STOP_REASON_NONE) {
state = STATE_STOPPED;
} else {
state = STATE_QUEUED;
}
mainHandler
.obtainMessage(MSG_PROCESSED, processedExternalMessage ? 1 : 0, downloadThreads.size())
.sendToTarget();
return true;
long nowMs = System.currentTimeMillis();
long startTimeMs = download.isTerminalState() ? nowMs : download.startTimeMs;
return new Download(
download.request.copyWithMergedRequest(request),
state,
startTimeMs,
/* updateTimeMs= */ nowMs,
/* contentLength= */ C.LENGTH_UNSET,
stopReason,
FAILURE_REASON_NONE);
}
private void initializeInternal(int notMetRequirements) {
this.notMetRequirements = notMetRequirements;
ArrayList<Download> loadedStates = new ArrayList<>();
try (DownloadCursor cursor =
downloadIndex.getDownloads(
STATE_QUEUED, STATE_STOPPED, STATE_DOWNLOADING, STATE_REMOVING, STATE_RESTARTING)) {
while (cursor.moveToNext()) {
loadedStates.add(cursor.getDownload());
}
logd("Downloads are loaded.");
} catch (Throwable e) {
Log.e(TAG, "Download state loading failed.", e);
loadedStates.clear();
}
for (Download download : loadedStates) {
addDownloadForState(download);
}
logd("Downloads are created.");
mainHandler.obtainMessage(MSG_INITIALIZED, loadedStates).sendToTarget();
for (int i = 0; i < downloadInternals.size(); i++) {
downloadInternals.get(i).start();
private static Download copyWithState(Download download, @Download.State int state) {
return new Download(
download.request,
state,
download.startTimeMs,
/* updateTimeMs= */ System.currentTimeMillis(),
download.contentLength,
download.stopReason,
FAILURE_REASON_NONE,
download.progress);
}
private static void logd(String message) {
if (DEBUG) {
Log.d(TAG, message);
}
}
private void setDownloadsPausedInternal(boolean downloadsPaused) {
if (this.downloadsPausedInternal == downloadsPaused) {
return;
private static void logd(String message, DownloadInternal downloadInternal) {
logd(message, downloadInternal.download.request);
}
private static void logd(String message, DownloadRequest request) {
if (DEBUG) {
logd(message + ": " + request);
}
this.downloadsPausedInternal = downloadsPaused;
for (int i = 0; i < downloadInternals.size(); i++) {
downloadInternals.get(i).updateStopState();
}
private static void logdFlags(String message, int flags) {
if (DEBUG) {
logd(message + ": " + Integer.toBinaryString(flags));
}
}
private void setNotMetRequirementsInternal(
@Requirements.RequirementFlags int notMetRequirements) {
if (this.notMetRequirements == notMetRequirements) {
return;
private static final class InternalHandler extends Handler {
public boolean released;
private final HandlerThread thread;
private final WritableDownloadIndex downloadIndex;
private final DownloaderFactory downloaderFactory;
private final Handler mainHandler;
private final ArrayList<DownloadInternal> downloadInternals;
private final HashMap<String, DownloadThread> downloadThreads;
// Mutable fields that are accessed on the internal thread.
@Requirements.RequirementFlags private int notMetRequirements;
private boolean downloadsPaused;
private int maxParallelDownloads;
private int minRetryCount;
private int parallelDownloads;
public InternalHandler(
HandlerThread thread,
WritableDownloadIndex downloadIndex,
DownloaderFactory downloaderFactory,
Handler mainHandler,
int maxParallelDownloads,
int minRetryCount,
boolean downloadsPaused) {
super(thread.getLooper());
this.thread = thread;
this.downloadIndex = downloadIndex;
this.downloaderFactory = downloaderFactory;
this.mainHandler = mainHandler;
this.maxParallelDownloads = maxParallelDownloads;
this.minRetryCount = minRetryCount;
this.downloadsPaused = downloadsPaused;
downloadInternals = new ArrayList<>();
downloadThreads = new HashMap<>();
}
this.notMetRequirements = notMetRequirements;
logdFlags("Not met requirements are changed", notMetRequirements);
for (int i = 0; i < downloadInternals.size(); i++) {
downloadInternals.get(i).updateStopState();
@Override
public void handleMessage(Message message) {
boolean processedExternalMessage = true;
switch (message.what) {
case MSG_INITIALIZE:
int notMetRequirements = message.arg1;
initialize(notMetRequirements);
break;
case MSG_SET_DOWNLOADS_PAUSED:
boolean downloadsPaused = message.arg1 != 0;
setDownloadsPaused(downloadsPaused);
break;
case MSG_SET_NOT_MET_REQUIREMENTS:
notMetRequirements = message.arg1;
setNotMetRequirements(notMetRequirements);
break;
case MSG_SET_STOP_REASON:
String id = (String) message.obj;
int stopReason = message.arg1;
setStopReason(id, stopReason);
break;
case MSG_SET_MAX_PARALLEL_DOWNLOADS:
int maxParallelDownloads = message.arg1;
setMaxParallelDownloads(maxParallelDownloads);
break;
case MSG_SET_MIN_RETRY_COUNT:
int minRetryCount = message.arg1;
setMinRetryCount(minRetryCount);
break;
case MSG_ADD_DOWNLOAD:
DownloadRequest request = (DownloadRequest) message.obj;
stopReason = message.arg1;
addDownload(request, stopReason);
break;
case MSG_REMOVE_DOWNLOAD:
id = (String) message.obj;
removeDownload(id);
break;
case MSG_DOWNLOAD_THREAD_STOPPED:
DownloadThread downloadThread = (DownloadThread) message.obj;
onDownloadThreadStopped(downloadThread);
processedExternalMessage = false; // This message is posted internally.
break;
case MSG_CONTENT_LENGTH_CHANGED:
downloadThread = (DownloadThread) message.obj;
onDownloadThreadContentLengthChanged(downloadThread);
processedExternalMessage = false; // This message is posted internally.
break;
case MSG_RELEASE:
release();
return; // Don't post back to mainHandler on release.
default:
throw new IllegalStateException();
}
mainHandler
.obtainMessage(MSG_PROCESSED, processedExternalMessage ? 1 : 0, downloadThreads.size())
.sendToTarget();
}
private void initialize(int notMetRequirements) {
this.notMetRequirements = notMetRequirements;
ArrayList<Download> loadedStates = new ArrayList<>();
try (DownloadCursor cursor =
downloadIndex.getDownloads(
STATE_QUEUED, STATE_STOPPED, STATE_DOWNLOADING, STATE_REMOVING, STATE_RESTARTING)) {
while (cursor.moveToNext()) {
loadedStates.add(cursor.getDownload());
}
logd("Downloads are loaded.");
} catch (Throwable e) {
Log.e(TAG, "Download state loading failed.", e);
loadedStates.clear();
}
for (Download download : loadedStates) {
addDownloadForState(download);
}
logd("Downloads are created.");
mainHandler.obtainMessage(MSG_INITIALIZED, loadedStates).sendToTarget();
for (int i = 0; i < downloadInternals.size(); i++) {
downloadInternals.get(i).start();
}
}
}
private void setStopReasonInternal(@Nullable String id, int stopReason) {
if (id != null) {
DownloadInternal downloadInternal = getDownload(id);
if (downloadInternal != null) {
logd("download stop reason is set to : " + stopReason, downloadInternal);
downloadInternal.setStopReason(stopReason);
private void setDownloadsPaused(boolean downloadsPaused) {
this.downloadsPaused = downloadsPaused;
for (int i = 0; i < downloadInternals.size(); i++) {
downloadInternals.get(i).updateStopState();
}
}
private void setNotMetRequirements(@Requirements.RequirementFlags int notMetRequirements) {
// TODO: Move this deduplication check to the main thread.
if (this.notMetRequirements == notMetRequirements) {
return;
}
} else {
this.notMetRequirements = notMetRequirements;
logdFlags("Not met requirements are changed", notMetRequirements);
for (int i = 0; i < downloadInternals.size(); i++) {
downloadInternals.get(i).setStopReason(stopReason);
downloadInternals.get(i).updateStopState();
}
}
try {
private void setStopReason(@Nullable String id, int stopReason) {
if (id != null) {
downloadIndex.setStopReason(id, stopReason);
DownloadInternal downloadInternal = getDownload(id);
if (downloadInternal != null) {
logd("download stop reason is set to : " + stopReason, downloadInternal);
downloadInternal.setStopReason(stopReason);
return;
}
} else {
downloadIndex.setStopReason(stopReason);
for (int i = 0; i < downloadInternals.size(); i++) {
downloadInternals.get(i).setStopReason(stopReason);
}
}
try {
if (id != null) {
downloadIndex.setStopReason(id, stopReason);
} else {
downloadIndex.setStopReason(stopReason);
}
} catch (IOException e) {
Log.e(TAG, "setStopReason failed", e);
}
} catch (IOException e) {
Log.e(TAG, "setStopReason failed", e);
}
}
private void setMaxParallelDownloadsInternal(int maxParallelDownloads) {
maxParallelDownloadsInternal = maxParallelDownloads;
// TODO: Start or stop downloads if necessary.
}
private void setMaxParallelDownloads(int maxParallelDownloads) {
this.maxParallelDownloads = maxParallelDownloads;
// TODO: Start or stop downloads if necessary.
}
private void setMinRetryCountInternal(int minRetryCount) {
minRetryCountInternal = minRetryCount;
}
private void setMinRetryCount(int minRetryCount) {
this.minRetryCount = minRetryCount;
}
private void addDownloadInternal(DownloadRequest request, int stopReason) {
DownloadInternal downloadInternal = getDownload(request.id);
if (downloadInternal != null) {
downloadInternal.addRequest(request, stopReason);
logd("Request is added to existing download", downloadInternal);
} else {
Download download = loadDownload(request.id);
if (download == null) {
long nowMs = System.currentTimeMillis();
download =
new Download(
request,
stopReason != Download.STOP_REASON_NONE ? STATE_STOPPED : STATE_QUEUED,
/* startTimeMs= */ nowMs,
/* updateTimeMs= */ nowMs,
/* contentLength= */ C.LENGTH_UNSET,
stopReason,
Download.FAILURE_REASON_NONE);
logd("Download state is created for " + request.id);
private void addDownload(DownloadRequest request, int stopReason) {
DownloadInternal downloadInternal = getDownload(request.id);
if (downloadInternal != null) {
downloadInternal.addRequest(request, stopReason);
logd("Request is added to existing download", downloadInternal);
} else {
download = mergeRequest(download, request, stopReason);
logd("Download state is loaded for " + request.id);
Download download = loadDownload(request.id);
if (download == null) {
long nowMs = System.currentTimeMillis();
download =
new Download(
request,
stopReason != Download.STOP_REASON_NONE ? STATE_STOPPED : STATE_QUEUED,
/* startTimeMs= */ nowMs,
/* updateTimeMs= */ nowMs,
/* contentLength= */ C.LENGTH_UNSET,
stopReason,
Download.FAILURE_REASON_NONE);
logd("Download state is created for " + request.id);
} else {
download = mergeRequest(download, request, stopReason);
logd("Download state is loaded for " + request.id);
}
addDownloadForState(download);
}
addDownloadForState(download);
}
}
private void removeDownloadInternal(String id) {
DownloadInternal downloadInternal = getDownload(id);
if (downloadInternal != null) {
downloadInternal.remove();
} else {
Download download = loadDownload(id);
if (download != null) {
addDownloadForState(copyWithState(download, STATE_REMOVING));
private void removeDownload(String id) {
DownloadInternal downloadInternal = getDownload(id);
if (downloadInternal != null) {
downloadInternal.remove();
} else {
logd("Can't remove download. No download with id: " + id);
Download download = loadDownload(id);
if (download != null) {
addDownloadForState(copyWithState(download, STATE_REMOVING));
} else {
logd("Can't remove download. No download with id: " + id);
}
}
}
}
private void onDownloadThreadStoppedInternal(DownloadThread downloadThread) {
logd("Download is stopped", downloadThread.request);
String downloadId = downloadThread.request.id;
downloadThreads.remove(downloadId);
boolean tryToStartDownloads = false;
if (!downloadThread.isRemove) {
// If maxParallelDownloads was hit, there might be a download waiting for a slot.
tryToStartDownloads = parallelDownloads == maxParallelDownloadsInternal;
parallelDownloads--;
}
getDownload(downloadId)
.onDownloadThreadStopped(downloadThread.isCanceled, downloadThread.finalError);
if (tryToStartDownloads) {
for (int i = 0;
parallelDownloads < maxParallelDownloadsInternal && i < downloadInternals.size();
i++) {
downloadInternals.get(i).start();
private void onDownloadThreadStopped(DownloadThread downloadThread) {
logd("Download is stopped", downloadThread.request);
String downloadId = downloadThread.request.id;
downloadThreads.remove(downloadId);
boolean tryToStartDownloads = false;
if (!downloadThread.isRemove) {
// If maxParallelDownloads was hit, there might be a download waiting for a slot.
tryToStartDownloads = parallelDownloads == maxParallelDownloads;
parallelDownloads--;
}
getDownload(downloadId)
.onDownloadThreadStopped(downloadThread.isCanceled, downloadThread.finalError);
if (tryToStartDownloads) {
for (int i = 0;
parallelDownloads < maxParallelDownloads && i < downloadInternals.size();
i++) {
downloadInternals.get(i).start();
}
}
}
}
private void onDownloadThreadContentLengthChangedInternal(DownloadThread downloadThread) {
String downloadId = downloadThread.request.id;
getDownload(downloadId).setContentLength(downloadThread.contentLength);
}
private void releaseInternal() {
for (DownloadThread downloadThread : downloadThreads.values()) {
downloadThread.cancel(/* released= */ true);
}
downloadThreads.clear();
downloadInternals.clear();
internalThread.quit();
synchronized (releaseLock) {
released = true;
releaseLock.notifyAll();
}
}
private void onDownloadChangedInternal(DownloadInternal downloadInternal, Download download) {
logd("Download state is changed", downloadInternal);
try {
downloadIndex.putDownload(download);
} catch (IOException e) {
Log.e(TAG, "Failed to update index", e);
}
if (downloadInternal.state == STATE_COMPLETED || downloadInternal.state == STATE_FAILED) {
downloadInternals.remove(downloadInternal);
}
mainHandler.obtainMessage(MSG_DOWNLOAD_CHANGED, download).sendToTarget();
}
private void onDownloadRemovedInternal(DownloadInternal downloadInternal, Download download) {
logd("Download is removed", downloadInternal);
try {
downloadIndex.removeDownload(download.request.id);
} catch (IOException e) {
Log.e(TAG, "Failed to remove from index", e);
private void onDownloadThreadContentLengthChanged(DownloadThread downloadThread) {
String downloadId = downloadThread.request.id;
getDownload(downloadId).setContentLength(downloadThread.contentLength);
}
downloadInternals.remove(downloadInternal);
mainHandler.obtainMessage(MSG_DOWNLOAD_REMOVED, download).sendToTarget();
}
@StartThreadResults
private int startDownloadThread(DownloadInternal downloadInternal) {
DownloadRequest request = downloadInternal.download.request;
String downloadId = request.id;
if (downloadThreads.containsKey(downloadId)) {
if (stopDownloadThreadInternal(downloadId)) {
return START_THREAD_WAIT_DOWNLOAD_CANCELLATION;
private void release() {
for (DownloadThread downloadThread : downloadThreads.values()) {
downloadThread.cancel(/* released= */ true);
}
return START_THREAD_WAIT_REMOVAL_TO_FINISH;
}
boolean isRemove = downloadInternal.isInRemoveState();
if (!isRemove) {
if (parallelDownloads == maxParallelDownloadsInternal) {
return START_THREAD_TOO_MANY_DOWNLOADS;
downloadThreads.clear();
downloadInternals.clear();
thread.quit();
synchronized (this) {
released = true;
notifyAll();
}
parallelDownloads++;
}
Downloader downloader = downloaderFactory.createDownloader(request);
DownloadProgress downloadProgress = downloadInternal.download.progress;
DownloadThread downloadThread =
new DownloadThread(
request,
downloader,
downloadProgress,
isRemove,
minRetryCountInternal,
internalHandler);
downloadThreads.put(downloadId, downloadThread);
downloadThread.start();
logd("Download is started", downloadInternal);
return START_THREAD_SUCCEEDED;
}
private boolean stopDownloadThreadInternal(String downloadId) {
DownloadThread downloadThread = downloadThreads.get(downloadId);
if (downloadThread != null && !downloadThread.isRemove) {
downloadThread.cancel(/* released= */ false);
logd("Download is cancelled", downloadThread.request);
return true;
private void onDownloadChangedInternal(DownloadInternal downloadInternal, Download download) {
logd("Download state is changed", downloadInternal);
try {
downloadIndex.putDownload(download);
} catch (IOException e) {
Log.e(TAG, "Failed to update index", e);
}
if (downloadInternal.state == STATE_COMPLETED || downloadInternal.state == STATE_FAILED) {
downloadInternals.remove(downloadInternal);
}
mainHandler.obtainMessage(MSG_DOWNLOAD_CHANGED, download).sendToTarget();
}
return false;
}
@Nullable
private DownloadInternal getDownload(String id) {
for (int i = 0; i < downloadInternals.size(); i++) {
DownloadInternal downloadInternal = downloadInternals.get(i);
if (downloadInternal.download.request.id.equals(id)) {
return downloadInternal;
private void onDownloadRemovedInternal(DownloadInternal downloadInternal, Download download) {
logd("Download is removed", downloadInternal);
try {
downloadIndex.removeDownload(download.request.id);
} catch (IOException e) {
Log.e(TAG, "Failed to remove from index", e);
}
downloadInternals.remove(downloadInternal);
mainHandler.obtainMessage(MSG_DOWNLOAD_REMOVED, download).sendToTarget();
}
return null;
}
private Download loadDownload(String id) {
try {
return downloadIndex.getDownload(id);
} catch (IOException e) {
Log.e(TAG, "loadDownload failed", e);
@StartThreadResults
private int startDownloadThread(DownloadInternal downloadInternal) {
DownloadRequest request = downloadInternal.download.request;
String downloadId = request.id;
if (downloadThreads.containsKey(downloadId)) {
if (stopDownloadThreadInternal(downloadId)) {
return START_THREAD_WAIT_DOWNLOAD_CANCELLATION;
}
return START_THREAD_WAIT_REMOVAL_TO_FINISH;
}
boolean isRemove = downloadInternal.isInRemoveState();
if (!isRemove) {
if (parallelDownloads == maxParallelDownloads) {
return START_THREAD_TOO_MANY_DOWNLOADS;
}
parallelDownloads++;
}
Downloader downloader = downloaderFactory.createDownloader(request);
DownloadProgress downloadProgress = downloadInternal.download.progress;
DownloadThread downloadThread =
new DownloadThread(request, downloader, downloadProgress, isRemove, minRetryCount, this);
downloadThreads.put(downloadId, downloadThread);
downloadThread.start();
logd("Download is started", downloadInternal);
return START_THREAD_SUCCEEDED;
}
private boolean stopDownloadThreadInternal(String downloadId) {
DownloadThread downloadThread = downloadThreads.get(downloadId);
if (downloadThread != null && !downloadThread.isRemove) {
downloadThread.cancel(/* released= */ false);
logd("Download is cancelled", downloadThread.request);
return true;
}
return false;
}
return null;
}
private void addDownloadForState(Download download) {
DownloadInternal downloadInternal = new DownloadInternal(this, download);
downloadInternals.add(downloadInternal);
logd("Download is added", downloadInternal);
downloadInternal.initialize();
}
private boolean canStartDownloads() {
return !downloadsPausedInternal && notMetRequirements == 0;
}
/* package */ static Download mergeRequest(
Download download, DownloadRequest request, int stopReason) {
@Download.State int state = download.state;
if (state == STATE_REMOVING || state == STATE_RESTARTING) {
state = STATE_RESTARTING;
} else if (stopReason != STOP_REASON_NONE) {
state = STATE_STOPPED;
} else {
state = STATE_QUEUED;
@Nullable
private DownloadInternal getDownload(String id) {
for (int i = 0; i < downloadInternals.size(); i++) {
DownloadInternal downloadInternal = downloadInternals.get(i);
if (downloadInternal.download.request.id.equals(id)) {
return downloadInternal;
}
}
return null;
}
long nowMs = System.currentTimeMillis();
long startTimeMs = download.isTerminalState() ? nowMs : download.startTimeMs;
return new Download(
download.request.copyWithMergedRequest(request),
state,
startTimeMs,
/* updateTimeMs= */ nowMs,
/* contentLength= */ C.LENGTH_UNSET,
stopReason,
FAILURE_REASON_NONE);
}
private static Download copyWithState(Download download, @Download.State int state) {
return new Download(
download.request,
state,
download.startTimeMs,
/* updateTimeMs= */ System.currentTimeMillis(),
download.contentLength,
download.stopReason,
FAILURE_REASON_NONE,
download.progress);
}
private static void logd(String message) {
if (DEBUG) {
Log.d(TAG, message);
private Download loadDownload(String id) {
try {
return downloadIndex.getDownload(id);
} catch (IOException e) {
Log.e(TAG, "loadDownload failed", e);
}
return null;
}
}
private static void logd(String message, DownloadInternal downloadInternal) {
logd(message, downloadInternal.download.request);
}
private static void logd(String message, DownloadRequest request) {
if (DEBUG) {
logd(message + ": " + request);
private void addDownloadForState(Download download) {
DownloadInternal downloadInternal = new DownloadInternal(this, download);
downloadInternals.add(downloadInternal);
logd("Download is added", downloadInternal);
downloadInternal.initialize();
}
}
private static void logdFlags(String message, int flags) {
if (DEBUG) {
logd(message + ": " + Integer.toBinaryString(flags));
private boolean canStartDownloads() {
return !downloadsPaused && notMetRequirements == 0;
}
}
private static final class DownloadInternal {
private final DownloadManager downloadManager;
private final InternalHandler internalHandler;
private Download download;
......@@ -967,8 +976,8 @@ public final class DownloadManager {
private int stopReason;
@MonotonicNonNull @Download.FailureReason private int failureReason;
private DownloadInternal(DownloadManager downloadManager, Download download) {
this.downloadManager = downloadManager;
private DownloadInternal(InternalHandler internalHandler, Download download) {
this.internalHandler = internalHandler;
this.download = download;
state = download.state;
contentLength = download.contentLength;
......@@ -1016,7 +1025,7 @@ public final class DownloadManager {
if (state == STATE_QUEUED || state == STATE_DOWNLOADING) {
startOrQueue();
} else if (isInRemoveState()) {
downloadManager.startDownloadThread(this);
internalHandler.startDownloadThread(this);
}
}
......@@ -1034,7 +1043,7 @@ public final class DownloadManager {
return;
}
this.contentLength = contentLength;
downloadManager.onDownloadChangedInternal(this, getUpdatedDownload());
internalHandler.onDownloadChangedInternal(this, getUpdatedDownload());
}
private void updateStopState() {
......@@ -1045,12 +1054,12 @@ public final class DownloadManager {
}
} else {
if (state == STATE_DOWNLOADING || state == STATE_QUEUED) {
downloadManager.stopDownloadThreadInternal(download.request.id);
internalHandler.stopDownloadThreadInternal(download.request.id);
setState(STATE_STOPPED);
}
}
if (oldDownload == download) {
downloadManager.onDownloadChangedInternal(this, getUpdatedDownload());
internalHandler.onDownloadChangedInternal(this, getUpdatedDownload());
}
}
......@@ -1059,24 +1068,24 @@ public final class DownloadManager {
// state immediately.
state = initialState;
if (isInRemoveState()) {
downloadManager.startDownloadThread(this);
internalHandler.startDownloadThread(this);
} else if (canStart()) {
startOrQueue();
} else {
setState(STATE_STOPPED);
}
if (state == initialState) {
downloadManager.onDownloadChangedInternal(this, getUpdatedDownload());
internalHandler.onDownloadChangedInternal(this, getUpdatedDownload());
}
}
private boolean canStart() {
return downloadManager.canStartDownloads() && stopReason == STOP_REASON_NONE;
return internalHandler.canStartDownloads() && stopReason == STOP_REASON_NONE;
}
private void startOrQueue() {
Assertions.checkState(!isInRemoveState());
@StartThreadResults int result = downloadManager.startDownloadThread(this);
@StartThreadResults int result = internalHandler.startDownloadThread(this);
Assertions.checkState(result != START_THREAD_WAIT_REMOVAL_TO_FINISH);
if (result == START_THREAD_SUCCEEDED || result == START_THREAD_WAIT_DOWNLOAD_CANCELLATION) {
setState(STATE_DOWNLOADING);
......@@ -1088,7 +1097,7 @@ public final class DownloadManager {
private void setState(@Download.State int newState) {
if (state != newState) {
state = newState;
downloadManager.onDownloadChangedInternal(this, getUpdatedDownload());
internalHandler.onDownloadChangedInternal(this, getUpdatedDownload());
}
}
......@@ -1097,9 +1106,9 @@ public final class DownloadManager {
return;
}
if (isCanceled) {
downloadManager.startDownloadThread(this);
internalHandler.startDownloadThread(this);
} else if (state == STATE_REMOVING) {
downloadManager.onDownloadRemovedInternal(this, getUpdatedDownload());
internalHandler.onDownloadRemovedInternal(this, getUpdatedDownload());
} else if (state == STATE_RESTARTING) {
initialize(STATE_QUEUED);
} else { // STATE_DOWNLOADING
......@@ -1122,7 +1131,7 @@ public final class DownloadManager {
private final boolean isRemove;
private final int minRetryCount;
private volatile Handler updateHandler;
private volatile InternalHandler internalHandler;
private volatile boolean isCanceled;
private Throwable finalError;
......@@ -1134,13 +1143,13 @@ public final class DownloadManager {
DownloadProgress downloadProgress,
boolean isRemove,
int minRetryCount,
Handler updateHandler) {
InternalHandler internalHandler) {
this.request = request;
this.downloader = downloader;
this.downloadProgress = downloadProgress;
this.isRemove = isRemove;
this.minRetryCount = minRetryCount;
this.updateHandler = updateHandler;
this.internalHandler = internalHandler;
contentLength = C.LENGTH_UNSET;
}
......@@ -1150,7 +1159,7 @@ public final class DownloadManager {
// cancellation to complete depends on the implementation of the downloader being used. We
// null the handler reference here so that it doesn't prevent garbage collection of the
// download manager whilst cancellation is ongoing.
updateHandler = null;
internalHandler = null;
}
isCanceled = true;
downloader.cancel();
......@@ -1192,9 +1201,9 @@ public final class DownloadManager {
} catch (Throwable e) {
finalError = e;
}
Handler updateHandler = this.updateHandler;
if (updateHandler != null) {
updateHandler.obtainMessage(MSG_DOWNLOAD_THREAD_STOPPED, this).sendToTarget();
Handler internalHandler = this.internalHandler;
if (internalHandler != null) {
internalHandler.obtainMessage(MSG_DOWNLOAD_THREAD_STOPPED, this).sendToTarget();
}
}
......@@ -1204,9 +1213,9 @@ public final class DownloadManager {
downloadProgress.percentDownloaded = percentDownloaded;
if (contentLength != this.contentLength) {
this.contentLength = contentLength;
Handler updateHandler = this.updateHandler;
if (updateHandler != null) {
updateHandler.obtainMessage(MSG_CONTENT_LENGTH_CHANGED, this).sendToTarget();
Handler internalHandler = this.internalHandler;
if (internalHandler != null) {
internalHandler.obtainMessage(MSG_CONTENT_LENGTH_CHANGED, this).sendToTarget();
}
}
}
......
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