Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2125)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java

Issue 848803002: Upstream DownloadManagerService (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: add findbugs warnings Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
new file mode 100644
index 0000000000000000000000000000000000000000..71045581966e282facd0f27ef8825eea041bed64
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -0,0 +1,719 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.download;
+
+import android.app.DownloadManager;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.os.Handler;
+import android.preference.PreferenceManager;
+import android.support.v4.util.LongSparseArray;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Toast;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.R;
+import org.chromium.content.browser.DownloadController;
+import org.chromium.content.browser.DownloadInfo;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Chrome implementation of the DownloadNotificationService interface. This class is responsible for
+ * keeping track of which downloads are in progress. It generates updates for progress of downloads
+ * and handles cleaning up of interrupted progress notifications.
+ */
+public class DownloadManagerService extends BroadcastReceiver implements
+ DownloadController.DownloadNotificationService {
+ private static final String TAG = "DownloadNotificationService";
+ private static final String DOWNLOAD_NOTIFICATION_IDS = "DownloadNotificationIds";
+ private static final String DOWNLOAD_DIRECTORY = "Download";
+ protected static final String PENDING_OMA_DOWNLOADS = "PendingOMADownloads";
+ private static final long UPDATE_DELAY_MILLIS = 1000;
+
+ private static DownloadManagerService sDownloadManagerService;
+
+ private final SharedPreferences mSharedPrefs;
+ private final ConcurrentHashMap<Integer, DownloadProgress> mDownloadProgressMap =
+ new ConcurrentHashMap<Integer, DownloadProgress>(4, 0.75f, 2);
+
+ private final DownloadNotifier mDownloadNotifier;
+ // Delay between UI updates.
+ private final long mUpdateDelayInMillis;
+
+ // Flag to track if we need to post a task to update download notifications.
+ private final AtomicBoolean mIsUIUpdateScheduled;
+ private final Handler mHandler;
+ private final Context mContext;
+
+ private final LongSparseArray<DownloadInfo> mPendingAutoOpenDownloads =
+ new LongSparseArray<DownloadInfo>();
+ private OMADownloadHandler mOMADownloadHandler;
+
+ /**
+ * Enum representing status of a download.
+ */
+ private enum DownloadStatus {
+ IN_PROGRESS,
+ COMPLETE,
+ FAILED
+ }
+
+ /**
+ * Class representing progress of a download.
+ */
+ private static class DownloadProgress {
+ final long mStartTimeInMillis;
+ volatile DownloadInfo mDownloadInfo;
+ volatile DownloadStatus mDownloadStatus;
+
+ DownloadProgress(long startTimeInMillis, DownloadInfo downloadInfo,
+ DownloadStatus downloadStatus) {
+ mStartTimeInMillis = startTimeInMillis;
+ mDownloadInfo = downloadInfo;
+ mDownloadStatus = downloadStatus;
+ }
+ }
+
+ /**
+ * Class representing an OMA download entry to be stored in SharedPrefs.
+ */
+ @VisibleForTesting
+ protected static class OMAEntry {
+ final long mDownloadId;
+ final String mInstallNotifyURI;
+
+ OMAEntry(long downloadId, String installNotifyURI) {
+ mDownloadId = downloadId;
+ mInstallNotifyURI = installNotifyURI;
+ }
+
+ /**
+ * Parse OMA entry from the SharedPrefs String
+ * TODO(qinmin): use a file instead of SharedPrefs to store the OMA entry.
+ *
+ * @param entry String contains the OMA information.
+ * @return an OMAEntry object.
+ */
+ @VisibleForTesting
+ static OMAEntry parseOMAEntry(String entry) {
+ int index = entry.indexOf(",");
+ long downloadId = Long.parseLong(entry.substring(0, index));
+ return new OMAEntry(downloadId, entry.substring(index + 1));
+ }
+
+ /**
+ * Generates a string for an OMA entry to be inserted into the SharedPrefs.
+ * TODO(qinmin): use a file instead of SharedPrefs to store the OMA entry.
+ *
+ * @return a String representing the download entry.
+ */
+ String generateSharedPrefsString() {
+ return String.valueOf(mDownloadId) + "," + mInstallNotifyURI;
+ }
+ }
+
+ /**
+ * Creates DownloadManagerService.
+ */
+ public static DownloadManagerService getDownloadManagerService(final Context context) {
+ ThreadUtils.assertOnUiThread();
+ assert context == context.getApplicationContext();
+ if (sDownloadManagerService == null) {
+ sDownloadManagerService = new DownloadManagerService(context,
+ new SystemDownloadNotifier(context), new Handler(), UPDATE_DELAY_MILLIS);
+ }
+ return sDownloadManagerService;
+ }
+
+ /**
+ * For tests only: sets the DownloadManagerService.
+ * @param service An instance of DownloadManagerService.
+ * @return Null or a currently set instance of DownloadManagerService.
+ */
+ @VisibleForTesting
+ public static DownloadManagerService setDownloadManagerService(DownloadManagerService service) {
+ ThreadUtils.assertOnUiThread();
+ DownloadManagerService prev = sDownloadManagerService;
+ sDownloadManagerService = service;
+ return prev;
+ }
+
+ @VisibleForTesting
+ protected DownloadManagerService(Context context,
+ DownloadNotifier downloadNotifier,
+ Handler handler,
+ long updateDelayInMillis) {
+ mContext = context;
+ mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(context
+ .getApplicationContext());
+ mDownloadNotifier = downloadNotifier;
+ mUpdateDelayInMillis = updateDelayInMillis;
+ mHandler = handler;
+ mIsUIUpdateScheduled = new AtomicBoolean(false);
+ mOMADownloadHandler = new OMADownloadHandler(context);
+ }
+
+ @Override
+ public void onDownloadCompleted(final DownloadInfo downloadInfo) {
+ DownloadStatus status = DownloadStatus.COMPLETE;
+ if (!downloadInfo.isSuccessful() || downloadInfo.getContentLength() == 0) {
+ status = DownloadStatus.FAILED;
+ }
+ updateDownloadProgress(downloadInfo, status);
+ scheduleUpdateIfNeeded();
+ }
+
+ @Override
+ public void onDownloadUpdated(final DownloadInfo downloadInfo) {
+ updateDownloadProgress(downloadInfo, DownloadStatus.IN_PROGRESS);
+ scheduleUpdateIfNeeded();
+ }
+
+ /**
+ * Clear any pending notifications for incomplete downloads by reading them from shared prefs.
+ * When Clank is restarted it clears any old notifications for incomplete downloads.
+ */
+ public void clearPendingDownloadNotifications() {
+ if (mSharedPrefs.contains(DOWNLOAD_NOTIFICATION_IDS)) {
+ Set<String> downloadIds = getStoredDownloadInfo(DOWNLOAD_NOTIFICATION_IDS);
+ for (String id : downloadIds) {
+ int notificationId = parseNotificationId(id);
+ if (notificationId > 0) {
+ mDownloadNotifier.cancelNotification(notificationId);
+ Log.w(TAG, "Download failed: Cleared download id:" + id);
+ }
+ }
+ mSharedPrefs.edit().remove(DOWNLOAD_NOTIFICATION_IDS);
+ mSharedPrefs.edit().apply();
+ }
+ if (mSharedPrefs.contains(PENDING_OMA_DOWNLOADS)) {
+ Set<String> omaDownloads = getStoredDownloadInfo(PENDING_OMA_DOWNLOADS);
+ for (String omaDownload : omaDownloads) {
+ OMAEntry entry = OMAEntry.parseOMAEntry(omaDownload);
+ clearPendingOMADownload(entry.mDownloadId, entry.mInstallNotifyURI);
+ }
+ }
+ }
+
+ /**
+ * Async task to clear the pending OMA download from SharedPrefs and inform
+ * the OMADownloadHandler about download status.
+ */
+ private class ClearPendingOMADownloadTask extends AsyncTask<Void, Void, Integer> {
+ private DownloadInfo mDownloadInfo;
+ private final long mDownloadId;
+ private final String mInstallNotifyURI;
+ private int mFailureReason;
+
+ public ClearPendingOMADownloadTask(long downloadId, String installNotifyURI) {
+ mDownloadId = downloadId;
+ mInstallNotifyURI = installNotifyURI;
+ mDownloadInfo = mPendingAutoOpenDownloads.get(downloadId);
+ }
+
+ @Override
+ public Integer doInBackground(Void...voids) {
+ DownloadManager manager =
+ (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+ Cursor c = manager.query(new DownloadManager.Query().setFilterById(mDownloadId));
+ int statusIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
+ int reasonIndex = c.getColumnIndex(DownloadManager.COLUMN_REASON);
+ int filenameIndex = c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
+ while (c.moveToNext()) {
+ int status = c.getInt(statusIndex);
+ if (mDownloadInfo == null) {
+ // Chrome has been killed, reconstruct a DownloadInfo.
+ mDownloadInfo = new DownloadInfo.Builder()
+ .setDownloadId((int) mDownloadId)
+ .setDescription(c.getString(
+ c.getColumnIndex(DownloadManager.COLUMN_DESCRIPTION)))
+ .setMimeType(c.getString(
+ c.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE)))
+ .setContentLength(Long.parseLong(c.getString(
+ c.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))))
+ .build();
+ }
+ if (status == DownloadManager.STATUS_SUCCESSFUL) {
+ mDownloadInfo = DownloadInfo.Builder.fromDownloadInfo(mDownloadInfo)
+ .setFilePath(c.getString(filenameIndex))
+ .build();
+ } else if (status == DownloadManager.STATUS_FAILED) {
+ mFailureReason = c.getInt(reasonIndex);
+ manager.remove(mDownloadId);
+ }
+ return status;
+ }
+ return DownloadManager.STATUS_FAILED;
+ }
+
+ @Override
+ protected void onPostExecute(Integer result) {
+ if (result == DownloadManager.STATUS_SUCCESSFUL) {
+ mOMADownloadHandler.onDownloadCompleted(mDownloadInfo, mInstallNotifyURI);
+ removeOMADownloadFromSharedPrefs(mDownloadId);
+ } else if (result == DownloadManager.STATUS_FAILED) {
+ mOMADownloadHandler.onDownloadFailed(
+ mDownloadInfo, mFailureReason, mInstallNotifyURI);
+ removeOMADownloadFromSharedPrefs(mDownloadId);
+ }
+ }
+ }
+
+ /**
+ * Clear pending OMA downloads for a particular download ID.
+ *
+ * @param downloadId Download identifier.
+ * @param info Information about the download.
+ * @param installNotifyURI URI to notify after installation.
+ */
+ private void clearPendingOMADownload(long downloadId, String installNotifyURI) {
+ ClearPendingOMADownloadTask task = new ClearPendingOMADownloadTask(
+ downloadId, installNotifyURI);
+ task.execute();
+ }
+
+ /**
+ * Parse the notification ID from a String object.
+ *
+ * @param id String containing the notification ID.
+ * @return notification ID.
+ */
+ private static int parseNotificationId(String id) {
+ try {
+ return Integer.parseInt(id);
+ } catch (NumberFormatException nfe) {
+ Log.w(TAG, "Exception while parsing download id:" + id);
+ return -1;
+ }
+ }
+
+ /**
+ * Broadcast that a download was successful.
+ * @param downloadInfo info about the download.
+ */
+ protected void broadcastDownloadSuccessful(DownloadInfo downloadInfo) {}
+
+ /**
+ * Gets download information from SharedPrefs.
+ * @param type Type of the information to retrieve.
+ * @return download information saved to the SharedPrefs for the given type.
+ */
+ @VisibleForTesting
+ protected Set<String> getStoredDownloadInfo(String type) {
+ return new HashSet<String>(mSharedPrefs.getStringSet(
+ type, new HashSet<String>()));
+ }
+
+ /**
+ * Removes a donwload Id from SharedPrefs.
+ * @param downloadId ID to be removed.
+ */
+ private void removeDownloadIdFromSharedPrefs(int downloadId) {
+ Set<String> downloadIds = getStoredDownloadInfo(DOWNLOAD_NOTIFICATION_IDS);
+ String id = Integer.toString(downloadId);
+ if (downloadIds.contains(id)) {
+ downloadIds.remove(id);
+ storeDownloadInfo(DOWNLOAD_NOTIFICATION_IDS, downloadIds);
+ }
+ }
+
+ /**
+ * Add download ID to SharedPrefs.
+ * @param downloadId ID to be stored.
+ */
+ private void addDownloadIdToSharedPrefs(int downloadId) {
+ Set<String> downloadIds = getStoredDownloadInfo(DOWNLOAD_NOTIFICATION_IDS);
+ downloadIds.add(Integer.toString(downloadId));
+ storeDownloadInfo(DOWNLOAD_NOTIFICATION_IDS, downloadIds);
+ }
+
+ /**
+ * Add OMA download info to SharedPrefs.
+ * @param omaInfo OMA download information to save.
+ */
+ @VisibleForTesting
+ protected void addOMADownloadToSharedPrefs(String omaInfo) {
+ Set<String> omaDownloads = getStoredDownloadInfo(PENDING_OMA_DOWNLOADS);
+ omaDownloads.add(omaInfo);
+ storeDownloadInfo(PENDING_OMA_DOWNLOADS, omaDownloads);
+ }
+
+ /**
+ * Remove OMA download info from SharedPrefs.
+ * @param downloadId ID to be removed.
+ */
+ private void removeOMADownloadFromSharedPrefs(long downloadId) {
+ Set<String> omaDownloads = getStoredDownloadInfo(PENDING_OMA_DOWNLOADS);
+ for (String omaDownload : omaDownloads) {
+ OMAEntry entry = OMAEntry.parseOMAEntry(omaDownload);
+ if (entry.mDownloadId == downloadId) {
+ omaDownloads.remove(omaDownload);
+ storeDownloadInfo(PENDING_OMA_DOWNLOADS, omaDownloads);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Check if a download ID is in OMA SharedPrefs.
+ * @param downloadId Download identifier to check.
+ * @param true if it is in the SharedPrefs, or false otherwise.
+ */
+ private boolean isDownloadIdInOMASharedPrefs(long downloadId) {
+ Set<String> omaDownloads = getStoredDownloadInfo(PENDING_OMA_DOWNLOADS);
+ for (String omaDownload : omaDownloads) {
+ OMAEntry entry = OMAEntry.parseOMAEntry(omaDownload);
+ if (entry.mDownloadId == downloadId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Stores download information to shared preferences. The information can be
+ * either pending download IDs, or pending OMA downloads.
+ *
+ * @param type Type of the information.
+ * @param downloadInfo Information to be saved.
+ */
+ private void storeDownloadInfo(String type, Set<String> downloadInfo) {
+ SharedPreferences.Editor editor = mSharedPrefs.edit();
+ if (downloadInfo.isEmpty()) {
+ editor.remove(type);
+ } else {
+ editor.putStringSet(type, downloadInfo);
+ }
+ editor.apply();
+ }
+
+ /**
+ * Updates notifications for all current downloads. Should not be called from UI thread.
+ */
+ private void updateAllNotifications() {
+ assert !ThreadUtils.runningOnUiThread();
+ for (DownloadProgress progress : mDownloadProgressMap.values()) {
+ if (progress != null) {
+ switch (progress.mDownloadStatus) {
+ case COMPLETE:
+ removeProgressNotificationForDownload(progress.mDownloadInfo
+ .getDownloadId());
+ mDownloadNotifier.notifyDownloadSuccessful(progress.mDownloadInfo);
+ broadcastDownloadSuccessful(progress.mDownloadInfo);
+ break;
+ case FAILED:
+ removeProgressNotificationForDownload(progress.mDownloadInfo
+ .getDownloadId());
+ mDownloadNotifier.notifyDownloadFailed(progress.mDownloadInfo);
+ Log.w(TAG, "Download failed: " + progress.mDownloadInfo.getFilePath());
+ break;
+ case IN_PROGRESS:
+ mDownloadNotifier.notifyDownloadProgress(progress.mDownloadInfo,
+ progress.mStartTimeInMillis);
+ }
+ }
+ }
+ }
+
+ /**
+ * Schedule an update if there is no update scheduled.
+ */
+ private void scheduleUpdateIfNeeded() {
+ if (mIsUIUpdateScheduled.compareAndSet(false, true)) {
+ Runnable updateTask = new Runnable() {
+ @Override
+ public void run() {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ public Void doInBackground(Void... params) {
+ updateAllNotifications();
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ mIsUIUpdateScheduled.set(false);
+ }
+ };
+ mHandler.postDelayed(updateTask, mUpdateDelayInMillis);
+ }
+ }
+
+ /**
+ * Cancel the progress notification of download and clear any cached information about this
+ * download.
+ *
+ * @param downloadId Download Identifier.
+ */
+ private void removeProgressNotificationForDownload(int downloadId) {
+ mDownloadProgressMap.remove(downloadId);
+ mDownloadNotifier.cancelNotification(downloadId);
+ removeDownloadIdFromSharedPrefs(downloadId);
+ }
+
+ /**
+ * Updates the progress of a download.
+ *
+ * @param downloadInfo Information about the download.
+ * @param status Status of the download.
+ */
+ private void updateDownloadProgress(DownloadInfo downloadInfo, DownloadStatus status) {
+ assert downloadInfo.hasDownloadId();
+ int downloadId = downloadInfo.getDownloadId();
+ DownloadProgress progress = mDownloadProgressMap.get(downloadId);
+ if (progress == null) {
+ progress = new DownloadProgress(System.currentTimeMillis(), downloadInfo,
+ status);
+ if (status == DownloadStatus.IN_PROGRESS) {
+ // A new in-progress download, add an entry to shared prefs to make sure
+ // to clear the notification.
+ addDownloadIdToSharedPrefs(downloadId);
+ }
+ mDownloadProgressMap.putIfAbsent(downloadId, progress);
+ } else {
+ progress.mDownloadStatus = status;
+ progress.mDownloadInfo = downloadInfo;
+ }
+ }
+
+ /**
+ * Sets the download handler for OMA downloads, for testing purpose.
+ *
+ * @param omaDownloadHandler Download handler for OMA contents.
+ */
+ @VisibleForTesting
+ protected void setOMADownloadHandler(OMADownloadHandler omaDownloadHandler) {
+ mOMADownloadHandler = omaDownloadHandler;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (!DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) return;
+ final DownloadManager manager =
+ (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+
+ long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
+ if (downloadId == -1) return;
+ boolean isPendingOMADownload = mOMADownloadHandler.isPendingOMADownload(downloadId);
+ boolean isInOMASharedPrefs = isDownloadIdInOMASharedPrefs(downloadId);
+ if (isPendingOMADownload || isInOMASharedPrefs) {
+ clearPendingOMADownload(downloadId, null);
+ mPendingAutoOpenDownloads.remove(downloadId);
+ } else if (mPendingAutoOpenDownloads.get(downloadId) != null) {
+ Cursor c = manager.query(new DownloadManager.Query().setFilterById(downloadId));
+ int statusIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
+ while (c.moveToNext()) {
+ int status = c.getInt(statusIndex);
+ DownloadInfo info = mPendingAutoOpenDownloads.get(downloadId);
+ switch (status) {
+ case DownloadManager.STATUS_SUCCESSFUL:
+ try {
+ mPendingAutoOpenDownloads.remove(downloadId);
+ if (OMADownloadHandler.OMA_DOWNLOAD_DESCRIPTOR_MIME.equalsIgnoreCase(
+ info.getMimeType())) {
+ mOMADownloadHandler.handleOMADownload(
+ info, downloadId);
+ manager.remove(downloadId);
+ break;
+ }
+ Uri uri = manager.getUriForDownloadedFile(downloadId);
+ Intent launchIntent = new Intent(Intent.ACTION_VIEW);
+
+ launchIntent.setDataAndType(
+ uri, manager.getMimeTypeForDownloadedFile(downloadId));
+ launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ mContext.startActivity(launchIntent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Activity not found.");
+ }
+ break;
+ case DownloadManager.STATUS_FAILED:
+ mPendingAutoOpenDownloads.remove(downloadId);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (mPendingAutoOpenDownloads.size() == 0) {
+ mContext.unregisterReceiver(this);
+ }
+ }
+
+ /**
+ * Sends the download request to Android download manager. If |notifyCompleted| is true,
+ * a notification will be sent to the user once download is complete and the downloaded
+ * content will be saved to the public directory on external storage. Otherwise, the
+ * download will be saved in the app directory and user will not get any notifications
+ * after download completion.
+ *
+ * @param info Download information about the download.
+ * @param notifyCompleted Whether to notify the user after Downloadmanager completes the
+ * download.
+ */
+ public void enqueueDownloadManagerRequest(final DownloadInfo info, boolean notifyCompleted) {
+ EnqueueDownloadRequestTask task = new EnqueueDownloadRequestTask(info);
+ task.execute(notifyCompleted);
+ }
+
+ /**
+ * Async task to enqueue a download request into DownloadManager.
+ */
+ private class EnqueueDownloadRequestTask extends
+ AsyncTask<Boolean, Void, Boolean> {
+ private int mErrorId;
+ private long mDownloadId;
+ private DownloadInfo mDownloadInfo;
+
+ public EnqueueDownloadRequestTask(DownloadInfo downloadInfo) {
+ mDownloadInfo = downloadInfo;
+ }
+
+ @Override
+ public Boolean doInBackground(Boolean... booleans) {
+ boolean notifyCompleted = booleans[0];
+ Uri uri = Uri.parse(mDownloadInfo.getUrl());
+ DownloadManager.Request request;
+ try {
+ request = new DownloadManager.Request(uri);
+ } catch (IllegalArgumentException e) {
+ mErrorId = R.string.cannot_download_http_or_https;
+ return false;
+ }
+
+ request.setMimeType(mDownloadInfo.getMimeType());
+ try {
+ if (notifyCompleted) {
+ // Set downloaded file destination to /sdcard/Download or, should it be
+ // set to one of several Environment.DIRECTORY* dirs depending on mimetype?
+ request.setDestinationInExternalPublicDir(
+ Environment.DIRECTORY_DOWNLOADS, mDownloadInfo.getFileName());
+ } else {
+ File dir = new File(mContext.getExternalFilesDir(null), DOWNLOAD_DIRECTORY);
+ if (dir.mkdir() || dir.isDirectory()) {
+ File file = new File(dir, mDownloadInfo.getFileName());
+ request.setDestinationUri(Uri.fromFile(file));
+ } else {
+ mErrorId = R.string.cannot_create_download_directory_title;
+ return false;
+ }
+ }
+ } catch (IllegalStateException e) {
+ mErrorId = R.string.cannot_create_download_directory_title;
+ return false;
+ }
+
+ if (notifyCompleted) {
+ // Let this downloaded file be scanned by MediaScanner - so that it can
+ // show up in Gallery app, for example.
+ request.allowScanningByMediaScanner();
+ request.setNotificationVisibility(
+ DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
+ } else {
+ request.setNotificationVisibility(
+ DownloadManager.Request.VISIBILITY_VISIBLE);
+ }
+ String description = mDownloadInfo.getDescription();
+ if (TextUtils.isEmpty(description)) {
+ description = mDownloadInfo.getFileName();
+ }
+ request.setDescription(description);
+ request.addRequestHeader("Cookie", mDownloadInfo.getCookie());
+ request.addRequestHeader("Referer", mDownloadInfo.getReferer());
+ request.addRequestHeader("User-Agent", mDownloadInfo.getUserAgent());
+
+ DownloadManager manager =
+ (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+ try {
+ mDownloadId = manager.enqueue(request);
+ } catch (IllegalArgumentException e) {
+ // See crbug.com/143499 for more details.
+ Log.e(TAG, "Download failed: " + e);
+ mErrorId = R.string.cannot_download_generic;
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ boolean isPendingOMADownload = mOMADownloadHandler.isPendingOMADownload(
+ (long) mDownloadInfo.getDownloadId());
+ if (!result) {
+ Toast.makeText(mContext, mErrorId, Toast.LENGTH_SHORT).show();
+ if (isPendingOMADownload) {
+ mOMADownloadHandler.onDownloadFailed(
+ mDownloadInfo, DownloadManager.ERROR_UNKNOWN, null);
+ }
+ return;
+ }
+ Toast.makeText(mContext, R.string.download_pending, Toast.LENGTH_SHORT).show();
+ if (isPendingOMADownload) {
+ // A new downloadId is generated, needs to update the OMADownloadHandler
+ // about this.
+ mDownloadInfo = mOMADownloadHandler.updateDownloadInfo(
+ mDownloadInfo, mDownloadId);
+ // TODO(qinmin): use a file instead of shared prefs to save the
+ // OMA information in case chrome is killed. This will allow us to
+ // save more information like cookies and user agent.
+ String notifyUri = mOMADownloadHandler.getInstallNotifyInfo(mDownloadId);
+ if (!TextUtils.isEmpty(notifyUri)) {
+ OMAEntry entry = new OMAEntry(mDownloadId, notifyUri);
+ addOMADownloadToSharedPrefs(entry.generateSharedPrefsString());
+ }
+ }
+ if (shouldOpenAfterDownload(mDownloadInfo) || isPendingOMADownload) {
+ if (mPendingAutoOpenDownloads.size() == 0) {
+ mContext.registerReceiver(DownloadManagerService.this,
+ new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
+ }
+ mPendingAutoOpenDownloads.put(mDownloadId, mDownloadInfo);
+ }
+ }
+ }
+
+ /**
+ * Determines if the download should be immediately opened after
+ * downloading.
+ *
+ * @param downloadInfo Information about the download.
+ * @return true if the downloaded content should be opened, or false otherwise.
+ */
+ @VisibleForTesting
+ static boolean shouldOpenAfterDownload(DownloadInfo downloadInfo) {
+ String type = downloadInfo.getMimeType();
+ return downloadInfo.hasUserGesture()
+ && !isAttachment(downloadInfo.getContentDisposition())
+ && (type.equalsIgnoreCase("application/pdf")
+ || type.equalsIgnoreCase(OMADownloadHandler.OMA_DOWNLOAD_DESCRIPTOR_MIME));
+ }
+
+ /**
+ * Returns true if the download meant to be treated as an attachment.
+ *
+ * @param contentDisposition Content disposition of the download.
+ * @return true if the downloaded is an attachment, or false otherwise.
+ */
+ public static boolean isAttachment(String contentDisposition) {
+ return contentDisposition != null
+ && contentDisposition.regionMatches(true, 0, "attachment", 0, 10);
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698