package com.android.providers.transfers; import static com.android.providers.transfers.Constants.TAG; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import android.content.Context; import android.os.FileUtils; import android.text.TextUtils; import android.util.Log; import android.provider.Downloads; import com.android.providers.transfers.Helpers; import com.android.providers.transfers.TransferThread; public class DownloadThread extends TransferThread { public DownloadThread(Context context, SystemFacade systemFacade, TransferInfo info, StorageManager storageManager, TransferNotifier notifier) { super(context, systemFacade, info, storageManager, notifier); } @Override public void runInternal() { } @Override public void executeTransfer(State state) throws StopRequestException { } @Override public void transferData(State state, HttpURLConnection conn) throws StopRequestException { } @Override public void transferData(State state, InputStream in, OutputStream out) throws StopRequestException { } /** * Called after a successful completion to take any necessary action on the transfered file. */ private void finalizeDestinationFile(State state) { if (state.mFilename != null) { // make sure the file is readable FileUtils.setPermissions(state.mFilename, 0644, -1, -1); } } /** * Called just before the thread finishes, regardless of status, to take any necessary action on * the transfered file. */ private void cleanupDestination(State state, int finalStatus) { if (state.mFilename != null && Downloads.Impl.isStatusError(finalStatus)) { if (Constants.LOGVV) { Log.d(TAG, "cleanupDestination() deleting " + state.mFilename); } new File(state.mFilename).delete(); state.mFilename = null; } } /** * Write a data buffer to the destination file. * @param data buffer containing the data to write * @param bytesRead how many bytes to write from the buffer */ private void writeDataToDestination(State state, byte[] data, int bytesRead, OutputStream out) throws StopRequestException { mStorageManager.verifySpaceBeforeWritingToFile( mInfo.mDestination, state.mFilename, bytesRead); boolean forceVerified = false; while (true) { try { out.write(data, 0, bytesRead); return; } catch (IOException ex) { // TODO: better differentiate between DRM and disk failures if (!forceVerified) { // couldn't write to file. are we out of space? check. mStorageManager.verifySpace(mInfo.mDestination, state.mFilename, bytesRead); forceVerified = true; } else { throw new StopRequestException(Downloads.Impl.STATUS_FILE_ERROR, "Failed to write data: " + ex); } } } } /** * Prepare the destination file to receive data. If the file already exists, we'll set up * appropriately for resumption. */ private void setupDestinationFile(State state) throws StopRequestException { if (!TextUtils.isEmpty(state.mFilename)) { // only true if we've already run a thread for this transfer if (Constants.LOGV) { Log.i(Constants.TAG, "have run thread before for id: " + mInfo.mId + ", and state.mFilename: " + state.mFilename); } if (!Helpers.isFilenameValid(state.mFilename, mStorageManager.getTransferDataDirectory())) { // this should never happen throw new StopRequestException(Downloads.Impl.STATUS_FILE_ERROR, "found invalid internal destination filename"); } // We're resuming a transfer that got interrupted File f = new File(state.mFilename); if (f.exists()) { if (Constants.LOGV) { Log.i(Constants.TAG, "resuming transfer for id: " + mInfo.mId + ", and state.mFilename: " + state.mFilename); } long fileLength = f.length(); if (fileLength == 0) { // The transfer hadn't actually started, we can restart from scratch if (Constants.LOGVV) { Log.d(TAG, "setupDestinationFile() found fileLength=0, deleting " + state.mFilename); } f.delete(); state.mFilename = null; if (Constants.LOGV) { Log.i(Constants.TAG, "resuming transfer for id: " + mInfo.mId + ", BUT starting from scratch again: "); } } else if (mInfo.mETag == null && !mInfo.mNoIntegrity) { // This should've been caught upon failure if (Constants.LOGVV) { Log.d(TAG, "setupDestinationFile() unable to resume transfer, deleting " + state.mFilename); } f.delete(); throw new StopRequestException(Downloads.Impl.STATUS_CANNOT_RESUME, "Trying to resume a transfer that can't be resumed"); } else { // All right, we'll be able to resume this transfer if (Constants.LOGV) { Log.i(Constants.TAG, "resuming transfer for id: " + mInfo.mId + ", and starting with file of length: " + fileLength); } state.mCurrentBytes = (int) fileLength; if (mInfo.mTotalBytes != -1) { state.mContentLength = mInfo.mTotalBytes; } state.mHeaderETag = mInfo.mETag; state.mContinuingTransfer = true; if (Constants.LOGV) { Log.i(Constants.TAG, "resuming transfer for id: " + mInfo.mId + ", state.mCurrentBytes: " + state.mCurrentBytes + ", and setting mContinuingTransfer to true: "); } } } } } }