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

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

Issue 2851023003: Moved OMA downloads out of DownloadManagerService (Closed)
Patch Set: Fix test Created 3 years, 7 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/OMADownloadHandler.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
index bff2741bb5806dc30eab25a691b014b4ab9629bc..71155bb95cf3ef7d112b630a92ee68ab172d50eb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
@@ -6,10 +6,14 @@ package org.chromium.chrome.browser.download;
import android.app.Activity;
import android.app.DownloadManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
@@ -19,6 +23,7 @@ import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.util.Log;
import android.util.LongSparseArray;
+import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.webkit.URLUtil;
@@ -29,6 +34,7 @@ import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.chromium.base.ApplicationStatus;
+import org.chromium.base.ContextUtils;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeApplication;
@@ -47,6 +53,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* This class handles OMA downloads according to the steps described in
@@ -66,8 +73,10 @@ import java.util.Map;
* be saved to the app directory first. If step 6 completes successfully, the content will
* be moved to the public external storage. Otherwise, it will be removed from the device.
*/
-public class OMADownloadHandler {
+public class OMADownloadHandler extends BroadcastReceiver
+ implements DownloadManagerDelegate.EnqueueDownloadRequestCallback {
private static final String TAG = "OMADownloadHandler";
+ private static final String PENDING_OMA_DOWNLOADS = "PendingOMADownloads";
// MIME types for OMA downloads.
public static final String OMA_DOWNLOAD_DESCRIPTOR_MIME = "application/vnd.oma.dd+xml";
@@ -104,8 +113,13 @@ public class OMADownloadHandler {
private static final String DOWNLOAD_STATUS_LOADER_ERROR = "954 Loader Error \n\r";
private final Context mContext;
+ private final SharedPreferences mSharedPrefs;
+ private final LongSparseArray<DownloadItem> mSystemDownloadIdMap =
+ new LongSparseArray<DownloadItem>();
private final LongSparseArray<OMAInfo> mPendingOMADownloads =
new LongSparseArray<OMAInfo>();
+ private final DownloadSnackbarController mDownloadSnackbarController;
+ private final DownloadManagerDelegate mDownloadManagerDelegate;
/**
* Information about the OMA content. The object is parsed from the download
@@ -192,8 +206,50 @@ public class OMADownloadHandler {
}
}
- public OMADownloadHandler(Context context) {
+ /**
+ * 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;
+ }
+ }
+
+ public OMADownloadHandler(Context context, DownloadManagerDelegate delegate,
+ DownloadSnackbarController downloadSnackbarController) {
mContext = context;
+ mSharedPrefs = ContextUtils.getAppSharedPreferences();
+ mDownloadManagerDelegate = delegate;
+ mDownloadSnackbarController = downloadSnackbarController;
}
/**
@@ -213,6 +269,7 @@ public class OMADownloadHandler {
private class OMAParserTask extends AsyncTask<Void, Void, OMAInfo> {
private final DownloadInfo mDownloadInfo;
private final long mDownloadId;
+ private long mFreeSpace;
public OMAParserTask(DownloadInfo downloadInfo, long downloadId) {
mDownloadInfo = downloadInfo;
mDownloadId = downloadId;
@@ -235,6 +292,7 @@ public class OMADownloadHandler {
Log.w(TAG, "Cannot read file.", e);
}
manager.remove(mDownloadId);
+ mFreeSpace = Environment.getExternalStorageDirectory().getUsableSpace();
return omaInfo;
}
@@ -256,7 +314,7 @@ public class OMADownloadHandler {
return;
}
// Check device capabilities.
- if (Environment.getExternalStorageDirectory().getUsableSpace() < getSize(omaInfo)) {
+ if (mFreeSpace < getSize(omaInfo)) {
showDownloadWarningDialog(
R.string.oma_download_insufficient_memory,
omaInfo, mDownloadInfo, DOWNLOAD_STATUS_INSUFFICIENT_MEMORY);
@@ -272,6 +330,36 @@ public class OMADownloadHandler {
}
}
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (!DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) return;
+ long downloadId = intent.getLongExtra(
+ DownloadManager.EXTRA_DOWNLOAD_ID, DownloadItem.INVALID_DOWNLOAD_ID);
+ if (downloadId == DownloadItem.INVALID_DOWNLOAD_ID) return;
+ boolean isPendingOMADownload = isPendingOMADownload(downloadId);
+ boolean isInOMASharedPrefs = isDownloadIdInOMASharedPrefs(downloadId);
+ if (isPendingOMADownload || isInOMASharedPrefs) {
+ clearPendingOMADownload(downloadId, null);
+ removeFromSystemDownloadIdMap(downloadId);
+ return;
+ }
+
+ DownloadItem downloadItem = mSystemDownloadIdMap.get(downloadId);
+ if (downloadItem != null) {
+ mDownloadManagerDelegate.queryDownloadResult(
+ downloadItem, true, DownloadManagerService.getDownloadManagerService());
+ removeFromSystemDownloadIdMap(downloadId);
+ }
+ }
+
+ private void removeFromSystemDownloadIdMap(long downloadId) {
+ mSystemDownloadIdMap.remove(downloadId);
+ if (mSystemDownloadIdMap.size() == 0) {
+ mContext.unregisterReceiver(this);
+ }
+ }
+
/**
* Called when the content is successfully downloaded by the Android DownloadManager.
*
@@ -279,7 +367,7 @@ public class OMADownloadHandler {
* @param downloadId Download Id from the Android DownloadManager.
* @param notifyURI The previously saved installNotifyURI attribute.
*/
- public void onDownloadCompleted(DownloadInfo downloadInfo, long downloadId, String notifyURI) {
+ private void onDownloadCompleted(DownloadInfo downloadInfo, long downloadId, String notifyURI) {
OMAInfo omaInfo = mPendingOMADownloads.get(downloadId);
if (omaInfo == null) {
omaInfo = new OMAInfo();
@@ -298,7 +386,7 @@ public class OMADownloadHandler {
* @param reason The reason of failure.
* @param notifyURI The previously saved installNotifyURI attribute.
*/
- public void onDownloadFailed(
+ private void onDownloadFailed(
DownloadInfo downloadInfo, long downloadId, int reason, String notifyURI) {
String status = DOWNLOAD_STATUS_DEVICE_ABORTED;
switch (reason) {
@@ -355,7 +443,8 @@ public class OMADownloadHandler {
* @param statusMessage The message to send to the notification server.
* @return true if the notification ise sent, or false otherwise.
*/
- private boolean sendNotification(
+ @VisibleForTesting
+ protected boolean sendNotification(
OMAInfo omaInfo, DownloadInfo downloadInfo, long downloadId, String statusMessage) {
if (omaInfo == null) return false;
if (omaInfo.isValueEmpty(OMA_INSTALL_NOTIFY_URI)) return false;
@@ -586,7 +675,8 @@ public class OMADownloadHandler {
* @param downloadInfo Information about the download.
* @param omaInfo Information about the OMA content.
*/
- private void downloadOMAContent(long downloadId, DownloadInfo downloadInfo, OMAInfo omaInfo) {
+ @VisibleForTesting
+ protected void downloadOMAContent(long downloadId, DownloadInfo downloadInfo, OMAInfo omaInfo) {
if (omaInfo == null) return;
String mimeType = omaInfo.getDrmType();
if (mimeType == null) {
@@ -609,8 +699,8 @@ public class OMADownloadHandler {
// Don't show complete notification until that happens.
DownloadItem item = new DownloadItem(true, newInfo);
item.setSystemDownloadId(downloadId);
- DownloadManagerService.getDownloadManagerService().enqueueDownloadManagerRequest(
- item, omaInfo.isValueEmpty(OMA_INSTALL_NOTIFY_URI));
+ mDownloadManagerDelegate.enqueueDownloadManagerRequest(
+ item, omaInfo.isValueEmpty(OMA_INSTALL_NOTIFY_URI), this);
mPendingOMADownloads.put(downloadId, omaInfo);
}
@@ -620,7 +710,7 @@ public class OMADownloadHandler {
* @param downloadId Download identifier.
* @return true if the download is in progress, or false otherwise.
*/
- public boolean isPendingOMADownload(long downloadId) {
+ private boolean isPendingOMADownload(long downloadId) {
return mPendingOMADownloads.get(downloadId) != null;
}
@@ -630,19 +720,187 @@ public class OMADownloadHandler {
* @param oldDownloadId Old download Id from the DownloadManager.
* @param newDownloadId New download Id from the DownloadManager.
*/
- public void updateDownloadInfo(long oldDownloadId, long newDownloadId) {
+ private void updateDownloadInfo(long oldDownloadId, long newDownloadId) {
OMAInfo omaInfo = mPendingOMADownloads.get(oldDownloadId);
mPendingOMADownloads.remove(oldDownloadId);
mPendingOMADownloads.put(newDownloadId, omaInfo);
}
+ @Override
+ public void onDownloadEnqueued(
+ boolean result, int failureReason, DownloadItem downloadItem, long downloadId) {
+ boolean isPendingOMADownload = isPendingOMADownload(downloadItem.getSystemDownloadId());
+ if (!result) {
+ if (isPendingOMADownload) {
+ onDownloadFailed(downloadItem.getDownloadInfo(), downloadItem.getSystemDownloadId(),
+ DownloadManager.ERROR_UNKNOWN, null);
+ }
+ return;
+ }
+
+ if (mSystemDownloadIdMap.size() == 0) {
+ mContext.registerReceiver(
+ this, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
+ }
+ mSystemDownloadIdMap.put(downloadId, downloadItem);
+
+ if (isPendingOMADownload) {
+ // A new downloadId is generated, needs to update the OMADownloadHandler
+ // about this.
+ updateDownloadInfo(downloadItem.getSystemDownloadId(), downloadId);
+ // 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 = getInstallNotifyInfo(downloadId);
+ if (!TextUtils.isEmpty(notifyUri)) {
+ OMAEntry entry = new OMAEntry(downloadId, notifyUri);
+ addOMADownloadToSharedPrefs(entry.generateSharedPrefsString());
+ }
+ }
+ DownloadManagerService.getDownloadManagerService().onDownloadEnqueued(
+ result, failureReason, downloadItem, downloadId);
+ }
+
+ /**
+ * Async task to clear the pending OMA download from SharedPrefs and inform
+ * the OMADownloadHandler about download status.
+ */
+ protected class ClearPendingOMADownloadTask
+ extends AsyncTask<Void, Void, Pair<Integer, Boolean>> {
+ private final DownloadItem mDownloadItem;
+ private final String mInstallNotifyURI;
+ private DownloadInfo mDownloadInfo;
+ private int mFailureReason;
+
+ public ClearPendingOMADownloadTask(DownloadItem downloadItem, String installNotifyURI) {
+ mDownloadItem = downloadItem;
+ mInstallNotifyURI = installNotifyURI;
+ mDownloadInfo = downloadItem.getDownloadInfo();
+ }
+
+ @Override
+ public Pair<Integer, Boolean> doInBackground(Void... voids) {
+ DownloadManager manager =
+ (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+ Cursor c = manager.query(
+ new DownloadManager.Query().setFilterById(mDownloadItem.getSystemDownloadId()));
+ int statusIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
+ int reasonIndex = c.getColumnIndex(DownloadManager.COLUMN_REASON);
+ int titleIndex = c.getColumnIndex(DownloadManager.COLUMN_TITLE);
+ int status = DownloadManager.STATUS_FAILED;
+ Boolean canResolve = false;
+ if (c.moveToNext()) {
+ status = c.getInt(statusIndex);
+ String title = c.getString(titleIndex);
+ if (mDownloadInfo == null) {
+ // Chrome has been killed, reconstruct a DownloadInfo.
+ mDownloadInfo =
+ new DownloadInfo.Builder()
+ .setFileName(title)
+ .setDescription(c.getString(
+ c.getColumnIndex(DownloadManager.COLUMN_DESCRIPTION)))
+ .setMimeType(c.getString(
+ c.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE)))
+ .setBytesReceived(Long.parseLong(c.getString(c.getColumnIndex(
+ DownloadManager.COLUMN_TOTAL_SIZE_BYTES))))
+ .build();
+ }
+ if (status == DownloadManager.STATUS_SUCCESSFUL) {
+ mDownloadInfo = DownloadInfo.Builder.fromDownloadInfo(mDownloadInfo)
+ .setFileName(title)
+ .build();
+ mDownloadItem.setDownloadInfo(mDownloadInfo);
+ canResolve = DownloadManagerService.canResolveDownloadItem(
+ mContext, mDownloadItem, false);
+ } else if (status == DownloadManager.STATUS_FAILED) {
+ mFailureReason = c.getInt(reasonIndex);
+ manager.remove(mDownloadItem.getSystemDownloadId());
+ }
+ }
+ c.close();
+ return Pair.create(status, canResolve);
+ }
+
+ @Override
+ protected void onPostExecute(Pair<Integer, Boolean> result) {
+ long downloadId = mDownloadItem.getSystemDownloadId();
+ if (result.first == DownloadManager.STATUS_SUCCESSFUL) {
+ onDownloadCompleted(mDownloadInfo, downloadId, mInstallNotifyURI);
+ removeOMADownloadFromSharedPrefs(downloadId);
+ mDownloadSnackbarController.onDownloadSucceeded(mDownloadInfo,
+ DownloadSnackbarController.INVALID_NOTIFICATION_ID, downloadId,
+ result.second, true);
+ } else if (result.first == DownloadManager.STATUS_FAILED) {
+ onDownloadFailed(mDownloadInfo, downloadId, mFailureReason, mInstallNotifyURI);
+ removeOMADownloadFromSharedPrefs(downloadId);
+ if (mDownloadInfo != null) {
+ String fileName = mDownloadInfo.getFileName();
+ DownloadManagerService.getDownloadManagerService().onDownloadFailed(
+ fileName, mFailureReason);
+ }
+ }
+ }
+ }
+
+ /**
+ * Clear pending OMA downloads for a particular download ID.
+ *
+ * @param downloadId Download identifier from Android DownloadManager.
+ * @param installNotifyURI URI to notify after installation.
+ */
+ private void clearPendingOMADownload(long downloadId, String installNotifyURI) {
+ DownloadItem item = mSystemDownloadIdMap.get(downloadId);
+ if (item == null) {
+ item = new DownloadItem(true, null);
+ item.setSystemDownloadId(downloadId);
+ }
+ ClearPendingOMADownloadTask task = new ClearPendingOMADownloadTask(item, installNotifyURI);
+ task.execute();
+ }
+
+ /**
+ * Clear any pending OMA downloads by reading them from shared prefs.
+ */
+ void clearPendingOMADownloads() {
+ if (mSharedPrefs.contains(PENDING_OMA_DOWNLOADS)) {
+ Set<String> omaDownloads = getStoredDownloadInfo(mSharedPrefs, PENDING_OMA_DOWNLOADS);
+ for (String omaDownload : omaDownloads) {
+ OMAEntry entry = OMAEntry.parseOMAEntry(omaDownload);
+ clearPendingOMADownload(entry.mDownloadId, entry.mInstallNotifyURI);
+ }
+ }
+ }
+
+ /**
+ * Gets download information from SharedPreferences.
+ * @param sharedPrefs The SharedPreferences object to parse.
+ * @param type Type of the information to retrieve.
+ * @return download information saved to the SharedPrefs for the given type.
+ */
+ private static Set<String> getStoredDownloadInfo(SharedPreferences sharedPrefs, String type) {
+ return DownloadManagerService.getStoredDownloadInfo(sharedPrefs, type);
+ }
+
+ /**
+ * Stores download information to shared preferences. The information can be
+ * either pending download IDs, or pending OMA downloads.
+ *
+ * @param sharedPrefs SharedPreferences to update.
+ * @param type Type of the information.
+ * @param downloadInfo Information to be saved.
+ */
+ static void storeDownloadInfo(
+ SharedPreferences sharedPrefs, String type, Set<String> downloadInfo) {
+ DownloadManagerService.storeDownloadInfo(sharedPrefs, type, downloadInfo);
+ }
+
/**
* Returns the installation notification URI for the OMA download.
*
* @param downloadId Download Identifier.
* @return String containing the installNotifyURI.
*/
- public String getInstallNotifyInfo(long downloadId) {
+ private String getInstallNotifyInfo(long downloadId) {
OMAInfo omaInfo = mPendingOMADownloads.get(downloadId);
return omaInfo.getValue(OMA_INSTALL_NOTIFY_URI);
}
@@ -738,4 +996,44 @@ public class OMADownloadHandler {
}
}
}
+
+ /**
+ * Add OMA download info to SharedPrefs.
+ * @param omaInfo OMA download information to save.
+ */
+ private void addOMADownloadToSharedPrefs(String omaInfo) {
+ Set<String> omaDownloads = getStoredDownloadInfo(mSharedPrefs, PENDING_OMA_DOWNLOADS);
+ omaDownloads.add(omaInfo);
+ storeDownloadInfo(mSharedPrefs, 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(mSharedPrefs, PENDING_OMA_DOWNLOADS);
+ for (String omaDownload : omaDownloads) {
+ OMAEntry entry = OMAEntry.parseOMAEntry(omaDownload);
+ if (entry.mDownloadId == downloadId) {
+ omaDownloads.remove(omaDownload);
+ storeDownloadInfo(mSharedPrefs, 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(mSharedPrefs, PENDING_OMA_DOWNLOADS);
+ for (String omaDownload : omaDownloads) {
+ OMAEntry entry = OMAEntry.parseOMAEntry(omaDownload);
+ if (entry.mDownloadId == downloadId) return true;
+ }
+ return false;
+ }
}

Powered by Google App Engine
This is Rietveld 408576698