// 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.DownloadManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.provider.Browser;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.util.Log;
import android.util.LongSparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.webkit.URLUtil;
import android.widget.TextView;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeApplication;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
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) {
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);
}
manager.remove(mDownloadId);
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, DownloadItem.INVALID_DOWNLOAD_ID,
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, DownloadItem.INVALID_DOWNLOAD_ID,
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 downloadId Download Id from the Android DownloadManager.
* @param notifyURI The previously saved installNotifyURI attribute.
*/
public void onDownloadCompleted(DownloadInfo downloadInfo, long downloadId, String notifyURI) {
OMAInfo omaInfo = mPendingOMADownloads.get(downloadId);
if (omaInfo == null) {
omaInfo = new OMAInfo();
omaInfo.addAttributeValue(OMA_INSTALL_NOTIFY_URI, notifyURI);
}
sendInstallNotificationAndNextStep(
omaInfo, downloadInfo, downloadId, DOWNLOAD_STATUS_SUCCESS);
mPendingOMADownloads.remove(downloadId);
}
/**
* Called when android DownloadManager fails to download the content.
*
* @param downloadInfo The information about the download.
* @param downloadId Download Id from the Android DownloadManager.
* @param reason The reason of failure.
* @param notifyURI The previously saved installNotifyURI attribute.
*/
public void onDownloadFailed(
DownloadInfo downloadInfo, long downloadId, 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;
}
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, downloadId, 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 downloadId Id of the download in Android DownloadManager.
* @param statusMessage The message to send to the notification server.
*/
private void sendInstallNotificationAndNextStep(
OMAInfo omaInfo, DownloadInfo downloadInfo, long downloadId, String statusMessage) {
if (!sendNotification(omaInfo, downloadInfo, downloadId, statusMessage)) {
showNextUrlDialog(omaInfo);
}
}
/**
* Sends the install notification to the server.
*
* @param omaInfo Information about the OMA content.
* @param downloadInfo Information about the download.
* @param downloadId Id of the download in Android DownloadManager.
* @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, long downloadId, String statusMessage) {
if (omaInfo == null) return false;
if (omaInfo.isValueEmpty(OMA_INSTALL_NOTIFY_URI)) return false;
PostStatusTask task = new PostStatusTask(omaInfo, downloadInfo, downloadId, 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 {
sendNotification(omaInfo, downloadInfo,
DownloadItem.INVALID_DOWNLOAD_ID,
DOWNLOAD_STATUS_USER_CANCELLED);
}
}
};
new AlertDialog.Builder(
ApplicationStatus.getLastTrackedFocusedActivity(), R.style.AlertDialogTheme)
.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,
DownloadItem.INVALID_DOWNLOAD_ID, statusMessage);
}
}
};
new AlertDialog.Builder(
ApplicationStatus.getLastTrackedFocusedActivity(), R.style.AlertDialogTheme)
.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);
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)
.setMessage(nextUrl)
.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);
if (!pm.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) {
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);
}
String fileName = omaInfo.getValue(OMA_NAME);
String url = omaInfo.getValue(OMA_OBJECT_URI);
if (TextUtils.isEmpty(fileName)) {
fileName = URLUtil.guessFileName(url, null, mimeType);
}
DownloadInfo newInfo = DownloadInfo.Builder.fromDownloadInfo(downloadInfo)
.setFileName(fileName)
.setUrl(url)
.setMimeType(mimeType)
.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.
DownloadItem item = new DownloadItem(true, newInfo);
item.setSystemDownloadId(downloadId);
DownloadManagerService.getDownloadManagerService(mContext).enqueueDownloadManagerRequest(
item, 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 oldDownloadId Old download Id from the DownloadManager.
* @param newDownloadId New download Id from the DownloadManager.
*/
public void updateDownloadInfo(long oldDownloadId, long newDownloadId) {
OMAInfo omaInfo = mPendingOMADownloads.get(oldDownloadId);
mPendingOMADownloads.remove(oldDownloadId);
mPendingOMADownloads.put(newDownloadId, omaInfo);
}
/**
* 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;
private final long mDownloadId;
public PostStatusTask(
OMAInfo omaInfo, DownloadInfo downloadInfo, long downloadId, String statusMessage) {
mOMAInfo = omaInfo;
mDownloadInfo = downloadInfo;
mStatusMessage = statusMessage;
mDownloadId = downloadId;
}
@Override
protected Boolean doInBackground(Void...voids) {
HttpURLConnection urlConnection = null;
try {
URL url = new URL(mOMAInfo.getValue(OMA_INSTALL_NOTIFY_URI));
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setDoOutput(true);
urlConnection.setUseCaches(false);
urlConnection.setRequestMethod("POST");
String userAgent = mDownloadInfo.getUserAgent();
if (TextUtils.isEmpty(userAgent)) {
userAgent = ChromeApplication.getBrowserUserAgent();
}
urlConnection.setRequestProperty("User-Agent", userAgent);
urlConnection.setRequestProperty("cookie", mDownloadInfo.getCookie());
DataOutputStream dos = new DataOutputStream(urlConnection.getOutputStream());
try {
dos.writeBytes(mStatusMessage);
dos.flush();
} catch (IOException e) {
Log.w(TAG, "Cannot write status message.", e);
} finally {
dos.close();
}
int responseCode = urlConnection.getResponseCode();
if (responseCode == 200 || responseCode == -1) {
return true;
}
return false;
} catch (MalformedURLException e) {
Log.w(TAG, "Invalid notification URL.", e);
} catch (IOException e) {
Log.w(TAG, "Cannot connect to server.", e);
} catch (IllegalStateException e) {
Log.w(TAG, "Cannot connect to server.", e);
} finally {
if (urlConnection != null) urlConnection.disconnect();
}
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 (mDownloadId != DownloadItem.INVALID_DOWNLOAD_ID) {
// Remove the downloaded content.
manager.remove(mDownloadId);
}
}
}
}