OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.chrome.browser.download; |
| 6 |
| 7 import android.app.Activity; |
| 8 import android.app.AlertDialog; |
| 9 import android.app.DownloadManager; |
| 10 import android.content.Context; |
| 11 import android.content.DialogInterface; |
| 12 import android.content.Intent; |
| 13 import android.content.pm.PackageManager; |
| 14 import android.content.pm.ResolveInfo; |
| 15 import android.net.Uri; |
| 16 import android.os.AsyncTask; |
| 17 import android.os.Environment; |
| 18 import android.os.ParcelFileDescriptor; |
| 19 import android.provider.Browser; |
| 20 import android.support.v4.util.LongSparseArray; |
| 21 import android.text.TextUtils; |
| 22 import android.util.Log; |
| 23 import android.view.LayoutInflater; |
| 24 import android.view.View; |
| 25 import android.widget.TextView; |
| 26 |
| 27 import org.apache.http.HttpResponse; |
| 28 import org.apache.http.client.ClientProtocolException; |
| 29 import org.apache.http.client.HttpClient; |
| 30 import org.apache.http.client.methods.HttpPost; |
| 31 import org.apache.http.entity.StringEntity; |
| 32 import org.apache.http.impl.client.DefaultHttpClient; |
| 33 import org.chromium.base.ApplicationStatus; |
| 34 import org.chromium.base.VisibleForTesting; |
| 35 import org.chromium.chrome.R; |
| 36 import org.chromium.chrome.browser.ChromiumApplication; |
| 37 import org.chromium.content.browser.DownloadInfo; |
| 38 import org.xmlpull.v1.XmlPullParser; |
| 39 import org.xmlpull.v1.XmlPullParserException; |
| 40 import org.xmlpull.v1.XmlPullParserFactory; |
| 41 |
| 42 import java.io.File; |
| 43 import java.io.FileInputStream; |
| 44 import java.io.FileNotFoundException; |
| 45 import java.io.IOException; |
| 46 import java.io.InputStream; |
| 47 import java.util.ArrayList; |
| 48 import java.util.Arrays; |
| 49 import java.util.HashMap; |
| 50 import java.util.List; |
| 51 import java.util.Map; |
| 52 |
| 53 /** |
| 54 * This class handles OMA downloads according to the steps described in |
| 55 * http://xml.coverpages.org/OMA-Download-OTA-V10-20020620.pdf: |
| 56 * 1. Receives a download descriptor xml file. |
| 57 * 2. Parses all the contents. |
| 58 * 3. Checks device capability to see if it is able to handle the content. |
| 59 * 4. Find the objectURI value from the download descriptor and prompt user with |
| 60 * a dialog to proceed with the download. |
| 61 * 5. On positive confirmation, sends a request to the download manager. |
| 62 * 6. Once the download is completed, sends a message to the server if installNo
tifyURI |
| 63 * is present in the download descriptor. |
| 64 * 7. Prompts user with a dialog to open the NextURL specified in the download d
escriptor. |
| 65 * If steps 2 - 6 fails, a warning dialog will be prompted to the user to let hi
m |
| 66 * know the error. Steps 6-7 will be executed afterwards. |
| 67 * If installNotifyURI is present in the download descriptor, the downloaded con
tent will |
| 68 * be saved to the app directory first. If step 6 completes successfully, the co
ntent will |
| 69 * be moved to the public external storage. Otherwise, it will be removed from t
he device. |
| 70 */ |
| 71 public class OMADownloadHandler { |
| 72 private static final String TAG = "OMADownloadHandler"; |
| 73 |
| 74 // MIME types for OMA downloads. |
| 75 public static final String OMA_DOWNLOAD_DESCRIPTOR_MIME = "application/vnd.o
ma.dd+xml"; |
| 76 public static final String OMA_DRM_MESSAGE_MIME = "application/vnd.oma.drm.m
essage"; |
| 77 public static final String OMA_DRM_CONTENT_MIME = "application/vnd.oma.drm.c
ontent"; |
| 78 public static final String OMA_DRM_RIGHTS_MIME = "application/vnd.oma.drm.ri
ghts+wbxml"; |
| 79 |
| 80 // Valid download descriptor attributes. |
| 81 protected static final String OMA_TYPE = "type"; |
| 82 protected static final String OMA_SIZE = "size"; |
| 83 protected static final String OMA_OBJECT_URI = "objectURI"; |
| 84 protected static final String OMA_INSTALL_NOTIFY_URI = "installNotifyURI"; |
| 85 protected static final String OMA_NEXT_URL = "nextURL"; |
| 86 protected static final String OMA_DD_VERSION = "DDVersion"; |
| 87 protected static final String OMA_NAME = "name"; |
| 88 protected static final String OMA_DESCRIPTION = "description"; |
| 89 protected static final String OMA_VENDOR = "vendor"; |
| 90 protected static final String OMA_INFO_URL = "infoURL"; |
| 91 protected static final String OMA_ICON_URI = "iconURI"; |
| 92 protected static final String OMA_INSTALL_PARAM = "installParam"; |
| 93 |
| 94 // Error message to send to the notification server. |
| 95 private static final String DOWNLOAD_STATUS_SUCCESS = "900 Success \n\r"; |
| 96 private static final String DOWNLOAD_STATUS_INSUFFICIENT_MEMORY = |
| 97 "901 insufficient memory \n\r"; |
| 98 private static final String DOWNLOAD_STATUS_USER_CANCELLED = "902 User Cance
lled \n\r"; |
| 99 private static final String DOWNLOAD_STATUS_LOSS_OF_SERVICE = "903 Loss of S
ervice \n\r"; |
| 100 private static final String DOWNLOAD_STATUS_ATTRIBUTE_MISMATCH = "905 Attrib
ute mismatch \n\r"; |
| 101 private static final String DOWNLOAD_STATUS_INVALID_DESCRIPTOR = "906 Invali
d descriptor \n\r"; |
| 102 private static final String DOWNLOAD_STATUS_INVALID_DDVERSION = "951 Invalid
DDVersion \n\r"; |
| 103 private static final String DOWNLOAD_STATUS_DEVICE_ABORTED = "952 Device Abo
rted \n\r"; |
| 104 private static final String DOWNLOAD_STATUS_NON_ACCEPTABLE_CONTENT = |
| 105 "953 Non-Acceptable Content \n\r"; |
| 106 private static final String DOWNLOAD_STATUS_LOADER_ERROR = "954 Loader Error
\n\r"; |
| 107 |
| 108 private final Context mContext; |
| 109 private final LongSparseArray<OMAInfo> mPendingOMADownloads = |
| 110 new LongSparseArray<OMAInfo>(); |
| 111 |
| 112 /** |
| 113 * Information about the OMA content. The object is parsed from the download |
| 114 * descriptor. There can be multiple MIME types for the object. |
| 115 */ |
| 116 @VisibleForTesting |
| 117 protected static class OMAInfo { |
| 118 private final Map<String, String> mDescription; |
| 119 private final List<String> mTypes; |
| 120 |
| 121 OMAInfo() { |
| 122 mDescription = new HashMap<String, String>(); |
| 123 mTypes = new ArrayList<String>(); |
| 124 } |
| 125 |
| 126 /** |
| 127 * Inserts an attribute-value pair about the OMA content. If the attribu
te already |
| 128 * exists, the new value will replace the old one. For MIME type, it wil
l be appended |
| 129 * to the existing MIME types. |
| 130 * |
| 131 * @param attribute The attribute to be inserted. |
| 132 * @param value The new value of the attribute. |
| 133 */ |
| 134 void addAttributeValue(String attribute, String value) { |
| 135 if (attribute.equals(OMA_TYPE)) { |
| 136 mTypes.add(value); |
| 137 } else { |
| 138 // TODO(qinmin): Handle duplicate attributes |
| 139 mDescription.put(attribute, value); |
| 140 } |
| 141 } |
| 142 |
| 143 /** |
| 144 * Gets the value for an attribute. |
| 145 * |
| 146 * @param attribute The attribute to be retrieved. |
| 147 * @return value of the attribute. |
| 148 */ |
| 149 String getValue(String attribute) { |
| 150 return mDescription.get(attribute); |
| 151 } |
| 152 |
| 153 /** |
| 154 * Checks whether the value is empty for an attribute. |
| 155 * |
| 156 * @param attribute The attribute to be retrieved. |
| 157 * @return true if it is empty, or false otherwise. |
| 158 */ |
| 159 boolean isValueEmpty(String attribute) { |
| 160 return TextUtils.isEmpty(getValue(attribute)); |
| 161 } |
| 162 |
| 163 /** |
| 164 * Gets the list of MIME types of the OMA content. |
| 165 * |
| 166 * @return List of MIME types. |
| 167 */ |
| 168 List<String> getTypes() { |
| 169 return mTypes; |
| 170 } |
| 171 |
| 172 /** |
| 173 * Checks whether the information about the OMA content is empty. |
| 174 * |
| 175 * @return true if all attributes are empty, or false otherwise. |
| 176 */ |
| 177 boolean isEmpty() { |
| 178 return mDescription.isEmpty() && mTypes.isEmpty(); |
| 179 } |
| 180 |
| 181 /** |
| 182 * Gets the DRM MIME type of this object. |
| 183 * |
| 184 * @return the DRM MIME type if it is found, or null otherwise. |
| 185 */ |
| 186 String getDrmType() { |
| 187 for (String type : mTypes) { |
| 188 if (type.equalsIgnoreCase(OMA_DRM_MESSAGE_MIME) |
| 189 || type.equalsIgnoreCase(OMA_DRM_CONTENT_MIME)) { |
| 190 return type; |
| 191 } |
| 192 } |
| 193 return null; |
| 194 } |
| 195 } |
| 196 |
| 197 public OMADownloadHandler(Context context) { |
| 198 mContext = context; |
| 199 } |
| 200 |
| 201 /** |
| 202 * Starts handling the OMA download. |
| 203 * |
| 204 * @param downloadInfo The information about the download. |
| 205 * @param downloadId The unique identifier maintained by the Android Downloa
dManager. |
| 206 */ |
| 207 public void handleOMADownload(DownloadInfo downloadInfo, long downloadId) { |
| 208 OMAParserTask task = new OMAParserTask(downloadInfo, downloadId); |
| 209 task.execute(); |
| 210 } |
| 211 |
| 212 /** |
| 213 * Async task to parse an OMA download descriptor. |
| 214 */ |
| 215 private class OMAParserTask extends AsyncTask<Void, Void, OMAInfo> { |
| 216 private final DownloadInfo mDownloadInfo; |
| 217 private final long mDownloadId; |
| 218 public OMAParserTask(DownloadInfo downloadInfo, long downloadId) { |
| 219 mDownloadInfo = downloadInfo; |
| 220 mDownloadId = downloadId; |
| 221 } |
| 222 |
| 223 @Override |
| 224 public OMAInfo doInBackground(Void...voids) { |
| 225 OMAInfo omaInfo = null; |
| 226 final DownloadManager manager = |
| 227 (DownloadManager) mContext.getSystemService(Context.DOWNLOAD
_SERVICE); |
| 228 try { |
| 229 ParcelFileDescriptor fd = manager.openDownloadedFile(mDownloadId
); |
| 230 if (fd == null) return null; |
| 231 omaInfo = parseDownloadDescriptor(new FileInputStream(fd.getFile
Descriptor())); |
| 232 fd.close(); |
| 233 } catch (FileNotFoundException e) { |
| 234 Log.w(TAG, "File not found.", e); |
| 235 } catch (IOException e) { |
| 236 Log.w(TAG, "Cannot read file.", e); |
| 237 } |
| 238 return omaInfo; |
| 239 } |
| 240 |
| 241 @Override |
| 242 protected void onPostExecute(OMAInfo omaInfo) { |
| 243 if (omaInfo == null) return; |
| 244 // Send notification if required attributes are missing. |
| 245 if (omaInfo.getTypes().isEmpty() || getSize(omaInfo) <= 0 |
| 246 || omaInfo.isValueEmpty(OMA_OBJECT_URI)) { |
| 247 sendNotification(omaInfo, mDownloadInfo, DOWNLOAD_STATUS_INVALID
_DESCRIPTOR); |
| 248 return; |
| 249 } |
| 250 // Check version. Null version are treated as 1.0. |
| 251 String version = omaInfo.getValue(OMA_DD_VERSION); |
| 252 if (version != null && !version.startsWith("1.")) { |
| 253 sendNotification(omaInfo, mDownloadInfo, DOWNLOAD_STATUS_INVALID
_DDVERSION); |
| 254 return; |
| 255 } |
| 256 // Check device capabilities. |
| 257 if (Environment.getExternalStorageDirectory().getUsableSpace() < get
Size(omaInfo)) { |
| 258 showDownloadWarningDialog( |
| 259 R.string.oma_download_insufficient_memory, |
| 260 omaInfo, mDownloadInfo, DOWNLOAD_STATUS_INSUFFICIENT_MEM
ORY); |
| 261 return; |
| 262 } |
| 263 if (getOpennableType(mContext.getPackageManager(), omaInfo) == null)
{ |
| 264 showDownloadWarningDialog( |
| 265 R.string.oma_download_non_acceptable_content, |
| 266 omaInfo, mDownloadInfo, DOWNLOAD_STATUS_NON_ACCEPTABLE_C
ONTENT); |
| 267 return; |
| 268 } |
| 269 showOMAInfoDialog(mDownloadId, mDownloadInfo, omaInfo); |
| 270 } |
| 271 } |
| 272 |
| 273 /** |
| 274 * Called when the content is successfully downloaded by the Android Downloa
dManager. |
| 275 * |
| 276 * @param downloadInfo The information about the download. |
| 277 * @param notifyURI The previously saved installNotifyURI attribute. |
| 278 */ |
| 279 public void onDownloadCompleted(DownloadInfo downloadInfo, String notifyURI)
{ |
| 280 long downloadId = (long) downloadInfo.getDownloadId(); |
| 281 OMAInfo omaInfo = mPendingOMADownloads.get(downloadId); |
| 282 if (omaInfo == null) { |
| 283 omaInfo = new OMAInfo(); |
| 284 omaInfo.addAttributeValue(OMA_INSTALL_NOTIFY_URI, notifyURI); |
| 285 } |
| 286 sendInstallNotificationAndNextStep(omaInfo, downloadInfo, DOWNLOAD_STATU
S_SUCCESS); |
| 287 mPendingOMADownloads.remove(downloadId); |
| 288 } |
| 289 |
| 290 /** |
| 291 * Called when android DownloadManager fails to download the content. |
| 292 * |
| 293 * @param downloadInfo The information about the download. |
| 294 * @param reason The reason of failure. |
| 295 * @param notifyURI The previously saved installNotifyURI attribute. |
| 296 */ |
| 297 public void onDownloadFailed(DownloadInfo downloadInfo, int reason, String n
otifyURI) { |
| 298 String status = DOWNLOAD_STATUS_DEVICE_ABORTED; |
| 299 switch (reason) { |
| 300 case DownloadManager.ERROR_CANNOT_RESUME: |
| 301 status = DOWNLOAD_STATUS_LOSS_OF_SERVICE; |
| 302 break; |
| 303 case DownloadManager.ERROR_HTTP_DATA_ERROR: |
| 304 case DownloadManager.ERROR_TOO_MANY_REDIRECTS: |
| 305 case DownloadManager.ERROR_UNHANDLED_HTTP_CODE: |
| 306 status = DOWNLOAD_STATUS_LOADER_ERROR; |
| 307 break; |
| 308 case DownloadManager.ERROR_INSUFFICIENT_SPACE: |
| 309 status = DOWNLOAD_STATUS_INSUFFICIENT_MEMORY; |
| 310 break; |
| 311 default: |
| 312 break; |
| 313 } |
| 314 long downloadId = (long) downloadInfo.getDownloadId(); |
| 315 OMAInfo omaInfo = mPendingOMADownloads.get(downloadId); |
| 316 if (omaInfo == null) { |
| 317 // Just send the notification in this case. |
| 318 omaInfo = new OMAInfo(); |
| 319 omaInfo.addAttributeValue(OMA_INSTALL_NOTIFY_URI, notifyURI); |
| 320 sendInstallNotificationAndNextStep(omaInfo, downloadInfo, status); |
| 321 return; |
| 322 } |
| 323 showDownloadWarningDialog( |
| 324 R.string.oma_download_failed, omaInfo, downloadInfo, status); |
| 325 mPendingOMADownloads.remove(downloadId); |
| 326 } |
| 327 |
| 328 /** |
| 329 * Sends the install notification and then opens the nextURL if they are pro
vided. |
| 330 * If the install notification is sent, nextURL will be opened after the ser
ver |
| 331 * response is received. |
| 332 * |
| 333 * @param omaInfo Information about the OMA content. |
| 334 * @param downloadInfo Information about the download. |
| 335 * @param statusMessage The message to send to the notification server. |
| 336 */ |
| 337 private void sendInstallNotificationAndNextStep( |
| 338 OMAInfo omaInfo, DownloadInfo downloadInfo, String statusMessage) { |
| 339 if (!sendNotification(omaInfo, downloadInfo, statusMessage)) { |
| 340 showNextUrlDialog(omaInfo); |
| 341 } |
| 342 } |
| 343 |
| 344 /** |
| 345 * Sends the install notification to the server. |
| 346 * |
| 347 * @param omaInfo Information about the OMA content. |
| 348 * @param downloadInfo Information about the download. |
| 349 * @param statusMessage The message to send to the notification server. |
| 350 * @return true if the notification ise sent, or false otherwise. |
| 351 */ |
| 352 private boolean sendNotification( |
| 353 OMAInfo omaInfo, DownloadInfo downloadInfo, String statusMessage) { |
| 354 if (omaInfo == null) return false; |
| 355 if (omaInfo.isValueEmpty(OMA_INSTALL_NOTIFY_URI)) return false; |
| 356 PostStatusTask task = new PostStatusTask(omaInfo, downloadInfo, statusMe
ssage); |
| 357 task.execute(); |
| 358 return true; |
| 359 } |
| 360 |
| 361 /** |
| 362 * Shows the OMA information to the user and ask whether user want to procee
d. |
| 363 * |
| 364 * @param downloadId The unique identifier maintained by the Android Downloa
dManager. |
| 365 * @param downloadInfo Information about the download. |
| 366 * @param omaInfo Information about the OMA content. |
| 367 */ |
| 368 private void showOMAInfoDialog( |
| 369 final long downloadId, final DownloadInfo downloadInfo, final OMAInf
o omaInfo) { |
| 370 LayoutInflater inflater = |
| 371 (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLAT
ER_SERVICE); |
| 372 View v = inflater.inflate(R.layout.confirm_oma_download, null); |
| 373 |
| 374 TextView textView = (TextView) v.findViewById(R.id.oma_download_name); |
| 375 textView.setText(omaInfo.getValue(OMA_NAME)); |
| 376 textView = (TextView) v.findViewById(R.id.oma_download_vendor); |
| 377 textView.setText(omaInfo.getValue(OMA_VENDOR)); |
| 378 textView = (TextView) v.findViewById(R.id.oma_download_size); |
| 379 textView.setText(omaInfo.getValue(OMA_SIZE)); |
| 380 textView = (TextView) v.findViewById(R.id.oma_download_type); |
| 381 textView.setText(getOpennableType(mContext.getPackageManager(), omaInfo)
); |
| 382 textView = (TextView) v.findViewById(R.id.oma_download_description); |
| 383 textView.setText(omaInfo.getValue(OMA_DESCRIPTION)); |
| 384 |
| 385 DialogInterface.OnClickListener clickListener = new DialogInterface.OnCl
ickListener() { |
| 386 @Override |
| 387 public void onClick(DialogInterface dialog, int which) { |
| 388 if (which == AlertDialog.BUTTON_POSITIVE) { |
| 389 downloadOMAContent(downloadId, downloadInfo, omaInfo); |
| 390 } else { |
| 391 sendInstallNotificationAndNextStep( |
| 392 omaInfo, downloadInfo, DOWNLOAD_STATUS_USER_CANCELLE
D); |
| 393 } |
| 394 } |
| 395 }; |
| 396 new AlertDialog.Builder(ApplicationStatus.getLastTrackedFocusedActivity(
)) |
| 397 .setTitle(R.string.proceed_oma_download_message) |
| 398 .setPositiveButton(R.string.ok, clickListener) |
| 399 .setNegativeButton(R.string.cancel, clickListener) |
| 400 .setView(v) |
| 401 .setCancelable(false) |
| 402 .show(); |
| 403 } |
| 404 |
| 405 /** |
| 406 * Shows a warning dialog indicating that download has failed. When user con
firms |
| 407 * the warning, a message will be sent to the notification server to inform
about the |
| 408 * error. |
| 409 * |
| 410 * @param titleId The resource identifier for the title. |
| 411 * @param omaInfo Information about the OMA content. |
| 412 * @param downloadInfo Information about the download. |
| 413 * @param statusMessage Message to be sent to the notification server. |
| 414 */ |
| 415 private void showDownloadWarningDialog( |
| 416 int titleId, final OMAInfo omaInfo, final DownloadInfo downloadInfo, |
| 417 final String statusMessage) { |
| 418 DialogInterface.OnClickListener clickListener = new DialogInterface.OnCl
ickListener() { |
| 419 @Override |
| 420 public void onClick(DialogInterface dialog, int which) { |
| 421 if (which == AlertDialog.BUTTON_POSITIVE) { |
| 422 sendInstallNotificationAndNextStep(omaInfo, downloadInfo, st
atusMessage); |
| 423 } |
| 424 } |
| 425 }; |
| 426 new AlertDialog.Builder(ApplicationStatus.getLastTrackedFocusedActivity(
)) |
| 427 .setTitle(titleId) |
| 428 .setPositiveButton(R.string.ok, clickListener) |
| 429 .setCancelable(false) |
| 430 .show(); |
| 431 } |
| 432 |
| 433 /** |
| 434 * Shows a dialog to ask whether user wants to open the nextURL. |
| 435 * |
| 436 * @param omaInfo Information about the OMA content. |
| 437 */ |
| 438 private void showNextUrlDialog(OMAInfo omaInfo) { |
| 439 if (omaInfo.isValueEmpty(OMA_NEXT_URL)) { |
| 440 return; |
| 441 } |
| 442 final String nextUrl = omaInfo.getValue(OMA_NEXT_URL); |
| 443 LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( |
| 444 Context.LAYOUT_INFLATER_SERVICE); |
| 445 View v = inflater.inflate(R.layout.next_url_post_oma_download, null); |
| 446 TextView textView = (TextView) v.findViewById(R.id.oma_download_next_url
); |
| 447 textView.setText(nextUrl); |
| 448 final Activity activity = ApplicationStatus.getLastTrackedFocusedActivit
y(); |
| 449 DialogInterface.OnClickListener clickListener = new DialogInterface.OnCl
ickListener() { |
| 450 @Override |
| 451 public void onClick(DialogInterface dialog, int which) { |
| 452 if (which == AlertDialog.BUTTON_POSITIVE) { |
| 453 Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(nex
tUrl)); |
| 454 intent.putExtra(Browser.EXTRA_APPLICATION_ID, activity.getPa
ckageName()); |
| 455 intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true); |
| 456 intent.setPackage(mContext.getPackageName()); |
| 457 activity.startActivity(intent); |
| 458 } |
| 459 } |
| 460 }; |
| 461 new AlertDialog.Builder(activity) |
| 462 .setTitle(R.string.open_url_post_oma_download) |
| 463 .setPositiveButton(R.string.ok, clickListener) |
| 464 .setNegativeButton(R.string.cancel, clickListener) |
| 465 .setView(v) |
| 466 .setCancelable(false) |
| 467 .show(); |
| 468 } |
| 469 |
| 470 /** |
| 471 * Returns the first MIME type in the OMA download that can be opened on the
device. |
| 472 * |
| 473 * @param pm PackageManger for the current context. |
| 474 * @param omaInfo Information about the OMA content. |
| 475 * @return the MIME type can be opened by the device. |
| 476 */ |
| 477 static String getOpennableType(PackageManager pm, OMAInfo omaInfo) { |
| 478 if (omaInfo.isValueEmpty(OMA_OBJECT_URI)) { |
| 479 return null; |
| 480 } |
| 481 Intent intent = new Intent(Intent.ACTION_VIEW); |
| 482 Uri uri = Uri.parse(omaInfo.getValue(OMA_OBJECT_URI)); |
| 483 for (String type : omaInfo.getTypes()) { |
| 484 if (!type.equalsIgnoreCase(OMA_DRM_MESSAGE_MIME) |
| 485 && !type.equalsIgnoreCase(OMA_DRM_CONTENT_MIME) |
| 486 && !type.equalsIgnoreCase(OMA_DOWNLOAD_DESCRIPTOR_MIME) |
| 487 && !type.equalsIgnoreCase(OMA_DRM_RIGHTS_MIME)) { |
| 488 intent.setDataAndType(uri, type); |
| 489 ResolveInfo resolveInfo = pm.resolveActivity(intent, |
| 490 PackageManager.MATCH_DEFAULT_ONLY); |
| 491 if (resolveInfo != null) { |
| 492 return type; |
| 493 } |
| 494 } |
| 495 } |
| 496 return null; |
| 497 } |
| 498 |
| 499 /** |
| 500 * Parses the input stream and returns the OMA information. |
| 501 * |
| 502 * @param is The input stream to the parser. |
| 503 * @return OMA information about the download content, or null if an error i
s found. |
| 504 */ |
| 505 @VisibleForTesting |
| 506 static OMAInfo parseDownloadDescriptor(InputStream is) { |
| 507 try { |
| 508 XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); |
| 509 factory.setNamespaceAware(true); |
| 510 XmlPullParser parser = factory.newPullParser(); |
| 511 parser.setInput(is, null); |
| 512 int eventType = parser.getEventType(); |
| 513 String currentAttribute = null; |
| 514 OMAInfo info = new OMAInfo(); |
| 515 StringBuilder sb = null; |
| 516 List<String> attributeList = new ArrayList<String>(Arrays.asList( |
| 517 OMA_TYPE, OMA_SIZE, OMA_OBJECT_URI, OMA_INSTALL_NOTIFY_URI,
OMA_NEXT_URL, |
| 518 OMA_DD_VERSION, OMA_NAME, OMA_DESCRIPTION, OMA_VENDOR, OMA_I
NFO_URL, |
| 519 OMA_ICON_URI, OMA_INSTALL_PARAM)); |
| 520 while (eventType != XmlPullParser.END_DOCUMENT) { |
| 521 if (eventType == XmlPullParser.START_DOCUMENT) { |
| 522 if (!info.isEmpty()) return null; |
| 523 } else if (eventType == XmlPullParser.START_TAG) { |
| 524 String tagName = parser.getName(); |
| 525 if (attributeList.contains(tagName)) { |
| 526 if (currentAttribute != null) { |
| 527 Log.w(TAG, "Nested attributes was found in the downl
oad descriptor"); |
| 528 return null; |
| 529 } |
| 530 sb = new StringBuilder(); |
| 531 currentAttribute = tagName; |
| 532 } |
| 533 } else if (eventType == XmlPullParser.END_TAG) { |
| 534 if (currentAttribute != null) { |
| 535 if (!currentAttribute.equals(parser.getName())) { |
| 536 Log.w(TAG, "Nested attributes was found in the downl
oad descriptor"); |
| 537 return null; |
| 538 } |
| 539 info.addAttributeValue(currentAttribute, sb.toString().t
rim()); |
| 540 currentAttribute = null; |
| 541 sb = null; |
| 542 } |
| 543 } else if (eventType == XmlPullParser.TEXT) { |
| 544 if (currentAttribute != null) { |
| 545 sb.append(parser.getText()); |
| 546 } |
| 547 } |
| 548 eventType = parser.next(); |
| 549 } |
| 550 return info; |
| 551 } catch (XmlPullParserException e) { |
| 552 Log.w(TAG, "Failed to parse download descriptor.", e); |
| 553 return null; |
| 554 } catch (IOException e) { |
| 555 Log.w(TAG, "Failed to read download descriptor.", e); |
| 556 return null; |
| 557 } |
| 558 } |
| 559 |
| 560 /** |
| 561 * Returns the size of the OMA content. |
| 562 * |
| 563 * @param omaInfo OMA information about the download content |
| 564 * @return size in bytes or 0 if the omaInfo doesn't contain size info. |
| 565 */ |
| 566 @VisibleForTesting |
| 567 protected static long getSize(OMAInfo omaInfo) { |
| 568 String sizeString = omaInfo.getValue(OMA_SIZE); |
| 569 try { |
| 570 long size = sizeString == null ? 0 : Long.parseLong(sizeString.repla
ce(",", "")); |
| 571 return size; |
| 572 } catch (NumberFormatException e) { |
| 573 Log.w(TAG, "Cannot parse size information.", e); |
| 574 } |
| 575 return 0; |
| 576 } |
| 577 |
| 578 /** |
| 579 * Enqueue a download request to the DownloadManager and starts downloading
the OMA content. |
| 580 * |
| 581 * @param downloadId The unique identifier maintained by the Android Downloa
dManager. |
| 582 * @param downloadInfo Information about the download. |
| 583 * @param omaInfo Information about the OMA content. |
| 584 */ |
| 585 private void downloadOMAContent(long downloadId, DownloadInfo downloadInfo,
OMAInfo omaInfo) { |
| 586 if (omaInfo == null) return; |
| 587 String mimeType = omaInfo.getDrmType(); |
| 588 if (mimeType == null) { |
| 589 mimeType = getOpennableType(mContext.getPackageManager(), omaInfo); |
| 590 } |
| 591 DownloadInfo newInfo = DownloadInfo.Builder.fromDownloadInfo(downloadInf
o) |
| 592 .setFileName(omaInfo.getValue(OMA_NAME)) |
| 593 .setUrl(omaInfo.getValue(OMA_OBJECT_URI)) |
| 594 .setMimeType(mimeType) |
| 595 .setDownloadId((int) downloadId) |
| 596 .setDescription(omaInfo.getValue(OMA_DESCRIPTION)) |
| 597 .setContentLength(getSize(omaInfo)) |
| 598 .build(); |
| 599 // If installNotifyURI is not empty, the downloaded content cannot |
| 600 // be used until the PostStatusTask gets a 200-series response. |
| 601 // Don't show complete notification until that happens. |
| 602 DownloadManagerService.getDownloadManagerService(mContext) |
| 603 .enqueueDownloadManagerRequest( |
| 604 newInfo, omaInfo.isValueEmpty(OMA_INSTALL_NOTIFY_URI)); |
| 605 mPendingOMADownloads.put(downloadId, omaInfo); |
| 606 } |
| 607 |
| 608 /** |
| 609 * Checks if an OMA download is currently pending. |
| 610 * |
| 611 * @param downloadId Download identifier. |
| 612 * @return true if the download is in progress, or false otherwise. |
| 613 */ |
| 614 public boolean isPendingOMADownload(long downloadId) { |
| 615 return mPendingOMADownloads.get(downloadId) != null; |
| 616 } |
| 617 |
| 618 /** |
| 619 * Updates the download information with the new download Id. |
| 620 * |
| 621 * @param downloadInfo Information about the download. |
| 622 * @param newDownloadId New download Id from the DownloadManager. |
| 623 * @return the new download information with the new Id. |
| 624 */ |
| 625 public DownloadInfo updateDownloadInfo(DownloadInfo downloadInfo, long newDo
wnloadId) { |
| 626 long oldDownloadId = (long) downloadInfo.getDownloadId(); |
| 627 OMAInfo omaInfo = mPendingOMADownloads.get(oldDownloadId); |
| 628 mPendingOMADownloads.remove(oldDownloadId); |
| 629 mPendingOMADownloads.put(newDownloadId, omaInfo); |
| 630 return DownloadInfo.Builder.fromDownloadInfo(downloadInfo) |
| 631 .setDownloadId((int) newDownloadId) |
| 632 .build(); |
| 633 } |
| 634 |
| 635 /** |
| 636 * Returns the installation notification URI for the OMA download. |
| 637 * |
| 638 * @param downloadId Download Identifier. |
| 639 * @return String containing the installNotifyURI. |
| 640 */ |
| 641 public String getInstallNotifyInfo(long downloadId) { |
| 642 OMAInfo omaInfo = mPendingOMADownloads.get(downloadId); |
| 643 return omaInfo.getValue(OMA_INSTALL_NOTIFY_URI); |
| 644 } |
| 645 |
| 646 /** |
| 647 * This class is responsible for posting the status message to the notificat
ion server. |
| 648 */ |
| 649 private class PostStatusTask extends AsyncTask<Void, Void, Boolean> { |
| 650 private static final String TAG = "PostStatusTask"; |
| 651 private final OMAInfo mOMAInfo; |
| 652 private final DownloadInfo mDownloadInfo; |
| 653 private final String mStatusMessage; |
| 654 |
| 655 public PostStatusTask( |
| 656 OMAInfo omaInfo, DownloadInfo downloadInfo, String statusMessage
) { |
| 657 mOMAInfo = omaInfo; |
| 658 mDownloadInfo = downloadInfo; |
| 659 mStatusMessage = statusMessage; |
| 660 } |
| 661 |
| 662 @Override |
| 663 protected Boolean doInBackground(Void...voids) { |
| 664 HttpClient httpClient = new DefaultHttpClient(); |
| 665 HttpPost httpPost = new HttpPost(mOMAInfo.getValue(OMA_INSTALL_NOTIF
Y_URI)); |
| 666 |
| 667 try { |
| 668 String userAgent = mDownloadInfo.getUserAgent(); |
| 669 if (TextUtils.isEmpty(userAgent)) { |
| 670 userAgent = ChromiumApplication.getBrowserUserAgent(); |
| 671 } |
| 672 httpPost.setHeader("Accept", "*/*"); |
| 673 httpPost.setHeader("User-Agent", userAgent); |
| 674 httpPost.setHeader("Cookie", mDownloadInfo.getCookie()); |
| 675 httpPost.setEntity(new StringEntity(mStatusMessage)); |
| 676 |
| 677 // Execute HTTP Post Request |
| 678 HttpResponse response = httpClient.execute(httpPost); |
| 679 |
| 680 byte[] responseBody; |
| 681 int responseCode = response.getStatusLine().getStatusCode(); |
| 682 if (responseCode == 200 || responseCode == -1) { |
| 683 return true; |
| 684 } |
| 685 return false; |
| 686 } catch (ClientProtocolException e) { |
| 687 Log.w(TAG, "Invalid protocol detected.", e); |
| 688 } catch (IOException e) { |
| 689 Log.w(TAG, "Cannot connect to server.", e); |
| 690 } catch (IllegalStateException e) { |
| 691 Log.w(TAG, "Cannot connect to server.", e); |
| 692 } |
| 693 return false; |
| 694 } |
| 695 |
| 696 @Override |
| 697 protected void onPostExecute(Boolean success) { |
| 698 DownloadManager manager = |
| 699 (DownloadManager) mContext.getSystemService(Context.DOWNLOAD
_SERVICE); |
| 700 if (success) { |
| 701 String path = mDownloadInfo.getFilePath(); |
| 702 if (!TextUtils.isEmpty(path)) { |
| 703 // Move the downloaded content from the app directory to pub
lic directory. |
| 704 File fromFile = new File(path); |
| 705 String fileName = fromFile.getName(); |
| 706 File toFile = new File(Environment.getExternalStoragePublicD
irectory( |
| 707 Environment.DIRECTORY_DOWNLOADS), fileName); |
| 708 if (fromFile.renameTo(toFile)) { |
| 709 manager.addCompletedDownload( |
| 710 fileName, mDownloadInfo.getDescription(), false, |
| 711 mDownloadInfo.getMimeType(), toFile.getPath(), |
| 712 mDownloadInfo.getContentLength(), true); |
| 713 } else if (fromFile.delete()) { |
| 714 Log.w(TAG, "Failed to rename the file."); |
| 715 return; |
| 716 } else { |
| 717 Log.w(TAG, "Failed to rename and delete the file."); |
| 718 } |
| 719 } |
| 720 showNextUrlDialog(mOMAInfo); |
| 721 } else if (mDownloadInfo.getDownloadId() > 0) { |
| 722 // Remove the downloaded content. |
| 723 manager.remove(mDownloadInfo.getDownloadId()); |
| 724 } |
| 725 } |
| 726 } |
| 727 } |
OLD | NEW |