| 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());
|
| + }
|
| + }
|
| + }
|
| +}
|
|
|