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

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.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/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
new file mode 100644
index 0000000000000000000000000000000000000000..830902eed8053f9391c73cf075df475d7edc28cc
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
@@ -0,0 +1,727 @@
+// 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.Activity;
+import android.app.AlertDialog;
+import android.app.DownloadManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.provider.Browser;
+import android.support.v4.util.LongSparseArray;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromiumApplication;
+import org.chromium.content.browser.DownloadInfo;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class handles OMA downloads according to the steps described in
+ * http://xml.coverpages.org/OMA-Download-OTA-V10-20020620.pdf:
+ * 1. Receives a download descriptor xml file.
+ * 2. Parses all the contents.
+ * 3. Checks device capability to see if it is able to handle the content.
+ * 4. Find the objectURI value from the download descriptor and prompt user with
+ * a dialog to proceed with the download.
+ * 5. On positive confirmation, sends a request to the download manager.
+ * 6. Once the download is completed, sends a message to the server if installNotifyURI
+ * is present in the download descriptor.
+ * 7. Prompts user with a dialog to open the NextURL specified in the download descriptor.
+ * If steps 2 - 6 fails, a warning dialog will be prompted to the user to let him
+ * know the error. Steps 6-7 will be executed afterwards.
+ * If installNotifyURI is present in the download descriptor, the downloaded content will
+ * 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 {
+ private static final String TAG = "OMADownloadHandler";
+
+ // MIME types for OMA downloads.
+ public static final String OMA_DOWNLOAD_DESCRIPTOR_MIME = "application/vnd.oma.dd+xml";
+ public static final String OMA_DRM_MESSAGE_MIME = "application/vnd.oma.drm.message";
+ public static final String OMA_DRM_CONTENT_MIME = "application/vnd.oma.drm.content";
+ public static final String OMA_DRM_RIGHTS_MIME = "application/vnd.oma.drm.rights+wbxml";
+
+ // Valid download descriptor attributes.
+ protected static final String OMA_TYPE = "type";
+ protected static final String OMA_SIZE = "size";
+ protected static final String OMA_OBJECT_URI = "objectURI";
+ protected static final String OMA_INSTALL_NOTIFY_URI = "installNotifyURI";
+ protected static final String OMA_NEXT_URL = "nextURL";
+ protected static final String OMA_DD_VERSION = "DDVersion";
+ protected static final String OMA_NAME = "name";
+ protected static final String OMA_DESCRIPTION = "description";
+ protected static final String OMA_VENDOR = "vendor";
+ protected static final String OMA_INFO_URL = "infoURL";
+ protected static final String OMA_ICON_URI = "iconURI";
+ protected static final String OMA_INSTALL_PARAM = "installParam";
+
+ // Error message to send to the notification server.
+ private static final String DOWNLOAD_STATUS_SUCCESS = "900 Success \n\r";
+ private static final String DOWNLOAD_STATUS_INSUFFICIENT_MEMORY =
+ "901 insufficient memory \n\r";
+ private static final String DOWNLOAD_STATUS_USER_CANCELLED = "902 User Cancelled \n\r";
+ private static final String DOWNLOAD_STATUS_LOSS_OF_SERVICE = "903 Loss of Service \n\r";
+ private static final String DOWNLOAD_STATUS_ATTRIBUTE_MISMATCH = "905 Attribute mismatch \n\r";
+ private static final String DOWNLOAD_STATUS_INVALID_DESCRIPTOR = "906 Invalid descriptor \n\r";
+ private static final String DOWNLOAD_STATUS_INVALID_DDVERSION = "951 Invalid DDVersion \n\r";
+ private static final String DOWNLOAD_STATUS_DEVICE_ABORTED = "952 Device Aborted \n\r";
+ private static final String DOWNLOAD_STATUS_NON_ACCEPTABLE_CONTENT =
+ "953 Non-Acceptable Content \n\r";
+ private static final String DOWNLOAD_STATUS_LOADER_ERROR = "954 Loader Error \n\r";
+
+ private final Context mContext;
+ private final LongSparseArray<OMAInfo> mPendingOMADownloads =
+ new LongSparseArray<OMAInfo>();
+
+ /**
+ * Information about the OMA content. The object is parsed from the download
+ * descriptor. There can be multiple MIME types for the object.
+ */
+ @VisibleForTesting
+ protected static class OMAInfo {
+ private final Map<String, String> mDescription;
+ private final List<String> mTypes;
+
+ OMAInfo() {
+ mDescription = new HashMap<String, String>();
+ mTypes = new ArrayList<String>();
+ }
+
+ /**
+ * Inserts an attribute-value pair about the OMA content. If the attribute already
+ * exists, the new value will replace the old one. For MIME type, it will be appended
+ * to the existing MIME types.
+ *
+ * @param attribute The attribute to be inserted.
+ * @param value The new value of the attribute.
+ */
+ void addAttributeValue(String attribute, String value) {
+ if (attribute.equals(OMA_TYPE)) {
+ mTypes.add(value);
+ } else {
+ // TODO(qinmin): Handle duplicate attributes
+ mDescription.put(attribute, value);
+ }
+ }
+
+ /**
+ * Gets the value for an attribute.
+ *
+ * @param attribute The attribute to be retrieved.
+ * @return value of the attribute.
+ */
+ String getValue(String attribute) {
+ return mDescription.get(attribute);
+ }
+
+ /**
+ * Checks whether the value is empty for an attribute.
+ *
+ * @param attribute The attribute to be retrieved.
+ * @return true if it is empty, or false otherwise.
+ */
+ boolean isValueEmpty(String attribute) {
+ return TextUtils.isEmpty(getValue(attribute));
+ }
+
+ /**
+ * Gets the list of MIME types of the OMA content.
+ *
+ * @return List of MIME types.
+ */
+ List<String> getTypes() {
+ return mTypes;
+ }
+
+ /**
+ * Checks whether the information about the OMA content is empty.
+ *
+ * @return true if all attributes are empty, or false otherwise.
+ */
+ boolean isEmpty() {
+ return mDescription.isEmpty() && mTypes.isEmpty();
+ }
+
+ /**
+ * Gets the DRM MIME type of this object.
+ *
+ * @return the DRM MIME type if it is found, or null otherwise.
+ */
+ String getDrmType() {
+ for (String type : mTypes) {
+ if (type.equalsIgnoreCase(OMA_DRM_MESSAGE_MIME)
+ || type.equalsIgnoreCase(OMA_DRM_CONTENT_MIME)) {
+ return type;
+ }
+ }
+ return null;
+ }
+ }
+
+ public OMADownloadHandler(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Starts handling the OMA download.
+ *
+ * @param downloadInfo The information about the download.
+ * @param downloadId The unique identifier maintained by the Android DownloadManager.
+ */
+ public void handleOMADownload(DownloadInfo downloadInfo, long downloadId) {
+ OMAParserTask task = new OMAParserTask(downloadInfo, downloadId);
+ task.execute();
+ }
+
+ /**
+ * Async task to parse an OMA download descriptor.
+ */
+ private class OMAParserTask extends AsyncTask<Void, Void, OMAInfo> {
+ private final DownloadInfo mDownloadInfo;
+ private final long mDownloadId;
+ public OMAParserTask(DownloadInfo downloadInfo, long downloadId) {
+ mDownloadInfo = downloadInfo;
+ mDownloadId = downloadId;
+ }
+
+ @Override
+ public OMAInfo doInBackground(Void...voids) {
+ OMAInfo omaInfo = null;
+ final DownloadManager manager =
+ (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+ try {
+ ParcelFileDescriptor fd = manager.openDownloadedFile(mDownloadId);
+ if (fd == null) return null;
+ omaInfo = parseDownloadDescriptor(new FileInputStream(fd.getFileDescriptor()));
+ fd.close();
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "File not found.", e);
+ } catch (IOException e) {
+ Log.w(TAG, "Cannot read file.", e);
+ }
+ return omaInfo;
+ }
+
+ @Override
+ protected void onPostExecute(OMAInfo omaInfo) {
+ if (omaInfo == null) return;
+ // Send notification if required attributes are missing.
+ if (omaInfo.getTypes().isEmpty() || getSize(omaInfo) <= 0
+ || omaInfo.isValueEmpty(OMA_OBJECT_URI)) {
+ sendNotification(omaInfo, mDownloadInfo, DOWNLOAD_STATUS_INVALID_DESCRIPTOR);
+ return;
+ }
+ // Check version. Null version are treated as 1.0.
+ String version = omaInfo.getValue(OMA_DD_VERSION);
+ if (version != null && !version.startsWith("1.")) {
+ sendNotification(omaInfo, mDownloadInfo, DOWNLOAD_STATUS_INVALID_DDVERSION);
+ return;
+ }
+ // Check device capabilities.
+ if (Environment.getExternalStorageDirectory().getUsableSpace() < getSize(omaInfo)) {
+ showDownloadWarningDialog(
+ R.string.oma_download_insufficient_memory,
+ omaInfo, mDownloadInfo, DOWNLOAD_STATUS_INSUFFICIENT_MEMORY);
+ return;
+ }
+ if (getOpennableType(mContext.getPackageManager(), omaInfo) == null) {
+ showDownloadWarningDialog(
+ R.string.oma_download_non_acceptable_content,
+ omaInfo, mDownloadInfo, DOWNLOAD_STATUS_NON_ACCEPTABLE_CONTENT);
+ return;
+ }
+ showOMAInfoDialog(mDownloadId, mDownloadInfo, omaInfo);
+ }
+ }
+
+ /**
+ * Called when the content is successfully downloaded by the Android DownloadManager.
+ *
+ * @param downloadInfo The information about the download.
+ * @param notifyURI The previously saved installNotifyURI attribute.
+ */
+ public void onDownloadCompleted(DownloadInfo downloadInfo, String notifyURI) {
+ long downloadId = (long) downloadInfo.getDownloadId();
+ OMAInfo omaInfo = mPendingOMADownloads.get(downloadId);
+ if (omaInfo == null) {
+ omaInfo = new OMAInfo();
+ omaInfo.addAttributeValue(OMA_INSTALL_NOTIFY_URI, notifyURI);
+ }
+ sendInstallNotificationAndNextStep(omaInfo, downloadInfo, DOWNLOAD_STATUS_SUCCESS);
+ mPendingOMADownloads.remove(downloadId);
+ }
+
+ /**
+ * Called when android DownloadManager fails to download the content.
+ *
+ * @param downloadInfo The information about the download.
+ * @param reason The reason of failure.
+ * @param notifyURI The previously saved installNotifyURI attribute.
+ */
+ public void onDownloadFailed(DownloadInfo downloadInfo, int reason, String notifyURI) {
+ String status = DOWNLOAD_STATUS_DEVICE_ABORTED;
+ switch (reason) {
+ case DownloadManager.ERROR_CANNOT_RESUME:
+ status = DOWNLOAD_STATUS_LOSS_OF_SERVICE;
+ break;
+ case DownloadManager.ERROR_HTTP_DATA_ERROR:
+ case DownloadManager.ERROR_TOO_MANY_REDIRECTS:
+ case DownloadManager.ERROR_UNHANDLED_HTTP_CODE:
+ status = DOWNLOAD_STATUS_LOADER_ERROR;
+ break;
+ case DownloadManager.ERROR_INSUFFICIENT_SPACE:
+ status = DOWNLOAD_STATUS_INSUFFICIENT_MEMORY;
+ break;
+ default:
+ break;
+ }
+ long downloadId = (long) downloadInfo.getDownloadId();
+ OMAInfo omaInfo = mPendingOMADownloads.get(downloadId);
+ if (omaInfo == null) {
+ // Just send the notification in this case.
+ omaInfo = new OMAInfo();
+ omaInfo.addAttributeValue(OMA_INSTALL_NOTIFY_URI, notifyURI);
+ sendInstallNotificationAndNextStep(omaInfo, downloadInfo, status);
+ return;
+ }
+ showDownloadWarningDialog(
+ R.string.oma_download_failed, omaInfo, downloadInfo, status);
+ mPendingOMADownloads.remove(downloadId);
+ }
+
+ /**
+ * Sends the install notification and then opens the nextURL if they are provided.
+ * If the install notification is sent, nextURL will be opened after the server
+ * response is received.
+ *
+ * @param omaInfo Information about the OMA content.
+ * @param downloadInfo Information about the download.
+ * @param statusMessage The message to send to the notification server.
+ */
+ private void sendInstallNotificationAndNextStep(
+ OMAInfo omaInfo, DownloadInfo downloadInfo, String statusMessage) {
+ if (!sendNotification(omaInfo, downloadInfo, statusMessage)) {
+ showNextUrlDialog(omaInfo);
+ }
+ }
+
+ /**
+ * Sends the install notification to the server.
+ *
+ * @param omaInfo Information about the OMA content.
+ * @param downloadInfo Information about the download.
+ * @param statusMessage The message to send to the notification server.
+ * @return true if the notification ise sent, or false otherwise.
+ */
+ private boolean sendNotification(
+ OMAInfo omaInfo, DownloadInfo downloadInfo, String statusMessage) {
+ if (omaInfo == null) return false;
+ if (omaInfo.isValueEmpty(OMA_INSTALL_NOTIFY_URI)) return false;
+ PostStatusTask task = new PostStatusTask(omaInfo, downloadInfo, statusMessage);
+ task.execute();
+ return true;
+ }
+
+ /**
+ * Shows the OMA information to the user and ask whether user want to proceed.
+ *
+ * @param downloadId The unique identifier maintained by the Android DownloadManager.
+ * @param downloadInfo Information about the download.
+ * @param omaInfo Information about the OMA content.
+ */
+ private void showOMAInfoDialog(
+ final long downloadId, final DownloadInfo downloadInfo, final OMAInfo omaInfo) {
+ LayoutInflater inflater =
+ (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View v = inflater.inflate(R.layout.confirm_oma_download, null);
+
+ TextView textView = (TextView) v.findViewById(R.id.oma_download_name);
+ textView.setText(omaInfo.getValue(OMA_NAME));
+ textView = (TextView) v.findViewById(R.id.oma_download_vendor);
+ textView.setText(omaInfo.getValue(OMA_VENDOR));
+ textView = (TextView) v.findViewById(R.id.oma_download_size);
+ textView.setText(omaInfo.getValue(OMA_SIZE));
+ textView = (TextView) v.findViewById(R.id.oma_download_type);
+ textView.setText(getOpennableType(mContext.getPackageManager(), omaInfo));
+ textView = (TextView) v.findViewById(R.id.oma_download_description);
+ textView.setText(omaInfo.getValue(OMA_DESCRIPTION));
+
+ DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == AlertDialog.BUTTON_POSITIVE) {
+ downloadOMAContent(downloadId, downloadInfo, omaInfo);
+ } else {
+ sendInstallNotificationAndNextStep(
+ omaInfo, downloadInfo, DOWNLOAD_STATUS_USER_CANCELLED);
+ }
+ }
+ };
+ new AlertDialog.Builder(ApplicationStatus.getLastTrackedFocusedActivity())
+ .setTitle(R.string.proceed_oma_download_message)
+ .setPositiveButton(R.string.ok, clickListener)
+ .setNegativeButton(R.string.cancel, clickListener)
+ .setView(v)
+ .setCancelable(false)
+ .show();
+ }
+
+ /**
+ * Shows a warning dialog indicating that download has failed. When user confirms
+ * the warning, a message will be sent to the notification server to inform about the
+ * error.
+ *
+ * @param titleId The resource identifier for the title.
+ * @param omaInfo Information about the OMA content.
+ * @param downloadInfo Information about the download.
+ * @param statusMessage Message to be sent to the notification server.
+ */
+ private void showDownloadWarningDialog(
+ int titleId, final OMAInfo omaInfo, final DownloadInfo downloadInfo,
+ final String statusMessage) {
+ DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == AlertDialog.BUTTON_POSITIVE) {
+ sendInstallNotificationAndNextStep(omaInfo, downloadInfo, statusMessage);
+ }
+ }
+ };
+ new AlertDialog.Builder(ApplicationStatus.getLastTrackedFocusedActivity())
+ .setTitle(titleId)
+ .setPositiveButton(R.string.ok, clickListener)
+ .setCancelable(false)
+ .show();
+ }
+
+ /**
+ * Shows a dialog to ask whether user wants to open the nextURL.
+ *
+ * @param omaInfo Information about the OMA content.
+ */
+ private void showNextUrlDialog(OMAInfo omaInfo) {
+ if (omaInfo.isValueEmpty(OMA_NEXT_URL)) {
+ return;
+ }
+ final String nextUrl = omaInfo.getValue(OMA_NEXT_URL);
+ LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ View v = inflater.inflate(R.layout.next_url_post_oma_download, null);
+ TextView textView = (TextView) v.findViewById(R.id.oma_download_next_url);
+ textView.setText(nextUrl);
+ final Activity activity = ApplicationStatus.getLastTrackedFocusedActivity();
+ DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == AlertDialog.BUTTON_POSITIVE) {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(nextUrl));
+ intent.putExtra(Browser.EXTRA_APPLICATION_ID, activity.getPackageName());
+ intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
+ intent.setPackage(mContext.getPackageName());
+ activity.startActivity(intent);
+ }
+ }
+ };
+ new AlertDialog.Builder(activity)
+ .setTitle(R.string.open_url_post_oma_download)
+ .setPositiveButton(R.string.ok, clickListener)
+ .setNegativeButton(R.string.cancel, clickListener)
+ .setView(v)
+ .setCancelable(false)
+ .show();
+ }
+
+ /**
+ * Returns the first MIME type in the OMA download that can be opened on the device.
+ *
+ * @param pm PackageManger for the current context.
+ * @param omaInfo Information about the OMA content.
+ * @return the MIME type can be opened by the device.
+ */
+ static String getOpennableType(PackageManager pm, OMAInfo omaInfo) {
+ if (omaInfo.isValueEmpty(OMA_OBJECT_URI)) {
+ return null;
+ }
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ Uri uri = Uri.parse(omaInfo.getValue(OMA_OBJECT_URI));
+ for (String type : omaInfo.getTypes()) {
+ if (!type.equalsIgnoreCase(OMA_DRM_MESSAGE_MIME)
+ && !type.equalsIgnoreCase(OMA_DRM_CONTENT_MIME)
+ && !type.equalsIgnoreCase(OMA_DOWNLOAD_DESCRIPTOR_MIME)
+ && !type.equalsIgnoreCase(OMA_DRM_RIGHTS_MIME)) {
+ intent.setDataAndType(uri, type);
+ ResolveInfo resolveInfo = pm.resolveActivity(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ if (resolveInfo != null) {
+ return type;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Parses the input stream and returns the OMA information.
+ *
+ * @param is The input stream to the parser.
+ * @return OMA information about the download content, or null if an error is found.
+ */
+ @VisibleForTesting
+ static OMAInfo parseDownloadDescriptor(InputStream is) {
+ try {
+ XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
+ factory.setNamespaceAware(true);
+ XmlPullParser parser = factory.newPullParser();
+ parser.setInput(is, null);
+ int eventType = parser.getEventType();
+ String currentAttribute = null;
+ OMAInfo info = new OMAInfo();
+ StringBuilder sb = null;
+ List<String> attributeList = new ArrayList<String>(Arrays.asList(
+ OMA_TYPE, OMA_SIZE, OMA_OBJECT_URI, OMA_INSTALL_NOTIFY_URI, OMA_NEXT_URL,
+ OMA_DD_VERSION, OMA_NAME, OMA_DESCRIPTION, OMA_VENDOR, OMA_INFO_URL,
+ OMA_ICON_URI, OMA_INSTALL_PARAM));
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_DOCUMENT) {
+ if (!info.isEmpty()) return null;
+ } else if (eventType == XmlPullParser.START_TAG) {
+ String tagName = parser.getName();
+ if (attributeList.contains(tagName)) {
+ if (currentAttribute != null) {
+ Log.w(TAG, "Nested attributes was found in the download descriptor");
+ return null;
+ }
+ sb = new StringBuilder();
+ currentAttribute = tagName;
+ }
+ } else if (eventType == XmlPullParser.END_TAG) {
+ if (currentAttribute != null) {
+ if (!currentAttribute.equals(parser.getName())) {
+ Log.w(TAG, "Nested attributes was found in the download descriptor");
+ return null;
+ }
+ info.addAttributeValue(currentAttribute, sb.toString().trim());
+ currentAttribute = null;
+ sb = null;
+ }
+ } else if (eventType == XmlPullParser.TEXT) {
+ if (currentAttribute != null) {
+ sb.append(parser.getText());
+ }
+ }
+ eventType = parser.next();
+ }
+ return info;
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "Failed to parse download descriptor.", e);
+ return null;
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to read download descriptor.", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the size of the OMA content.
+ *
+ * @param omaInfo OMA information about the download content
+ * @return size in bytes or 0 if the omaInfo doesn't contain size info.
+ */
+ @VisibleForTesting
+ protected static long getSize(OMAInfo omaInfo) {
+ String sizeString = omaInfo.getValue(OMA_SIZE);
+ try {
+ long size = sizeString == null ? 0 : Long.parseLong(sizeString.replace(",", ""));
+ return size;
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Cannot parse size information.", e);
+ }
+ return 0;
+ }
+
+ /**
+ * Enqueue a download request to the DownloadManager and starts downloading the OMA content.
+ *
+ * @param downloadId The unique identifier maintained by the Android DownloadManager.
+ * @param downloadInfo Information about the download.
+ * @param omaInfo Information about the OMA content.
+ */
+ private void downloadOMAContent(long downloadId, DownloadInfo downloadInfo, OMAInfo omaInfo) {
+ if (omaInfo == null) return;
+ String mimeType = omaInfo.getDrmType();
+ if (mimeType == null) {
+ mimeType = getOpennableType(mContext.getPackageManager(), omaInfo);
+ }
+ DownloadInfo newInfo = DownloadInfo.Builder.fromDownloadInfo(downloadInfo)
+ .setFileName(omaInfo.getValue(OMA_NAME))
+ .setUrl(omaInfo.getValue(OMA_OBJECT_URI))
+ .setMimeType(mimeType)
+ .setDownloadId((int) downloadId)
+ .setDescription(omaInfo.getValue(OMA_DESCRIPTION))
+ .setContentLength(getSize(omaInfo))
+ .build();
+ // If installNotifyURI is not empty, the downloaded content cannot
+ // be used until the PostStatusTask gets a 200-series response.
+ // Don't show complete notification until that happens.
+ DownloadManagerService.getDownloadManagerService(mContext)
+ .enqueueDownloadManagerRequest(
+ newInfo, omaInfo.isValueEmpty(OMA_INSTALL_NOTIFY_URI));
+ mPendingOMADownloads.put(downloadId, omaInfo);
+ }
+
+ /**
+ * Checks if an OMA download is currently pending.
+ *
+ * @param downloadId Download identifier.
+ * @return true if the download is in progress, or false otherwise.
+ */
+ public boolean isPendingOMADownload(long downloadId) {
+ return mPendingOMADownloads.get(downloadId) != null;
+ }
+
+ /**
+ * Updates the download information with the new download Id.
+ *
+ * @param downloadInfo Information about the download.
+ * @param newDownloadId New download Id from the DownloadManager.
+ * @return the new download information with the new Id.
+ */
+ public DownloadInfo updateDownloadInfo(DownloadInfo downloadInfo, long newDownloadId) {
+ long oldDownloadId = (long) downloadInfo.getDownloadId();
+ OMAInfo omaInfo = mPendingOMADownloads.get(oldDownloadId);
+ mPendingOMADownloads.remove(oldDownloadId);
+ mPendingOMADownloads.put(newDownloadId, omaInfo);
+ return DownloadInfo.Builder.fromDownloadInfo(downloadInfo)
+ .setDownloadId((int) newDownloadId)
+ .build();
+ }
+
+ /**
+ * Returns the installation notification URI for the OMA download.
+ *
+ * @param downloadId Download Identifier.
+ * @return String containing the installNotifyURI.
+ */
+ public String getInstallNotifyInfo(long downloadId) {
+ OMAInfo omaInfo = mPendingOMADownloads.get(downloadId);
+ return omaInfo.getValue(OMA_INSTALL_NOTIFY_URI);
+ }
+
+ /**
+ * This class is responsible for posting the status message to the notification server.
+ */
+ private class PostStatusTask extends AsyncTask<Void, Void, Boolean> {
+ private static final String TAG = "PostStatusTask";
+ private final OMAInfo mOMAInfo;
+ private final DownloadInfo mDownloadInfo;
+ private final String mStatusMessage;
+
+ public PostStatusTask(
+ OMAInfo omaInfo, DownloadInfo downloadInfo, String statusMessage) {
+ mOMAInfo = omaInfo;
+ mDownloadInfo = downloadInfo;
+ mStatusMessage = statusMessage;
+ }
+
+ @Override
+ protected Boolean doInBackground(Void...voids) {
+ HttpClient httpClient = new DefaultHttpClient();
+ HttpPost httpPost = new HttpPost(mOMAInfo.getValue(OMA_INSTALL_NOTIFY_URI));
+
+ try {
+ String userAgent = mDownloadInfo.getUserAgent();
+ if (TextUtils.isEmpty(userAgent)) {
+ userAgent = ChromiumApplication.getBrowserUserAgent();
+ }
+ httpPost.setHeader("Accept", "*/*");
+ httpPost.setHeader("User-Agent", userAgent);
+ httpPost.setHeader("Cookie", mDownloadInfo.getCookie());
+ httpPost.setEntity(new StringEntity(mStatusMessage));
+
+ // Execute HTTP Post Request
+ HttpResponse response = httpClient.execute(httpPost);
+
+ byte[] responseBody;
+ int responseCode = response.getStatusLine().getStatusCode();
+ if (responseCode == 200 || responseCode == -1) {
+ return true;
+ }
+ return false;
+ } catch (ClientProtocolException e) {
+ Log.w(TAG, "Invalid protocol detected.", e);
+ } catch (IOException e) {
+ Log.w(TAG, "Cannot connect to server.", e);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Cannot connect to server.", e);
+ }
+ return false;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean success) {
+ DownloadManager manager =
+ (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+ if (success) {
+ String path = mDownloadInfo.getFilePath();
+ if (!TextUtils.isEmpty(path)) {
+ // Move the downloaded content from the app directory to public directory.
+ File fromFile = new File(path);
+ String fileName = fromFile.getName();
+ File toFile = new File(Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS), fileName);
+ if (fromFile.renameTo(toFile)) {
+ manager.addCompletedDownload(
+ fileName, mDownloadInfo.getDescription(), false,
+ mDownloadInfo.getMimeType(), toFile.getPath(),
+ mDownloadInfo.getContentLength(), true);
+ } else if (fromFile.delete()) {
+ Log.w(TAG, "Failed to rename the file.");
+ return;
+ } else {
+ Log.w(TAG, "Failed to rename and delete the file.");
+ }
+ }
+ showNextUrlDialog(mOMAInfo);
+ } else if (mDownloadInfo.getDownloadId() > 0) {
+ // Remove the downloaded content.
+ manager.remove(mDownloadInfo.getDownloadId());
+ }
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698