summaryrefslogtreecommitdiffstats
path: root/src/com/android/providers/downloads/DownloadThread.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/providers/downloads/DownloadThread.java')
-rw-r--r--src/com/android/providers/downloads/DownloadThread.java195
1 files changed, 84 insertions, 111 deletions
diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java
index 861ea949..95e9e961 100644
--- a/src/com/android/providers/downloads/DownloadThread.java
+++ b/src/com/android/providers/downloads/DownloadThread.java
@@ -30,7 +30,6 @@ import android.os.PowerManager;
import android.os.Process;
import android.provider.Downloads;
import android.provider.DrmStore;
-import android.util.Config;
import android.util.Log;
import android.util.Pair;
@@ -115,16 +114,21 @@ public class DownloadThread extends Thread {
/**
* Raised from methods called by run() to indicate that the current request should be stopped
* immediately.
+ *
+ * Note the message passed to this exception will be logged and therefore must be guaranteed
+ * not to contain any PII, meaning it generally can't include any information about the request
+ * URI, headers, or destination filename.
*/
private class StopRequest extends Throwable {
public int mFinalStatus;
- public StopRequest(int finalStatus) {
+ public StopRequest(int finalStatus, String message) {
+ super(message);
mFinalStatus = finalStatus;
}
- public StopRequest(int finalStatus, Throwable throwable) {
- super(throwable);
+ public StopRequest(int finalStatus, String message, Throwable throwable) {
+ super(message, throwable);
mFinalStatus = finalStatus;
}
}
@@ -184,15 +188,12 @@ public class DownloadThread extends Thread {
finalStatus = Downloads.Impl.STATUS_SUCCESS;
} catch (StopRequest error) {
// remove the cause before printing, in case it contains PII
- Log.w(Constants.TAG, "Aborting request for download " + mInfo.mId, removeCause(error));
+ Log.w(Constants.TAG,
+ "Aborting request for download " + mInfo.mId + ": " + error.getMessage());
finalStatus = error.mFinalStatus;
// fall through to finally block
- } catch (FileNotFoundException ex) {
- Log.w(Constants.TAG, "FileNotFoundException for " + state.mFilename, ex);
- finalStatus = Downloads.Impl.STATUS_FILE_ERROR;
- // falls through to the code that reports an error
} catch (Throwable ex) { //sometimes the socket code throws unchecked exceptions
- Log.w(Constants.TAG, "Exception for id " + mInfo.mId, ex);
+ Log.w(Constants.TAG, "Exception for id " + mInfo.mId + ": " + ex);
finalStatus = Downloads.Impl.STATUS_UNKNOWN_ERROR;
// falls through to the code that reports an error
} finally {
@@ -213,20 +214,11 @@ public class DownloadThread extends Thread {
}
/**
- * @return an identical StopRequest but with the cause removed.
- */
- private StopRequest removeCause(StopRequest error) {
- StopRequest newException = new StopRequest(error.mFinalStatus);
- newException.setStackTrace(error.getStackTrace());
- return newException;
- }
-
- /**
* Fully execute a single download request - setup and send the request, handle the response,
* and transfer the data to the destination file.
*/
private void executeDownload(State state, AndroidHttpClient client, HttpGet request)
- throws StopRequest, RetryDownload, FileNotFoundException {
+ throws StopRequest, RetryDownload {
InnerState innerState = new InnerState();
byte data[] = new byte[Constants.BUFFER_SIZE];
@@ -262,7 +254,7 @@ public class DownloadThread extends Thread {
status = Downloads.Impl.STATUS_QUEUED_FOR_WIFI;
mInfo.notifyPauseDueToSize(false);
}
- throw new StopRequest(status);
+ throw new StopRequest(status, mInfo.getLogMessageForNetworkError(networkUsable));
}
}
@@ -364,8 +356,8 @@ public class DownloadThread extends Thread {
file.delete();
if (item == null) {
- Log.w(Constants.TAG, "unable to add file " + state.mFilename + " to DrmProvider");
- throw new StopRequest(Downloads.Impl.STATUS_UNKNOWN_ERROR);
+ throw new StopRequest(Downloads.Impl.STATUS_UNKNOWN_ERROR,
+ "unable to add file to DrmProvider");
} else {
state.mFilename = item.getDataString();
state.mMimeType = item.getType();
@@ -397,17 +389,12 @@ public class DownloadThread extends Thread {
private void checkPausedOrCanceled(State state) throws StopRequest {
synchronized (mInfo) {
if (mInfo.mControl == Downloads.Impl.CONTROL_PAUSED) {
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "paused " + mInfo.mUri);
- }
- throw new StopRequest(Downloads.Impl.STATUS_PAUSED_BY_APP);
+ throw new StopRequest(Downloads.Impl.STATUS_PAUSED_BY_APP,
+ "download paused by owner");
}
}
if (mInfo.mStatus == Downloads.Impl.STATUS_CANCELED) {
- if (Constants.LOGV) {
- Log.d(Constants.TAG, "canceled " + mInfo.mUri);
- }
- throw new StopRequest(Downloads.Impl.STATUS_CANCELED);
+ throw new StopRequest(Downloads.Impl.STATUS_CANCELED, "download canceled");
}
}
@@ -452,15 +439,18 @@ public class DownloadThread extends Thread {
continue;
}
} else if (!Helpers.isExternalMediaMounted()) {
- throw new StopRequest(Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR);
+ throw new StopRequest(Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR,
+ "external media not mounted while writing destination file");
}
long availableBytes =
Helpers.getAvailableBytes(Helpers.getFilesystemRoot(state.mFilename));
if (availableBytes < bytesRead) {
- throw new StopRequest(Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR, ex);
+ throw new StopRequest(Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR,
+ "insufficient space while writing destination file", ex);
}
- throw new StopRequest(Downloads.Impl.STATUS_FILE_ERROR, ex);
+ throw new StopRequest(Downloads.Impl.STATUS_FILE_ERROR,
+ "while writing destination file: " + ex.toString(), ex);
}
}
}
@@ -481,16 +471,11 @@ public class DownloadThread extends Thread {
&& (innerState.mBytesSoFar != Integer.parseInt(innerState.mHeaderContentLength));
if (lengthMismatched) {
if (cannotResume(innerState)) {
- if (Constants.LOGV) {
- Log.d(Constants.TAG, "mismatched content length " +
- mInfo.mUri);
- } else if (Config.LOGD) {
- Log.d(Constants.TAG, "mismatched content length for " +
- mInfo.mId);
- }
- throw new StopRequest(Downloads.Impl.STATUS_CANNOT_RESUME);
+ throw new StopRequest(Downloads.Impl.STATUS_CANNOT_RESUME,
+ "mismatched content length");
} else {
- throw new StopRequest(handleHttpError(state, "closed socket"));
+ throw new StopRequest(getFinalStatusForHttpError(state),
+ "closed socket before end of file");
}
}
}
@@ -515,11 +500,13 @@ public class DownloadThread extends Thread {
values.put(Downloads.Impl.COLUMN_CURRENT_BYTES, innerState.mBytesSoFar);
mContext.getContentResolver().update(mInfo.getAllDownloadsUri(), values, null, null);
if (cannotResume(innerState)) {
- Log.d(Constants.TAG, "download IOException for download " + mInfo.mId, ex);
- Log.d(Constants.TAG, "can't resume interrupted download with no ETag");
- throw new StopRequest(Downloads.Impl.STATUS_CANNOT_RESUME, ex);
+ String message = "while reading response: " + ex.toString()
+ + ", can't resume interrupted download with no ETag";
+ throw new StopRequest(Downloads.Impl.STATUS_CANNOT_RESUME,
+ message, ex);
} else {
- throw new StopRequest(handleHttpError(state, "download IOException"), ex);
+ throw new StopRequest(getFinalStatusForHttpError(state),
+ "while reading response: " + ex.toString(), ex);
}
}
}
@@ -534,7 +521,8 @@ public class DownloadThread extends Thread {
return response.getEntity().getContent();
} catch (IOException ex) {
logNetworkState();
- throw new StopRequest(handleHttpError(state, "IOException getting entity"), ex);
+ throw new StopRequest(getFinalStatusForHttpError(state),
+ "while getting entity: " + ex.toString(), ex);
}
}
@@ -550,7 +538,7 @@ public class DownloadThread extends Thread {
* file and updating the database.
*/
private void processResponseHeaders(State state, InnerState innerState, HttpResponse response)
- throws StopRequest, FileNotFoundException {
+ throws StopRequest {
if (innerState.mContinuingDownload) {
// ignore response headers on resume requests
return;
@@ -558,22 +546,27 @@ public class DownloadThread extends Thread {
readResponseHeaders(state, innerState, response);
- DownloadFileInfo fileInfo = Helpers.generateSaveFile(
- mContext,
- mInfo.mUri,
- mInfo.mHint,
- innerState.mHeaderContentDisposition,
- innerState.mHeaderContentLocation,
- state.mMimeType,
- mInfo.mDestination,
- (innerState.mHeaderContentLength != null) ?
- Long.parseLong(innerState.mHeaderContentLength) : 0,
- mInfo.mIsPublicApi);
- if (fileInfo.mFileName == null) {
- throw new StopRequest(fileInfo.mStatus);
- }
- state.mFilename = fileInfo.mFileName;
- state.mStream = fileInfo.mStream;
+ try {
+ state.mFilename = Helpers.generateSaveFile(
+ mContext,
+ mInfo.mUri,
+ mInfo.mHint,
+ innerState.mHeaderContentDisposition,
+ innerState.mHeaderContentLocation,
+ state.mMimeType,
+ mInfo.mDestination,
+ (innerState.mHeaderContentLength != null) ?
+ Long.parseLong(innerState.mHeaderContentLength) : 0,
+ mInfo.mIsPublicApi);
+ } catch (Helpers.GenerateSaveFileError exc) {
+ throw new StopRequest(exc.mStatus, exc.mMessage);
+ }
+ try {
+ state.mStream = new FileOutputStream(state.mFilename);
+ } catch (FileNotFoundException exc) {
+ throw new StopRequest(Downloads.Impl.STATUS_FILE_ERROR,
+ "while opening destination file: " + exc.toString(), exc);
+ }
if (Constants.LOGV) {
Log.v(Constants.TAG, "writing " + mInfo.mUri + " to " + state.mFilename);
}
@@ -655,8 +648,8 @@ public class DownloadThread extends Thread {
&& (headerTransferEncoding == null
|| !headerTransferEncoding.equalsIgnoreCase("chunked"));
if (!mInfo.mNoIntegrity && noSizeInfo) {
- Log.d(Constants.TAG, "can't know size of download, giving up");
- throw new StopRequest(Downloads.Impl.STATUS_HTTP_DATA_ERROR);
+ throw new StopRequest(Downloads.Impl.STATUS_HTTP_DATA_ERROR,
+ "can't know size of download, giving up");
}
}
@@ -684,12 +677,6 @@ public class DownloadThread extends Thread {
*/
private void handleOtherStatus(State state, InnerState innerState, int statusCode)
throws StopRequest {
- if (Constants.LOGV) {
- Log.d(Constants.TAG, "http error " + statusCode + " for " + mInfo.mUri);
- } else if (Config.LOGD) {
- Log.d(Constants.TAG, "http error " + statusCode + " for download " +
- mInfo.mId);
- }
int finalStatus;
if (Downloads.Impl.isStatusError(statusCode)) {
finalStatus = statusCode;
@@ -700,7 +687,7 @@ public class DownloadThread extends Thread {
} else {
finalStatus = Downloads.Impl.STATUS_UNHANDLED_HTTP_CODE;
}
- throw new StopRequest(finalStatus);
+ throw new StopRequest(finalStatus, "http error " + statusCode);
}
/**
@@ -712,13 +699,7 @@ public class DownloadThread extends Thread {
Log.v(Constants.TAG, "got HTTP redirect " + statusCode);
}
if (state.mRedirectCount >= Constants.MAX_REDIRECTS) {
- if (Constants.LOGV) {
- Log.d(Constants.TAG, "too many redirects for download " + mInfo.mId +
- " at " + mInfo.mUri);
- } else if (Config.LOGD) {
- Log.d(Constants.TAG, "too many redirects for download " + mInfo.mId);
- }
- throw new StopRequest(Downloads.Impl.STATUS_TOO_MANY_REDIRECTS);
+ throw new StopRequest(Downloads.Impl.STATUS_TOO_MANY_REDIRECTS, "too many redirects");
}
Header header = response.getFirstHeader("Location");
if (header == null) {
@@ -735,12 +716,9 @@ public class DownloadThread extends Thread {
if (Constants.LOGV) {
Log.d(Constants.TAG, "Couldn't resolve redirect URI " + header.getValue()
+ " for " + mInfo.mUri);
- } else if (Config.LOGD) {
- Log.d(Constants.TAG,
- "Couldn't resolve redirect URI for download " +
- mInfo.mId);
}
- throw new StopRequest(Downloads.Impl.STATUS_HTTP_DATA_ERROR);
+ throw new StopRequest(Downloads.Impl.STATUS_HTTP_DATA_ERROR,
+ "Couldn't resolve redirect URI");
}
++state.mRedirectCount;
state.mRequestUri = newUri;
@@ -781,7 +759,8 @@ public class DownloadThread extends Thread {
// ignored - retryAfter stays 0 in this case.
}
}
- throw new StopRequest(Downloads.Impl.STATUS_WAITING_TO_RETRY);
+ throw new StopRequest(Downloads.Impl.STATUS_WAITING_TO_RETRY,
+ "got 503 Service Unavailable, will retry later");
}
/**
@@ -792,36 +771,23 @@ public class DownloadThread extends Thread {
try {
return client.execute(request);
} catch (IllegalArgumentException ex) {
- if (Constants.LOGV) {
- Log.d(Constants.TAG, "Arg exception trying to execute request for " +
- mInfo.mUri + " : " + ex);
- } else if (Config.LOGD) {
- Log.d(Constants.TAG, "Arg exception trying to execute request for " +
- mInfo.mId + " : " + ex);
- }
- throw new StopRequest(Downloads.Impl.STATUS_HTTP_DATA_ERROR, ex);
+ throw new StopRequest(Downloads.Impl.STATUS_HTTP_DATA_ERROR,
+ "while trying to execute request: " + ex.toString(), ex);
} catch (IOException ex) {
logNetworkState();
- throw new StopRequest(handleHttpError(state, "IOException trying to execute request"),
- ex);
+ throw new StopRequest(getFinalStatusForHttpError(state),
+ "while trying to execute request: " + ex.toString(), ex);
}
}
- /**
- * @return the final status for this attempt
- */
- private int handleHttpError(State state, String message) {
- if (Constants.LOGV) {
- Log.d(Constants.TAG, message + " for " + mInfo.mUri);
- }
-
+ private int getFinalStatusForHttpError(State state) {
if (!Helpers.isNetworkAvailable(mSystemFacade)) {
return Downloads.Impl.STATUS_WAITING_FOR_NETWORK;
} else if (mInfo.mNumFailed < Constants.MAX_RETRIES) {
state.mCountRetry = true;
return Downloads.Impl.STATUS_WAITING_TO_RETRY;
} else {
- Log.d(Constants.TAG, "reached max retries: " + message + " for " + mInfo.mId);
+ Log.w(Constants.TAG, "reached max retries for " + mInfo.mId);
return Downloads.Impl.STATUS_HTTP_DATA_ERROR;
}
}
@@ -831,10 +797,12 @@ public class DownloadThread extends Thread {
* appropriately for resumption.
*/
private void setupDestinationFile(State state, InnerState innerState)
- throws StopRequest, FileNotFoundException {
+ throws StopRequest {
if (state.mFilename != null) { // only true if we've already run a thread for this download
if (!Helpers.isFilenameValid(state.mFilename)) {
- throw new StopRequest(Downloads.Impl.STATUS_FILE_ERROR);
+ // this should never happen
+ throw new StopRequest(Downloads.Impl.STATUS_FILE_ERROR,
+ "found invalid internal destination filename");
}
// We're resuming a download that got interrupted
File f = new File(state.mFilename);
@@ -846,12 +814,17 @@ public class DownloadThread extends Thread {
state.mFilename = null;
} else if (mInfo.mETag == null && !mInfo.mNoIntegrity) {
// This should've been caught upon failure
- Log.wtf(Constants.TAG, "Trying to resume a download that can't be resumed");
f.delete();
- throw new StopRequest(Downloads.Impl.STATUS_CANNOT_RESUME);
+ throw new StopRequest(Downloads.Impl.STATUS_CANNOT_RESUME,
+ "Trying to resume a download that can't be resumed");
} else {
// All right, we'll be able to resume this download
- state.mStream = new FileOutputStream(state.mFilename, true);
+ try {
+ state.mStream = new FileOutputStream(state.mFilename, true);
+ } catch (FileNotFoundException exc) {
+ throw new StopRequest(Downloads.Impl.STATUS_FILE_ERROR,
+ "while opening destination for resuming: " + exc.toString(), exc);
+ }
innerState.mBytesSoFar = (int) fileLength;
if (mInfo.mTotalBytes != -1) {
innerState.mHeaderContentLength = Long.toString(mInfo.mTotalBytes);