package us.shandian.giga.get; import android.support.annotation.NonNull; import android.util.Log; import java.io.File; import java.io.IOException; import java.io.InterruptedIOException; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.nio.channels.ClosedByInterruptException; import us.shandian.giga.util.Utility; import static org.schabi.newpipe.BuildConfig.DEBUG; public class DownloadInitializer extends Thread { private final static String TAG = "DownloadInitializer"; final static int mId = 0; private DownloadMission mMission; private HttpURLConnection mConn; DownloadInitializer(@NonNull DownloadMission mission) { mMission = mission; mConn = null; } @Override public void run() { if (mMission.current > 0) mMission.resetState(); int retryCount = 0; while (true) { try { mMission.currentThreadCount = mMission.threadCount; mConn = mMission.openConnection(mId, -1, -1); mMission.establishConnection(mId, mConn); if (!mMission.running || Thread.interrupted()) return; mMission.length = Utility.getContentLength(mConn); if (mMission.length == 0) { mMission.notifyError(DownloadMission.ERROR_HTTP_NO_CONTENT, null); return; } // check for dynamic generated content if (mMission.length == -1 && mConn.getResponseCode() == 200) { mMission.blocks = 0; mMission.length = 0; mMission.fallback = true; mMission.unknownLength = true; mMission.currentThreadCount = 1; if (DEBUG) { Log.d(TAG, "falling back (unknown length)"); } } else { // Open again mConn = mMission.openConnection(mId, mMission.length - 10, mMission.length); mMission.establishConnection(mId, mConn); if (!mMission.running || Thread.interrupted()) return; synchronized (mMission.blockState) { if (mConn.getResponseCode() == 206) { if (mMission.currentThreadCount > 1) { mMission.blocks = mMission.length / DownloadMission.BLOCK_SIZE; if (mMission.currentThreadCount > mMission.blocks) { mMission.currentThreadCount = (int) mMission.blocks; } if (mMission.currentThreadCount <= 0) { mMission.currentThreadCount = 1; } if (mMission.blocks * DownloadMission.BLOCK_SIZE < mMission.length) { mMission.blocks++; } } else { // if one thread is solicited don't calculate blocks, is useless mMission.blocks = 1; mMission.fallback = true; mMission.unknownLength = false; } if (DEBUG) { Log.d(TAG, "http response code = " + mConn.getResponseCode()); } } else { // Fallback to single thread mMission.blocks = 0; mMission.fallback = true; mMission.unknownLength = false; mMission.currentThreadCount = 1; if (DEBUG) { Log.d(TAG, "falling back due http response code = " + mConn.getResponseCode()); } } for (long i = 0; i < mMission.currentThreadCount; i++) { mMission.threadBlockPositions.add(i); mMission.threadBytePositions.add(0L); } } if (!mMission.running || Thread.interrupted()) return; } File file; if (mMission.current == 0) { file = new File(mMission.location); if (!Utility.mkdir(file, true)) { mMission.notifyError(DownloadMission.ERROR_PATH_CREATION, null); return; } file = new File(file, mMission.name); // if the name is used by another process, delete it if (file.exists() && !file.isFile() && !file.delete()) { mMission.notifyError(DownloadMission.ERROR_FILE_CREATION, null); return; } if (!file.exists() && !file.createNewFile()) { mMission.notifyError(DownloadMission.ERROR_FILE_CREATION, null); return; } } else { file = new File(mMission.location, mMission.name); } RandomAccessFile af = new RandomAccessFile(file, "rw"); af.setLength(mMission.offsets[mMission.current] + mMission.length); af.seek(mMission.offsets[mMission.current]); af.close(); if (!mMission.running || Thread.interrupted()) return; mMission.running = false; break; } catch (InterruptedIOException | ClosedByInterruptException e) { return; } catch (Exception e) { if (!mMission.running) return; if (e instanceof IOException && e.getMessage().contains("Permission denied")) { mMission.notifyError(DownloadMission.ERROR_PERMISSION_DENIED, e); return; } if (retryCount++ > mMission.maxRetry) { Log.e(TAG, "initializer failed", e); mMission.notifyError(e); return; } Log.e(TAG, "initializer failed, retrying", e); } } // hide marquee in the progress bar mMission.done++; mMission.start(); } @Override public void interrupt() { super.interrupt(); if (mConn != null) { try { mConn.disconnect(); } catch (Exception e) { // nothing to do } } } }