long-term downloads resume

* recovery infrastructure
* bump serialVersionUID of DownloadMission
* misc cleanup in DownloadMission.java
* remove unused/redundant from strings.xml
This commit is contained in:
kapodamy 2019-09-28 18:11:05 -03:00
parent dab53450c1
commit 86dafdd92b
42 changed files with 478 additions and 97 deletions

View file

@ -27,7 +27,7 @@ import us.shandian.giga.util.Utility;
import static org.schabi.newpipe.BuildConfig.DEBUG;
public class DownloadMission extends Mission {
private static final long serialVersionUID = 5L;// last bump: 30 june 2019
private static final long serialVersionUID = 6L;// last bump: 28 september 2019
static final int BUFFER_SIZE = 64 * 1024;
static final int BLOCK_SIZE = 512 * 1024;
@ -51,8 +51,9 @@ public class DownloadMission extends Mission {
public static final int ERROR_INSUFFICIENT_STORAGE = 1010;
public static final int ERROR_PROGRESS_LOST = 1011;
public static final int ERROR_TIMEOUT = 1012;
public static final int ERROR_RESOURCE_GONE = 1013;
public static final int ERROR_HTTP_NO_CONTENT = 204;
public static final int ERROR_HTTP_UNSUPPORTED_RANGE = 206;
static final int ERROR_HTTP_FORBIDDEN = 403;
/**
* The urls of the file to download
@ -125,6 +126,11 @@ public class DownloadMission extends Mission {
*/
public int threadCount = 3;
/**
* information required to recover a download
*/
public MissionRecoveryInfo[] recoveryInfo;
private transient int finishCount;
public transient boolean running;
public boolean enqueued;
@ -132,7 +138,6 @@ public class DownloadMission extends Mission {
public int errCode = ERROR_NOTHING;
public Exception errObject = null;
public transient boolean recovered;
public transient Handler mHandler;
private transient boolean mWritingToFile;
private transient boolean[] blockAcquired;
@ -197,9 +202,9 @@ public class DownloadMission extends Mission {
}
/**
* Open connection
* Opens a connection
*
* @param threadId id of the calling thread, used only for debug
* @param threadId id of the calling thread, used only for debugging
* @param rangeStart range start
* @param rangeEnd range end
* @return a {@link java.net.URLConnection URLConnection} linking to the URL.
@ -251,7 +256,7 @@ public class DownloadMission extends Mission {
case 204:
case 205:
case 207:
throw new HttpError(conn.getResponseCode());
throw new HttpError(statusCode);
case 416:
return;// let the download thread handle this error
default:
@ -270,10 +275,6 @@ public class DownloadMission extends Mission {
synchronized void notifyProgress(long deltaLen) {
if (!running) return;
if (recovered) {
recovered = false;
}
if (unknownLength) {
length += deltaLen;// Update length before proceeding
}
@ -344,7 +345,6 @@ public class DownloadMission extends Mission {
if (running) {
running = false;
recovered = true;
if (threads != null) selfPause();
}
}
@ -409,12 +409,13 @@ public class DownloadMission extends Mission {
* Start downloading with multiple threads.
*/
public void start() {
if (running || isFinished()) return;
if (running || isFinished() || urls.length < 1) return;
// ensure that the previous state is completely paused.
joinForThread(init);
int maxWait = 10000;// 10 seconds
joinForThread(init, maxWait);
if (threads != null) {
for (Thread thread : threads) joinForThread(thread);
for (Thread thread : threads) joinForThread(thread, maxWait);
threads = null;
}
@ -431,6 +432,11 @@ public class DownloadMission extends Mission {
return;
}
if (urls[current] == null) {
doRecover(null);
return;
}
if (blocks == null) {
initializer();
return;
@ -478,7 +484,6 @@ public class DownloadMission extends Mission {
}
running = false;
recovered = true;
if (init != null && init.isAlive()) {
// NOTE: if start() method is running ¡will no have effect!
@ -563,7 +568,7 @@ public class DownloadMission extends Mission {
* Write this {@link DownloadMission} to the meta file asynchronously
* if no thread is already running.
*/
private void writeThisToFile() {
void writeThisToFile() {
synchronized (LOCK) {
if (deleted) return;
Utility.writeToFile(metadata, DownloadMission.this);
@ -667,6 +672,7 @@ public class DownloadMission extends Mission {
* @return {@code true} is this mission its "healthy", otherwise, {@code false}
*/
public boolean isCorrupt() {
if (urls.length < 1) return false;
return (isPsFailed() || errCode == ERROR_POSTPROCESSING_HOLD) || isFinished();
}
@ -710,6 +716,48 @@ public class DownloadMission extends Mission {
return true;
}
/**
* Attempts to recover the download
*
* @param fromError exception which require update the url from the source
*/
void doRecover(Exception fromError) {
Log.i(TAG, "Attempting to recover the mission: " + storage.getName());
if (recoveryInfo == null) {
if (fromError == null)
notifyError(ERROR_RESOURCE_GONE, null);
else
notifyError(fromError);
urls = new String[0];// mark this mission as dead
return;
}
if (threads != null) {
for (Thread thread : threads) {
if (thread == Thread.currentThread()) continue;
thread.interrupt();
joinForThread(thread, 0);
}
}
// set the current download url to null in case if the recovery
// process is canceled. Next time start() method is called the
// recovery will be executed, saving time
urls[current] = null;
if (recoveryInfo[current].attempts >= maxRetry) {
recoveryInfo[current].attempts = 0;
notifyError(fromError);
return;
}
threads = new Thread[]{
runAsync(DownloadMissionRecover.mID, new DownloadMissionRecover(this, fromError))
};
}
private boolean deleteThisFromFile() {
synchronized (LOCK) {
return metadata.delete();
@ -749,7 +797,13 @@ public class DownloadMission extends Mission {
return who;
}
private void joinForThread(Thread thread) {
/**
* Waits at most {@code millis} milliseconds for the thread to die
*
* @param thread the desired thread
* @param millis the time to wait in milliseconds
*/
private void joinForThread(Thread thread, int millis) {
if (thread == null || !thread.isAlive()) return;
if (thread == Thread.currentThread()) return;
@ -764,7 +818,7 @@ public class DownloadMission extends Mission {
// start() method called quickly after pause()
try {
thread.join(10000);
thread.join(millis);
} catch (InterruptedException e) {
Log.d(TAG, "timeout on join : " + thread.getName());
throw new RuntimeException("A thread is still running:\n" + thread.getName());
@ -785,9 +839,9 @@ public class DownloadMission extends Mission {
}
}
static class Block {
int position;
int done;
public static class Block {
public int position;
public int done;
}
private static class Lock implements Serializable {