diff options
Diffstat (limited to 'src/com/android/providers/downloads/DownloadThread.java')
-rw-r--r-- | src/com/android/providers/downloads/DownloadThread.java | 710 |
1 files changed, 0 insertions, 710 deletions
diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java deleted file mode 100644 index 923e36d1..00000000 --- a/src/com/android/providers/downloads/DownloadThread.java +++ /dev/null @@ -1,710 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.providers.downloads; - -import org.apache.http.client.methods.AbortableHttpRequest; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.client.HttpClient; -import org.apache.http.entity.StringEntity; -import org.apache.http.Header; -import org.apache.http.HttpResponse; - -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.drm.mobile1.DrmRawContent; -import android.net.http.AndroidHttpClient; -import android.net.Uri; -import android.os.FileUtils; -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 java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URI; - -/** - * Runs an actual download - */ -public class DownloadThread extends Thread { - - private Context mContext; - private DownloadInfo mInfo; - - public DownloadThread(Context context, DownloadInfo info) { - mContext = context; - mInfo = info; - } - - /** - * Returns the user agent provided by the initiating app, or use the default one - */ - private String userAgent() { - String userAgent = mInfo.userAgent; - if (userAgent != null) { - } - if (userAgent == null) { - userAgent = Constants.DEFAULT_USER_AGENT; - } - return userAgent; - } - - /** - * Executes the download in a separate thread - */ - public void run() { - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - - int finalStatus = Downloads.STATUS_UNKNOWN_ERROR; - boolean countRetry = false; - int retryAfter = 0; - int redirectCount = mInfo.redirectCount; - String newUri = null; - boolean gotData = false; - String filename = null; - String mimeType = mInfo.mimetype; - FileOutputStream stream = null; - AndroidHttpClient client = null; - PowerManager.WakeLock wakeLock = null; - Uri contentUri = Uri.parse(Downloads.CONTENT_URI + "/" + mInfo.id); - - try { - boolean continuingDownload = false; - String headerAcceptRanges = null; - String headerContentDisposition = null; - String headerContentLength = null; - String headerContentLocation = null; - String headerETag = null; - String headerTransferEncoding = null; - - byte data[] = new byte[Constants.BUFFER_SIZE]; - - int bytesSoFar = 0; - - PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Constants.TAG); - wakeLock.acquire(); - - filename = mInfo.filename; - if (filename != null) { - if (!Helpers.isFilenameValid(filename)) { - finalStatus = Downloads.STATUS_FILE_ERROR; - notifyDownloadCompleted( - finalStatus, false, 0, 0, false, filename, null, mInfo.mimetype); - return; - } - // We're resuming a download that got interrupted - File f = new File(filename); - if (f.exists()) { - long fileLength = f.length(); - if (fileLength == 0) { - // The download hadn't actually started, we can restart from scratch - f.delete(); - filename = null; - } else if (mInfo.etag == null && !mInfo.noIntegrity) { - // Tough luck, that's not a resumable download - if (Config.LOGD) { - Log.d(Constants.TAG, - "can't resume interrupted non-resumable download"); - } - f.delete(); - finalStatus = Downloads.STATUS_PRECONDITION_FAILED; - notifyDownloadCompleted( - finalStatus, false, 0, 0, false, filename, null, mInfo.mimetype); - return; - } else { - // All right, we'll be able to resume this download - stream = new FileOutputStream(filename, true); - bytesSoFar = (int) fileLength; - if (mInfo.totalBytes != -1) { - headerContentLength = Integer.toString(mInfo.totalBytes); - } - headerETag = mInfo.etag; - continuingDownload = true; - } - } - } - - int bytesNotified = bytesSoFar; - // starting with MIN_VALUE means that the first write will commit - // progress to the database - long timeLastNotification = 0; - - client = AndroidHttpClient.newInstance(userAgent()); - - if (stream != null && mInfo.destination == Downloads.DESTINATION_EXTERNAL - && !DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING - .equalsIgnoreCase(mimeType)) { - try { - stream.close(); - stream = null; - } catch (IOException ex) { - if (Constants.LOGV) { - Log.v(Constants.TAG, "exception when closing the file before download : " + - ex); - } - // nothing can really be done if the file can't be closed - } - } - - /* - * This loop is run once for every individual HTTP request that gets sent. - * The very first HTTP request is a "virgin" request, while every subsequent - * request is done with the original ETag and a byte-range. - */ -http_request_loop: - while (true) { - // Prepares the request and fires it. - HttpGet request = new HttpGet(mInfo.uri); - - if (Constants.LOGV) { - Log.v(Constants.TAG, "initiating download for " + mInfo.uri); - } - - if (mInfo.cookies != null) { - request.addHeader("Cookie", mInfo.cookies); - } - if (mInfo.referer != null) { - request.addHeader("Referer", mInfo.referer); - } - if (continuingDownload) { - if (headerETag != null) { - request.addHeader("If-Match", headerETag); - } - request.addHeader("Range", "bytes=" + bytesSoFar + "-"); - } - - HttpResponse response; - try { - response = client.execute(request); - } catch (IllegalArgumentException ex) { - if (Constants.LOGV) { - Log.d(Constants.TAG, "Arg exception trying to execute request for " + - mInfo.uri + " : " + ex); - } else if (Config.LOGD) { - Log.d(Constants.TAG, "Arg exception trying to execute request for " + - mInfo.id + " : " + ex); - } - finalStatus = Downloads.STATUS_BAD_REQUEST; - request.abort(); - break http_request_loop; - } catch (IOException ex) { - if (!Helpers.isNetworkAvailable(mContext)) { - finalStatus = Downloads.STATUS_RUNNING_PAUSED; - } else if (mInfo.numFailed < Constants.MAX_RETRIES) { - finalStatus = Downloads.STATUS_RUNNING_PAUSED; - countRetry = true; - } else { - if (Constants.LOGV) { - Log.d(Constants.TAG, "IOException trying to execute request for " + - mInfo.uri + " : " + ex); - } else if (Config.LOGD) { - Log.d(Constants.TAG, "IOException trying to execute request for " + - mInfo.id + " : " + ex); - } - finalStatus = Downloads.STATUS_HTTP_DATA_ERROR; - } - request.abort(); - break http_request_loop; - } - - int statusCode = response.getStatusLine().getStatusCode(); - if (statusCode == 503 && mInfo.numFailed < Constants.MAX_RETRIES) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "got HTTP response code 503"); - } - finalStatus = Downloads.STATUS_RUNNING_PAUSED; - countRetry = true; - Header header = response.getFirstHeader("Retry-After"); - if (header != null) { - try { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "Retry-After :" + header.getValue()); - } - retryAfter = Integer.parseInt(header.getValue()); - if (retryAfter < 0) { - retryAfter = 0; - } else { - if (retryAfter < Constants.MIN_RETRY_AFTER) { - retryAfter = Constants.MIN_RETRY_AFTER; - } else if (retryAfter > Constants.MAX_RETRY_AFTER) { - retryAfter = Constants.MAX_RETRY_AFTER; - } - retryAfter += Helpers.rnd.nextInt(Constants.MIN_RETRY_AFTER + 1); - retryAfter *= 1000; - } - } catch (NumberFormatException ex) { - // ignored - retryAfter stays 0 in this case. - } - } - request.abort(); - break http_request_loop; - } - if (statusCode == 301 || - statusCode == 302 || - statusCode == 303 || - statusCode == 307) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "got HTTP redirect " + statusCode); - } - if (redirectCount >= Constants.MAX_REDIRECTS) { - if (Constants.LOGV) { - Log.d(Constants.TAG, "too many redirects for download " + mInfo.id + - " at " + mInfo.uri); - } else if (Config.LOGD) { - Log.d(Constants.TAG, "too many redirects for download " + mInfo.id); - } - finalStatus = Downloads.STATUS_TOO_MANY_REDIRECTS; - request.abort(); - break http_request_loop; - } - Header header = response.getFirstHeader("Location"); - if (header != null) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "Location :" + header.getValue()); - } - newUri = new URI(mInfo.uri).resolve(new URI(header.getValue())).toString(); - ++redirectCount; - finalStatus = Downloads.STATUS_RUNNING_PAUSED; - request.abort(); - break http_request_loop; - } - } - if ((!continuingDownload && statusCode != Downloads.STATUS_SUCCESS) - || (continuingDownload && statusCode != 206)) { - if (Constants.LOGV) { - Log.d(Constants.TAG, "http error " + statusCode + " for " + mInfo.uri); - } else if (Config.LOGD) { - Log.d(Constants.TAG, "http error " + statusCode + " for download " + - mInfo.id); - } - if (Downloads.isStatusError(statusCode)) { - finalStatus = statusCode; - } else if (statusCode >= 300 && statusCode < 400) { - finalStatus = Downloads.STATUS_UNHANDLED_REDIRECT; - } else if (continuingDownload && statusCode == Downloads.STATUS_SUCCESS) { - finalStatus = Downloads.STATUS_PRECONDITION_FAILED; - } else { - finalStatus = Downloads.STATUS_UNHANDLED_HTTP_CODE; - } - request.abort(); - break http_request_loop; - } else { - // Handles the response, saves the file - if (Constants.LOGV) { - Log.v(Constants.TAG, "received response for " + mInfo.uri); - } - - if (!continuingDownload) { - Header header = response.getFirstHeader("Accept-Ranges"); - if (header != null) { - headerAcceptRanges = header.getValue(); - } - header = response.getFirstHeader("Content-Disposition"); - if (header != null) { - headerContentDisposition = header.getValue(); - } - header = response.getFirstHeader("Content-Location"); - if (header != null) { - headerContentLocation = header.getValue(); - } - if (mimeType == null) { - header = response.getFirstHeader("Content-Type"); - if (header != null) { - mimeType = header.getValue(); - final int semicolonIndex = mimeType.indexOf(';'); - if (semicolonIndex != -1) { - mimeType = mimeType.substring(0, semicolonIndex); - } - } - } - header = response.getFirstHeader("ETag"); - if (header != null) { - headerETag = header.getValue(); - } - header = response.getFirstHeader("Transfer-Encoding"); - if (header != null) { - headerTransferEncoding = header.getValue(); - } - if (headerTransferEncoding == null) { - header = response.getFirstHeader("Content-Length"); - if (header != null) { - headerContentLength = header.getValue(); - } - } else { - // Ignore content-length with transfer-encoding - 2616 4.4 3 - if (Constants.LOGVV) { - Log.v(Constants.TAG, - "ignoring content-length because of xfer-encoding"); - } - } - if (Constants.LOGVV) { - Log.v(Constants.TAG, "Accept-Ranges: " + headerAcceptRanges); - Log.v(Constants.TAG, "Content-Disposition: " + - headerContentDisposition); - Log.v(Constants.TAG, "Content-Length: " + headerContentLength); - Log.v(Constants.TAG, "Content-Location: " + headerContentLocation); - Log.v(Constants.TAG, "Content-Type: " + mimeType); - Log.v(Constants.TAG, "ETag: " + headerETag); - Log.v(Constants.TAG, "Transfer-Encoding: " + headerTransferEncoding); - } - - if (!mInfo.noIntegrity && headerContentLength == null && - (headerTransferEncoding == null - || !headerTransferEncoding.equalsIgnoreCase("chunked")) - ) { - if (Config.LOGD) { - Log.d(Constants.TAG, "can't know size of download, giving up"); - } - finalStatus = Downloads.STATUS_LENGTH_REQUIRED; - request.abort(); - break http_request_loop; - } - - DownloadFileInfo fileInfo = Helpers.generateSaveFile( - mContext, - mInfo.uri, - mInfo.hint, - headerContentDisposition, - headerContentLocation, - mimeType, - mInfo.destination, - (headerContentLength != null) ? - Integer.parseInt(headerContentLength) : 0); - if (fileInfo.filename == null) { - finalStatus = fileInfo.status; - request.abort(); - break http_request_loop; - } - filename = fileInfo.filename; - stream = fileInfo.stream; - if (Constants.LOGV) { - Log.v(Constants.TAG, "writing " + mInfo.uri + " to " + filename); - } - - ContentValues values = new ContentValues(); - values.put(Downloads._DATA, filename); - if (headerETag != null) { - values.put(Constants.ETAG, headerETag); - } - if (mimeType != null) { - values.put(Downloads.MIMETYPE, mimeType); - } - int contentLength = -1; - if (headerContentLength != null) { - contentLength = Integer.parseInt(headerContentLength); - } - values.put(Downloads.TOTAL_BYTES, contentLength); - mContext.getContentResolver().update(contentUri, values, null, null); - } - - InputStream entityStream; - try { - entityStream = response.getEntity().getContent(); - } catch (IOException ex) { - if (!Helpers.isNetworkAvailable(mContext)) { - finalStatus = Downloads.STATUS_RUNNING_PAUSED; - } else if (mInfo.numFailed < Constants.MAX_RETRIES) { - finalStatus = Downloads.STATUS_RUNNING_PAUSED; - countRetry = true; - } else { - if (Constants.LOGV) { - Log.d(Constants.TAG, "IOException getting entity for " + mInfo.uri + - " : " + ex); - } else if (Config.LOGD) { - Log.d(Constants.TAG, "IOException getting entity for download " + - mInfo.id + " : " + ex); - } - finalStatus = Downloads.STATUS_HTTP_DATA_ERROR; - } - request.abort(); - break http_request_loop; - } - for (;;) { - int bytesRead; - try { - bytesRead = entityStream.read(data); - } catch (IOException ex) { - ContentValues values = new ContentValues(); - values.put(Downloads.CURRENT_BYTES, bytesSoFar); - mContext.getContentResolver().update(contentUri, values, null, null); - if (!mInfo.noIntegrity && headerETag == null) { - if (Constants.LOGV) { - Log.v(Constants.TAG, "download IOException for " + mInfo.uri + - " : " + ex); - } else if (Config.LOGD) { - Log.d(Constants.TAG, "download IOException for download " + - mInfo.id + " : " + ex); - } - if (Config.LOGD) { - Log.d(Constants.TAG, - "can't resume interrupted download with no ETag"); - } - finalStatus = Downloads.STATUS_PRECONDITION_FAILED; - } else if (!Helpers.isNetworkAvailable(mContext)) { - finalStatus = Downloads.STATUS_RUNNING_PAUSED; - } else if (mInfo.numFailed < Constants.MAX_RETRIES) { - finalStatus = Downloads.STATUS_RUNNING_PAUSED; - countRetry = true; - } else { - if (Constants.LOGV) { - Log.v(Constants.TAG, "download IOException for " + mInfo.uri + - " : " + ex); - } else if (Config.LOGD) { - Log.d(Constants.TAG, "download IOException for download " + - mInfo.id + " : " + ex); - } - finalStatus = Downloads.STATUS_HTTP_DATA_ERROR; - } - request.abort(); - break http_request_loop; - } - if (bytesRead == -1) { // success - ContentValues values = new ContentValues(); - values.put(Downloads.CURRENT_BYTES, bytesSoFar); - if (headerContentLength == null) { - values.put(Downloads.TOTAL_BYTES, bytesSoFar); - } - mContext.getContentResolver().update(contentUri, values, null, null); - if ((headerContentLength != null) - && (bytesSoFar - != Integer.parseInt(headerContentLength))) { - if (!mInfo.noIntegrity && headerETag == null) { - if (Constants.LOGV) { - Log.d(Constants.TAG, "mismatched content length " + - mInfo.uri); - } else if (Config.LOGD) { - Log.d(Constants.TAG, "mismatched content length for " + - mInfo.id); - } - finalStatus = Downloads.STATUS_LENGTH_REQUIRED; - } else if (!Helpers.isNetworkAvailable(mContext)) { - finalStatus = Downloads.STATUS_RUNNING_PAUSED; - } else if (mInfo.numFailed < Constants.MAX_RETRIES) { - finalStatus = Downloads.STATUS_RUNNING_PAUSED; - countRetry = true; - } else { - if (Constants.LOGV) { - Log.v(Constants.TAG, "closed socket for " + mInfo.uri); - } else if (Config.LOGD) { - Log.d(Constants.TAG, "closed socket for download " + - mInfo.id); - } - finalStatus = Downloads.STATUS_HTTP_DATA_ERROR; - } - break http_request_loop; - } - break; - } - gotData = true; - for (;;) { - try { - if (stream == null) { - stream = new FileOutputStream(filename, true); - } - stream.write(data, 0, bytesRead); - if (mInfo.destination == Downloads.DESTINATION_EXTERNAL - && !DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING - .equalsIgnoreCase(mimeType)) { - try { - stream.close(); - stream = null; - } catch (IOException ex) { - if (Constants.LOGV) { - Log.v(Constants.TAG, - "exception when closing the file " + - "during download : " + ex); - } - // nothing can really be done if the file can't be closed - } - } - break; - } catch (IOException ex) { - if (!Helpers.discardPurgeableFiles( - mContext, Constants.BUFFER_SIZE)) { - finalStatus = Downloads.STATUS_FILE_ERROR; - break http_request_loop; - } - } - } - bytesSoFar += bytesRead; - long now = System.currentTimeMillis(); - if (bytesSoFar - bytesNotified > Constants.MIN_PROGRESS_STEP - && now - timeLastNotification - > Constants.MIN_PROGRESS_TIME) { - ContentValues values = new ContentValues(); - values.put(Downloads.CURRENT_BYTES, bytesSoFar); - mContext.getContentResolver().update( - contentUri, values, null, null); - bytesNotified = bytesSoFar; - timeLastNotification = now; - } - - if (Constants.LOGVV) { - Log.v(Constants.TAG, "downloaded " + bytesSoFar + " for " + mInfo.uri); - } - synchronized(mInfo) { - if (mInfo.control == Downloads.CONTROL_PAUSED) { - if (Constants.LOGV) { - Log.v(Constants.TAG, "paused " + mInfo.uri); - } - finalStatus = Downloads.STATUS_RUNNING_PAUSED; - request.abort(); - break http_request_loop; - } - } - if (mInfo.status == Downloads.STATUS_CANCELED) { - if (Constants.LOGV) { - Log.d(Constants.TAG, "canceled " + mInfo.uri); - } else if (Config.LOGD) { - // Log.d(Constants.TAG, "canceled id " + mInfo.id); - } - finalStatus = Downloads.STATUS_CANCELED; - break http_request_loop; - } - } - if (Constants.LOGV) { - Log.v(Constants.TAG, "download completed for " + mInfo.uri); - } - finalStatus = Downloads.STATUS_SUCCESS; - } - break; - } - } catch (FileNotFoundException ex) { - if (Config.LOGD) { - Log.d(Constants.TAG, "FileNotFoundException for " + filename + " : " + ex); - } - finalStatus = Downloads.STATUS_FILE_ERROR; - // falls through to the code that reports an error - } catch (Exception ex) { //sometimes the socket code throws unchecked exceptions - if (Constants.LOGV) { - Log.d(Constants.TAG, "Exception for " + mInfo.uri, ex); - } else if (Config.LOGD) { - Log.d(Constants.TAG, "Exception for id " + mInfo.id, ex); - } - finalStatus = Downloads.STATUS_UNKNOWN_ERROR; - // falls through to the code that reports an error - } finally { - mInfo.hasActiveThread = false; - if (wakeLock != null) { - wakeLock.release(); - wakeLock = null; - } - if (client != null) { - client.close(); - client = null; - } - try { - // close the file - if (stream != null) { - stream.close(); - } - } catch (IOException ex) { - if (Constants.LOGV) { - Log.v(Constants.TAG, "exception when closing the file after download : " + ex); - } - // nothing can really be done if the file can't be closed - } - if (filename != null) { - // if the download wasn't successful, delete the file - if (Downloads.isStatusError(finalStatus)) { - new File(filename).delete(); - filename = null; - } else if (Downloads.isStatusSuccess(finalStatus) && - DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING - .equalsIgnoreCase(mimeType)) { - // transfer the file to the DRM content provider - File file = new File(filename); - Intent item = DrmStore.addDrmFile(mContext.getContentResolver(), file, null); - if (item == null) { - Log.w(Constants.TAG, "unable to add file " + filename + " to DrmProvider"); - finalStatus = Downloads.STATUS_UNKNOWN_ERROR; - } else { - filename = item.getDataString(); - mimeType = item.getType(); - } - - file.delete(); - } else if (Downloads.isStatusSuccess(finalStatus)) { - // make sure the file is readable - FileUtils.setPermissions(filename, 0644, -1, -1); - } - } - notifyDownloadCompleted(finalStatus, countRetry, retryAfter, redirectCount, - gotData, filename, newUri, mimeType); - } - } - - /** - * Stores information about the completed download, and notifies the initiating application. - */ - private void notifyDownloadCompleted( - int status, boolean countRetry, int retryAfter, int redirectCount, boolean gotData, - String filename, String uri, String mimeType) { - notifyThroughDatabase( - status, countRetry, retryAfter, redirectCount, gotData, filename, uri, mimeType); - if (Downloads.isStatusCompleted(status)) { - notifyThroughIntent(); - } - } - - private void notifyThroughDatabase( - int status, boolean countRetry, int retryAfter, int redirectCount, boolean gotData, - String filename, String uri, String mimeType) { - ContentValues values = new ContentValues(); - values.put(Downloads.STATUS, status); - values.put(Downloads._DATA, filename); - if (uri != null) { - values.put(Downloads.URI, uri); - } - values.put(Downloads.MIMETYPE, mimeType); - values.put(Downloads.LAST_MODIFICATION, System.currentTimeMillis()); - values.put(Constants.RETRY_AFTER___REDIRECT_COUNT, retryAfter + (redirectCount << 28)); - if (!countRetry) { - values.put(Constants.FAILED_CONNECTIONS, 0); - } else if (gotData) { - values.put(Constants.FAILED_CONNECTIONS, 1); - } else { - values.put(Constants.FAILED_CONNECTIONS, mInfo.numFailed + 1); - } - - mContext.getContentResolver().update( - ContentUris.withAppendedId(Downloads.CONTENT_URI, mInfo.id), values, null, null); - } - - /** - * Notifies the initiating app if it requested it. That way, it can know that the - * download completed even if it's not actively watching the cursor. - */ - private void notifyThroughIntent() { - Uri uri = Uri.parse(Downloads.CONTENT_URI + "/" + mInfo.id); - mInfo.sendIntentIfRequested(uri, mContext); - } - -} |