Merge "Import translations. DO NOT MERGE"
diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java
index 34bc8e3..cab1a3e 100644
--- a/src/com/android/providers/downloads/DownloadThread.java
+++ b/src/com/android/providers/downloads/DownloadThread.java
@@ -16,16 +16,18 @@
 
 package com.android.providers.downloads;
 
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static com.android.providers.downloads.Constants.TAG;
+import static java.net.HttpURLConnection.HTTP_OK;
+import static java.net.HttpURLConnection.HTTP_PARTIAL;
+import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
 
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.net.INetworkPolicyListener;
 import android.net.NetworkPolicyManager;
-import android.net.Proxy;
 import android.net.TrafficStats;
-import android.net.http.AndroidHttpClient;
 import android.os.FileUtils;
 import android.os.PowerManager;
 import android.os.Process;
@@ -35,25 +37,30 @@
 import android.util.Log;
 import android.util.Pair;
 
-import org.apache.http.Header;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.conn.params.ConnRouteParams;
-
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.SyncFailedException;
-import java.net.URI;
-import java.net.URISyntaxException;
+import java.net.CookieHandler;
+import java.net.CookieManager;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+
+import libcore.io.IoUtils;
 
 /**
- * Runs an actual download
+ * Thread which executes a given {@link DownloadInfo}: making network requests,
+ * persisting data to disk, and updating {@link DownloadProvider}.
  */
 public class DownloadThread extends Thread {
 
+    private static final int HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+
+    private static final int DEFAULT_TIMEOUT = (int) MINUTE_IN_MILLIS;
+
     private final Context mContext;
     private final DownloadInfo mInfo;
     private final SystemFacade mSystemFacade;
@@ -90,8 +97,6 @@
         public String mMimeType;
         public boolean mCountRetry = false;
         public int mRetryAfter = 0;
-        public int mRedirectCount = 0;
-        public String mNewUri;
         public boolean mGotData = false;
         public String mRequestUri;
         public long mTotalBytes = -1;
@@ -121,18 +126,12 @@
      * State within executeDownload()
      */
     private static class InnerState {
-        public String mHeaderContentLength;
-        public String mHeaderContentDisposition;
-        public String mHeaderContentLocation;
+        public long mContentLength;
+        public String mContentDisposition;
+        public String mContentLocation;
     }
 
     /**
-     * Raised from methods called by executeDownload() to indicate that the download should be
-     * retried immediately.
-     */
-    private class RetryDownload extends Throwable {}
-
-    /**
      * Executes the download in a separate thread
      */
     @Override
@@ -155,7 +154,6 @@
         }
 
         State state = new State(mInfo);
-        AndroidHttpClient client = null;
         PowerManager.WakeLock wakeLock = null;
         int finalStatus = Downloads.Impl.STATUS_UNKNOWN_ERROR;
         String errorMsg = null;
@@ -174,29 +172,24 @@
                 Log.v(Constants.TAG, "initiating download for " + mInfo.mUri);
             }
 
-            client = AndroidHttpClient.newInstance(userAgent(), mContext);
-
             // network traffic on this thread should be counted against the
             // requesting uid, and is tagged with well-known value.
             TrafficStats.setThreadStatsTag(TrafficStats.TAG_SYSTEM_DOWNLOAD);
             TrafficStats.setThreadStatsUid(mInfo.mUid);
 
             boolean finished = false;
-            while(!finished) {
+            while (!finished) {
                 Log.i(Constants.TAG, "Initiating request for download " + mInfo.mId);
-                // Set or unset proxy, which may have changed since last GET request.
-                // setDefaultProxy() supports null as proxy parameter.
-                ConnRouteParams.setDefaultProxy(client.getParams(),
-                        Proxy.getPreferredHttpHost(mContext, state.mRequestUri));
-                HttpGet request = new HttpGet(state.mRequestUri);
+
+                final URL url = new URL(state.mRequestUri);
+                final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+                conn.setConnectTimeout(DEFAULT_TIMEOUT);
+                conn.setReadTimeout(DEFAULT_TIMEOUT);
                 try {
-                    executeDownload(state, client, request);
+                    executeDownload(state, conn);
                     finished = true;
-                } catch (RetryDownload exc) {
-                    // fall through
                 } finally {
-                    request.abort();
-                    request = null;
+                    conn.disconnect();
                 }
             }
 
@@ -215,7 +208,7 @@
             }
             finalStatus = error.mFinalStatus;
             // fall through to finally block
