/** * Read the data from the URI. * * @param dest where to write it to. * @return true if we read the entire stream. */ /* package */ CompletionStatus readFromUri(final String dest) { cumulativeBytesRead = 0L; if (null == uri) { failureMessage = MSG_BAD_URI; downloadErrorCode = DownloadError.BAD_URI.getValue(); return CompletionStatus.FAILED; } if (!haveNetwork()) { Log.i(LOG_TAG, "No network appears available, insta-pausing download"); failureMessage = MSG_NO_NETWORK; downloadErrorCode = DownloadError.NO_NETWORK.getValue(); return CompletionStatus.PAUSED; } // Give the download policy a chance to veto based only on the URI final DownloadPolicyProvider dpp = provider.get(); if (null != dpp) { final DownloadPolicyProvider.Response policyResponse = dpp.mayStartDownload(forUser, uri); if (policyResponse != null) { final boolean policyResponseResponse = policyResponse.getResponse(); if (!policyResponseResponse) { failureMessage = MSG_VETO + policyResponse.getReason(); downloadErrorCode = DownloadError.POLICY_ERROR.getValue(); if (policyResponse.getShouldPause()) { return CompletionStatus.PAUSED; } return CompletionStatus.FAILED; } } else { Log.w(LOG_TAG, "DownloadPolicyProvider response was null!"); } } // Acquire a wifi lock if required. networkStatusProvider.acquireWifiLock(downloadId); int retryAttempt = 0; while (retryAttempt < NUM_RETRIES) { start(); InputStream stream = null; RandomAccessFile output = null; try { output = new RandomAccessFile(dest, "rw"); final URL url = new URL(uri); final HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); setupRequest(httpURLConnection); // Open connection and send headers. stream = httpURLConnection.getInputStream(); // check response final int responseCode = httpURLConnection.getResponseCode(); if (!gotSuccessResponse(responseCode)) { Log.w(LOG_TAG, "Did not get a 2xx response code back from request."); // Could use getErrorStream to read back error info. failureMessage = MSG_PREFIX_HTTP + responseCode; // get error code string downloadErrorCode = DownloadError.HTTP_ERROR.getValue(); return CompletionStatus.FAILED; } if (!gotValidRangeResponse(responseCode)) { // we didn't get a 206, but instead a 200 indicating to re-read the entire entity Log.w(LOG_TAG, "Did not get a 206 response code back from request."); downloadOffset = 0L; } // if we don't currently have an eTag for this download, try to set from the response // headers. if (downloadTag == null) { downloadTag = httpURLConnection.getHeaderField(Downloader.HEADER_ETAG); } // update the mime type and other fields in the content provider whether the actual // read is vetoed or not updateProviderFromHeaders(httpURLConnection); // Give the download policy a chance to veto based on the URI and file type and size if (totalBytes == 0L) { totalBytes = getTotalLengthFromHeader(httpURLConnection); } final String mt = getMimeType(httpURLConnection); if (null != dpp) { final DownloadPolicyProvider.Response policyResponse = dpp.mayReadStream(forUser, uri, totalBytes, mt); if (policyResponse != null) { final boolean policyResponseResponse = policyResponse.getResponse(); if (!policyResponseResponse) { // We are aborting the request, since the finally block closes the stream, // there is nothing additional to do here. failureMessage = MSG_VETO + policyResponse.getReason(); downloadErrorCode = DownloadError.POLICY_ERROR.getValue(); if (policyResponse.getShouldPause()) { return CompletionStatus.PAUSED; } return CompletionStatus.FAILED; } } else { Log.w(LOG_TAG, "DownloadPolicyProvider response was null!"); } } output.seek(downloadOffset); final byte[] buffer = new byte[BUFFER_SIZE]; cumulativeBytesRead = downloadOffset; int bytesRead; boolean interrupted = false; while ((bytesRead = stream.read(buffer)) > 0) { cumulativeBytesRead += bytesRead; output.write(buffer, 0, bytesRead); if (!isSilent) { sendProgress(cumulativeBytesRead, totalBytes); } if (Thread.interrupted() || !TaskCancelReason.UNEXPECTED.equals(getCancelReason())) { Log.i(LOG_TAG, "Download task is interrupted"); switch (getCancelReason()) { case UNEXPECTED: Log.w(LOG_TAG, "Unexpected interruption of download task."); failureMessage = MSG_UNEXPECTED_INTERRUPTION; downloadErrorCode = DownloadError.DOWNLOAD_INTERRUPTED.getValue(); retryAttempt = NUM_RETRIES; break; case PAUSED_BY_USER: // Set the failure message since this gets passed back as a completion description. failureMessage = MSG_PAUSED_DOWNLOAD; downloadErrorCode = DownloadError.USER_PAUSED.getValue(); return CompletionStatus.PAUSED_BY_USER; case CANCELED_BY_USER: failureMessage = MSG_CANCELED_DOWNLOAD; downloadErrorCode = DownloadError.USER_CANCELED.getValue(); interrupted = true; retryAttempt = Integer.MAX_VALUE; break; } break; } } if (!interrupted) { return CompletionStatus.SUCCEEDED; } } catch (final IOException ex) { Log.e(LOG_TAG, "Caught IO exception while downloading", ex); failureMessage = ex.getClass().getSimpleName() + ": " + ex.getMessage(); downloadErrorCode = DownloadError.IO_EXCEPTION.getValue(); retryAttempt++; downloadOffset = cumulativeBytesRead; } finally { safeClose(stream); safeClose(output); } // if we succeeded in getting any of the file, report a pause so that it can be resumed. if (haveDownloadProgress() && retryAttempt == NUM_RETRIES) { return CompletionStatus.PAUSED; } } return CompletionStatus.FAILED; }