-        } catch (Throwable ex) { //sometimes the socket code throws unchecked exceptions
+        } catch (Throwable ex) {
             errorMsg = ex.getMessage();
             String msg = "Exception for id " + mInfo.mId + ": " + errorMsg;
             Log.w(Constants.TAG, msg, ex);
@@ -225,14 +218,9 @@
             TrafficStats.clearThreadStatsTag();
             TrafficStats.clearThreadStatsUid();
 
-            if (client != null) {
-                client.close();
-                client = null;
-            }
             cleanupDestination(state, finalStatus);
             notifyDownloadCompleted(finalStatus, state.mCountRetry, state.mRetryAfter,
-                                    state.mGotData, state.mFilename,
-                                    state.mNewUri, state.mMimeType, errorMsg);
+                    state.mGotData, state.mFilename, state.mMimeType, errorMsg);
 
             netPolicy.unregisterListener(mPolicyListener);
 
@@ -248,13 +236,13 @@
      * 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 StopRequestException, RetryDownload {
-        InnerState innerState = new InnerState();
-        byte data[] = new byte[Constants.BUFFER_SIZE];
+    private void executeDownload(State state, HttpURLConnection conn)
+            throws IOException, StopRequestException {
+        final InnerState innerState = new InnerState();
+        final byte data[] = new byte[Constants.BUFFER_SIZE];
 
         setupDestinationFile(state, innerState);
-        addRequestHeaders(state, request);
+        addRequestHeaders(state, conn);
 
         // skip when already finished; remove after fixing race in 5217390
         if (state.mCurrentBytes == state.mTotalBytes) {
@@ -266,16 +254,14 @@
         // check just before sending the request to avoid using an invalid connection at all
         checkConnectivity();
 
-        HttpResponse response = sendRequest(state, client, request);
-        handleExceptionalStatus(state, innerState, response);
+        // Asking for response code will execute the request
+        final int statusCode = conn.getResponseCode();
 
-        if (Constants.LOGV) {
-            Log.v(Constants.TAG, "received response for " + mInfo.mUri);
-        }
+        handleExceptionalStatus(state, innerState, conn, statusCode);
+        processResponseHeaders(state, innerState, conn);
 
-        processResponseHeaders(state, innerState, response);
-        InputStream entityStream = openResponseEntity(state, response);
-        transferData(state, innerState, data, entityStream);
+        final InputStream in = conn.getInputStream();
+        transferData(state, innerState, data, in);
     }
 
     /**
@@ -332,7 +318,7 @@
     /**
      * Called after a successful completion to take any necessary action on the downloaded file.
      */
-    private void finalizeDestinationFile(State state) throws StopRequestException {
+    private void finalizeDestinationFile(State state) {
         if (state.mFilename != null) {
             // make sure the file is readable
             FileUtils.setPermissions(state.mFilename, 0644, -1, -1);
@@ -376,15 +362,7 @@
         } catch (RuntimeException ex) {
             Log.w(Constants.TAG, "exception while syncing file: ", ex);
         } finally {
-            if(downloadedFileStream != null) {
-                try {
-                    downloadedFileStream.close();
-                } catch (IOException ex) {
-                    Log.w(Constants.TAG, "IOException while closing synced file: ", ex);
-                } catch (RuntimeException ex) {
-                    Log.w(Constants.TAG, "exception while closing file: ", ex);
-                }
-            }
+            IoUtils.closeQuietly(downloadedFileStream);
         }
     }
 
@@ -392,18 +370,8 @@
      * Close the destination output stream.
      */
     private void closeDestination(State state) {
-        try {
-            // close the file
-            if (state.mStream != null) {
-                state.mStream.close();
-                state.mStream = null;
-            }
-        } 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
-        }
+        IoUtils.closeQuietly(state.mStream);
+        state.mStream = null;
     }
 
     /**
@@ -508,13 +476,13 @@
     private void handleEndOfStream(State state, InnerState innerState) throws StopRequestException {
         ContentValues values = new ContentValues();
         values.put(Downloads.Impl.COLUMN_CURRENT_BYTES, state.mCurrentBytes);
-        if (innerState.mHeaderContentLength == null) {
+        if (innerState.mContentLength == -1) {
             values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, state.mCurrentBytes);
         }
         mContext.getContentResolver().update(mInfo.getAllDownloadsUri(), values, null, null);
 
-        boolean lengthMismatched = (innerState.mHeaderContentLength != null)
-                && (state.mCurrentBytes != Integer.parseInt(innerState.mHeaderContentLength));
+        boolean lengthMismatched = (innerState.mContentLength != -1)
+                && (state.mCurrentBytes != innerState.mContentLength);
         if (lengthMismatched) {
             if (cannotResume(state)) {
                 throw new StopRequestException(Downloads.Impl.STATUS_CANNOT_RESUME,
@@ -541,7 +509,6 @@
         try {
             return entityStream.read(data);
         } catch (IOException ex) {
-            logNetworkState(mInfo.mUid);
             ContentValues values = new ContentValues();
             values.put(Downloads.Impl.COLUMN_CURRENT_BYTES, state.mCurrentBytes);
             mContext.getContentResolver().update(mInfo.getAllDownloadsUri(), values, null, null);
@@ -558,39 +525,17 @@
     }
 
     /**
-     * Open a stream for the HTTP response entity, handling I/O errors.
-     * @return an InputStream to read the response entity
-     */
-    private InputStream openResponseEntity(State state, HttpResponse response)
-            throws StopRequestException {
-        try {
-            return response.getEntity().getContent();
-        } catch (IOException ex) {
-            logNetworkState(mInfo.mUid);
-            throw new StopRequestException(getFinalStatusForHttpError(state),
-                    "while getting entity: " + ex.toString(), ex);
-        }
-    }
-
-    private void logNetworkState(int uid) {
-        if (Constants.LOGX) {
-            Log.i(Constants.TAG,
-                    "Net " + (Helpers.isNetworkAvailable(mSystemFacade, uid) ? "Up" : "Down"));
-        }
-    }
-
-    /**
      * Read HTTP response headers and take appropriate action, including setting up the destination
      * file and updating the database.
      */
-    private void processResponseHeaders(State state, InnerState innerState, HttpResponse response)
+    private void processResponseHeaders(State state, InnerState innerState, HttpURLConnection conn)
             throws StopRequestException {
         if (state.mContinuingDownload) {
             // ignore response headers on resume requests
             return;
         }
 
-        readResponseHeaders(state, innerState, response);
+        readResponseHeaders(state, innerState, conn);
         if (DownloadDrmHelper.isDrmConvertNeeded(state.mMimeType)) {
             mDrmConvertSession = DrmConvertSession.open(mContext, state.mMimeType);
             if (mDrmConvertSession == null) {
@@ -603,12 +548,11 @@
                 mContext,
                 mInfo.mUri,
                 mInfo.mHint,
-                innerState.mHeaderContentDisposition,
-                innerState.mHeaderContentLocation,
+                innerState.mContentDisposition,
+                innerState.mContentLocation,
                 state.mMimeType,
                 mInfo.mDestination,
-                (innerState.mHeaderContentLength != null) ?
-                        Long.parseLong(innerState.mHeaderContentLength) : 0,
+                innerState.mContentLength,
                 mInfo.mIsPublicApi, mStorageManager);
         try {
             state.mStream = new FileOutputStream(state.mFilename);
@@ -645,58 +589,30 @@
     /**
      * Read headers from the HTTP response and store them into local state.
      */
-    private void readResponseHeaders(State state, InnerState innerState, HttpResponse response)
+    private void readResponseHeaders(State state, InnerState innerState, HttpURLConnection conn)
             throws StopRequestException {
-        Header header = response.getFirstHeader("Content-Disposition");
-        if (header != null) {
-            innerState.mHeaderContentDisposition = header.getValue();
-        }
-        header = response.getFirstHeader("Content-Location");
-        if (header != null) {
-            innerState.mHeaderContentLocation = header.getValue();
-        }
+        innerState.mContentDisposition = conn.getHeaderField("Content-Disposition");
+        innerState.mContentLocation = conn.getHeaderField("Content-Location");
+
         if (state.mMimeType == null) {
-            header = response.getFirstHeader("Content-Type");
-            if (header != null) {
-                state.mMimeType = Intent.normalizeMimeType(header.getValue());
-            }
-        }
-        header = response.getFirstHeader("ETag");
-        if (header != null) {
-            state.mHeaderETag = header.getValue();
-        }
-        String headerTransferEncoding = null;
-        header = response.getFirstHeader("Transfer-Encoding");
-        if (header != null) {
-            headerTransferEncoding = header.getValue();
-        }
-        if (headerTransferEncoding == null) {
-            header = response.getFirstHeader("Content-Length");
-            if (header != null) {
-                innerState.mHeaderContentLength = header.getValue();
-                state.mTotalBytes = mInfo.mTotalBytes =
-                        Long.parseLong(innerState.mHeaderContentLength);
-            }
-        } 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, "Content-Disposition: " +
-                    innerState.mHeaderContentDisposition);
-            Log.v(Constants.TAG, "Content-Length: " + innerState.mHeaderContentLength);
-            Log.v(Constants.TAG, "Content-Location: " + innerState.mHeaderContentLocation);
-            Log.v(Constants.TAG, "Content-Type: " + state.mMimeType);
-            Log.v(Constants.TAG, "ETag: " + state.mHeaderETag);
-            Log.v(Constants.TAG, "Transfer-Encoding: " + headerTransferEncoding);
+            state.mMimeType = Intent.normalizeMimeType(conn.getContentType());
         }
 
-        boolean noSizeInfo = innerState.mHeaderContentLength == null
-                && (headerTransferEncoding == null
-                    || !headerTransferEncoding.equalsIgnoreCase("chunked"));
+        state.mHeaderETag = conn.getHeaderField("ETag");
+
+        final String transferEncoding = conn.getHeaderField("Transfer-Encoding");
+        if (transferEncoding == null) {
+            innerState.mContentLength = getHeaderFieldLong(conn, "Content-Length", -1);
+        } else {
+            Log.i(TAG, "Ignoring Content-Length since Transfer-Encoding is also defined");
+            innerState.mContentLength = -1;
+        }
+
+        state.mTotalBytes = innerState.mContentLength;
+        mInfo.mTotalBytes = innerState.mContentLength;
+
+        final boolean noSizeInfo = innerState.mContentLength == -1
+                && (transferEncoding == null || !transferEncoding.equalsIgnoreCase("chunked"));
         if (!mInfo.mNoIntegrity && noSizeInfo) {
             throw new StopRequestException(Downloads.Impl.STATUS_HTTP_DATA_ERROR,
                     "can't know size of download, giving up");
@@ -706,21 +622,18 @@
     /**
      * Check the HTTP response status and handle anything unusual (e.g. not 200/206).
      */
-    private void handleExceptionalStatus(State state, InnerState innerState, HttpResponse response)
-            throws StopRequestException, RetryDownload {
-        int statusCode = response.getStatusLine().getStatusCode();
-        if (statusCode == 503 && mInfo.mNumFailed < Constants.MAX_RETRIES) {
-            handleServiceUnavailable(state, response);
-        }
-        if (statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 307) {
-            handleRedirect(state, response, statusCode);
+    private void handleExceptionalStatus(
+            State state, InnerState innerState, HttpURLConnection conn, int statusCode)
+            throws StopRequestException {
+        if (statusCode == HTTP_UNAVAILABLE && mInfo.mNumFailed < Constants.MAX_RETRIES) {
+            handleServiceUnavailable(state, conn);
         }
 
         if (Constants.LOGV) {
             Log.i(Constants.TAG, "recevd_status = " + statusCode +
                     ", mContinuingDownload = " + state.mContinuingDownload);
         }
-        int expectedStatus = state.mContinuingDownload ? 206 : Downloads.Impl.STATUS_SUCCESS;
+        int expectedStatus = state.mContinuingDownload ? HTTP_PARTIAL : HTTP_OK;
         if (statusCode != expectedStatus) {
             handleOtherStatus(state, innerState, statusCode);
         }
@@ -731,17 +644,17 @@
      */
     private void handleOtherStatus(State state, InnerState innerState, int statusCode)
             throws StopRequestException {
-        if (statusCode == 416) {
+        if (statusCode == HTTP_REQUESTED_RANGE_NOT_SATISFIABLE) {
             // range request failed. it should never fail.
             throw new IllegalStateException("Http Range request failure: totalBytes = " +
                     state.mTotalBytes + ", bytes recvd so far: " + state.mCurrentBytes);
         }
         int finalStatus;
-        if (Downloads.Impl.isStatusError(statusCode)) {
+        if (statusCode >= 400 && statusCode < 600) {
             finalStatus = statusCode;
         } else if (statusCode >= 300 && statusCode < 400) {
             finalStatus = Downloads.Impl.STATUS_UNHANDLED_REDIRECT;
-        } else if (state.mContinuingDownload && statusCode == Downloads.Impl.STATUS_SUCCESS) {
+        } else if (state.mContinuingDownload && statusCode == HTTP_OK) {
             finalStatus = Downloads.Impl.STATUS_CANNOT_RESUME;
         } else {
             finalStatus = Downloads.Impl.STATUS_UNHANDLED_HTTP_CODE;
@@ -751,97 +664,28 @@
     }
 
     /**
-     * Handle a 3xx redirect status.
-     */
-    private void handleRedirect(State state, HttpResponse response, int statusCode)
-            throws StopRequestException, RetryDownload {
-        if (Constants.LOGVV) {
-            Log.v(Constants.TAG, "got HTTP redirect " + statusCode);
-        }
-        if (state.mRedirectCount >= Constants.MAX_REDIRECTS) {
-            throw new StopRequestException(Downloads.Impl.STATUS_TOO_MANY_REDIRECTS,
-                    "too many redirects");
-        }
-        Header header = response.getFirstHeader("Location");
-        if (header == null) {
-            return;
-        }
-        if (Constants.LOGVV) {
-            Log.v(Constants.TAG, "Location :" + header.getValue());
-        }
-
-        String newUri;
-        try {
-            newUri = new URI(mInfo.mUri).resolve(new URI(header.getValue())).toString();
-        } catch(URISyntaxException ex) {
-            if (Constants.LOGV) {
-                Log.d(Constants.TAG, "Couldn't resolve redirect URI " + header.getValue()
-                        + " for " + mInfo.mUri);
-            }
-            throw new StopRequestException(Downloads.Impl.STATUS_HTTP_DATA_ERROR,
-                    "Couldn't resolve redirect URI");
-        }
-        ++state.mRedirectCount;
-        state.mRequestUri = newUri;
-        if (statusCode == 301 || statusCode == 303) {
-            // use the new URI for all future requests (should a retry/resume be necessary)
-            state.mNewUri = newUri;
-        }
-        throw new RetryDownload();
-    }
-
-    /**
      * Handle a 503 Service Unavailable status by processing the Retry-After header.
      */
-    private void handleServiceUnavailable(State state, HttpResponse response)
+    private void handleServiceUnavailable(State state, HttpURLConnection conn)
             throws StopRequestException {
-        if (Constants.LOGVV) {
-            Log.v(Constants.TAG, "got HTTP response code 503");
-        }
         state.mCountRetry = true;
-        Header header = response.getFirstHeader("Retry-After");
-        if (header != null) {
-           try {
-               if (Constants.LOGVV) {
-                   Log.v(Constants.TAG, "Retry-After :" + header.getValue());
-               }
-               state.mRetryAfter = Integer.parseInt(header.getValue());
-               if (state.mRetryAfter < 0) {
-                   state.mRetryAfter = 0;
-               } else {
-                   if (state.mRetryAfter < Constants.MIN_RETRY_AFTER) {
-                       state.mRetryAfter = Constants.MIN_RETRY_AFTER;
-                   } else if (state.mRetryAfter > Constants.MAX_RETRY_AFTER) {
-                       state.mRetryAfter = Constants.MAX_RETRY_AFTER;
-                   }
-                   state.mRetryAfter += Helpers.sRandom.nextInt(Constants.MIN_RETRY_AFTER + 1);
-                   state.mRetryAfter *= 1000;
-               }
-           } catch (NumberFormatException ex) {
-               // ignored - retryAfter stays 0 in this case.
-           }
+        state.mRetryAfter = conn.getHeaderFieldInt("Retry-After", -1);
+        if (state.mRetryAfter < 0) {
+            state.mRetryAfter = 0;
+        } else {
+            if (state.mRetryAfter < Constants.MIN_RETRY_AFTER) {
+                state.mRetryAfter = Constants.MIN_RETRY_AFTER;
+            } else if (state.mRetryAfter > Constants.MAX_RETRY_AFTER) {
+                state.mRetryAfter = Constants.MAX_RETRY_AFTER;
+            }
+            state.mRetryAfter += Helpers.sRandom.nextInt(Constants.MIN_RETRY_AFTER + 1);
+            state.mRetryAfter *= 1000;
         }
+
         throw new StopRequestException(Downloads.Impl.STATUS_WAITING_TO_RETRY,
                 "got 503 Service Unavailable, will retry later");
     }
 
-    /**
-     * Send the request to the server, handling any I/O exceptions.
-     */
-    private HttpResponse sendRequest(State state, AndroidHttpClient client, HttpGet request)
-            throws StopRequestException {
-        try {
-            return client.execute(request);
-        } catch (IllegalArgumentException ex) {
-            throw new StopRequestException(Downloads.Impl.STATUS_HTTP_DATA_ERROR,
-                    "while trying to execute request: " + ex.toString(), ex);
-        } catch (IOException ex) {
-            logNetworkState(mInfo.mUid);
-            throw new StopRequestException(getFinalStatusForHttpError(state),
-                    "while trying to execute request: " + ex.toString(), ex);
-        }
-    }
-
     private int getFinalStatusForHttpError(State state) {
         int networkUsable = mInfo.checkCanUseNetwork();
         if (networkUsable != DownloadInfo.NETWORK_OK) {
@@ -921,7 +765,7 @@
                     }
                     state.mCurrentBytes = (int) fileLength;
                     if (mInfo.mTotalBytes != -1) {
-                        innerState.mHeaderContentLength = Long.toString(mInfo.mTotalBytes);
+                        innerState.mContentLength = mInfo.mTotalBytes;
                     }
                     state.mHeaderETag = mInfo.mETag;
                     state.mContinuingDownload = true;
@@ -942,16 +786,18 @@
     /**
      * Add custom headers for this download to the HTTP request.
      */
-    private void addRequestHeaders(State state, HttpGet request) {
+    private void addRequestHeaders(State state, HttpURLConnection conn) {
+        conn.addRequestProperty("User-Agent", userAgent());
+
         for (Pair<String, String> header : mInfo.getHeaders()) {
-            request.addHeader(header.first, header.second);
+            conn.addRequestProperty(header.first, header.second);
         }
 
         if (state.mContinuingDownload) {
             if (state.mHeaderETag != null) {
-                request.addHeader("If-Match", state.mHeaderETag);
+                conn.addRequestProperty("If-Match", state.mHeaderETag);
             }
-            request.addHeader("Range", "bytes=" + state.mCurrentBytes + "-");
+            conn.addRequestProperty("Range", "bytes=" + state.mCurrentBytes + "-");
             if (Constants.LOGV) {
                 Log.i(Constants.TAG, "Adding Range header: " +
                         "bytes=" + state.mCurrentBytes + "-");
@@ -963,26 +809,20 @@
     /**
      * Stores information about the completed download, and notifies the initiating application.
      */
-    private void notifyDownloadCompleted(
-            int status, boolean countRetry, int retryAfter, boolean gotData,
-            String filename, String uri, String mimeType, String errorMsg) {
+    private void notifyDownloadCompleted(int status, boolean countRetry, int retryAfter,
+            boolean gotData, String filename, String mimeType, String errorMsg) {
         notifyThroughDatabase(
-                status, countRetry, retryAfter, gotData, filename, uri, mimeType,
-                errorMsg);
+                status, countRetry, retryAfter, gotData, filename, mimeType, errorMsg);
         if (Downloads.Impl.isStatusCompleted(status)) {
             mInfo.sendIntentIfRequested();
         }
     }
 
-    private void notifyThroughDatabase(
-            int status, boolean countRetry, int retryAfter, boolean gotData,
-            String filename, String uri, String mimeType, String errorMsg) {
+    private void notifyThroughDatabase(int status, boolean countRetry, int retryAfter,
+            boolean gotData, String filename, String mimeType, String errorMsg) {
         ContentValues values = new ContentValues();
         values.put(Downloads.Impl.COLUMN_STATUS, status);
         values.put(Downloads.Impl._DATA, filename);
-        if (uri != null) {
-            values.put(Downloads.Impl.COLUMN_URI, uri);
-        }
         values.put(Downloads.Impl.COLUMN_MIME_TYPE, mimeType);
         values.put(Downloads.Impl.COLUMN_LAST_MODIFICATION, mSystemFacade.currentTimeMillis());
         values.put(Constants.RETRY_AFTER_X_REDIRECT_COUNT, retryAfter);
@@ -1021,4 +861,12 @@
             mPolicyDirty = true;
         }
     };
+
+    public static long getHeaderFieldLong(URLConnection conn, String field, long defaultValue) {
+        try {
+            return Long.parseLong(conn.getHeaderField(field));
+        } catch (NumberFormatException e) {
+            return defaultValue;
+        }
+    }
 }
diff --git a/src/com/android/providers/downloads/Helpers.java b/src/com/android/providers/downloads/Helpers.java
index 359f6fa..484c925 100644
--- a/src/com/android/providers/downloads/Helpers.java
+++ b/src/com/android/providers/downloads/Helpers.java
@@ -78,6 +78,9 @@
             int destination,
             long contentLength,
             boolean isPublicApi, StorageManager storageManager) throws StopRequestException {
+        if (contentLength < 0) {
+            contentLength = 0;
+        }
         checkCanHandleDownload(context, mimeType, destination, isPublicApi);
         String path;
         File base = null;