1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.providers.downloads; 18 19import static android.app.DownloadManager.STATUS_FAILED; 20import static android.app.DownloadManager.STATUS_PAUSED; 21import static android.net.TrafficStats.GB_IN_BYTES; 22import static android.text.format.DateUtils.SECOND_IN_MILLIS; 23import static java.net.HttpURLConnection.HTTP_MOVED_TEMP; 24import static java.net.HttpURLConnection.HTTP_NOT_FOUND; 25import static java.net.HttpURLConnection.HTTP_OK; 26import static java.net.HttpURLConnection.HTTP_PARTIAL; 27import static java.net.HttpURLConnection.HTTP_PRECON_FAILED; 28import static java.net.HttpURLConnection.HTTP_UNAVAILABLE; 29import static org.mockito.Matchers.anyInt; 30import static org.mockito.Matchers.anyString; 31import static org.mockito.Matchers.isA; 32import static org.mockito.Mockito.atLeastOnce; 33import static org.mockito.Mockito.never; 34import static org.mockito.Mockito.times; 35import static org.mockito.Mockito.verify; 36 37import android.app.DownloadManager; 38import android.app.Notification; 39import android.app.NotificationManager; 40import android.content.Context; 41import android.content.Intent; 42import android.database.Cursor; 43import android.net.ConnectivityManager; 44import android.net.Uri; 45import android.os.Environment; 46import android.os.SystemClock; 47import android.provider.Downloads; 48import android.test.suitebuilder.annotation.LargeTest; 49import android.test.suitebuilder.annotation.Suppress; 50import android.text.format.DateUtils; 51 52import com.google.mockwebserver.MockResponse; 53import com.google.mockwebserver.RecordedRequest; 54import com.google.mockwebserver.SocketPolicy; 55 56import java.io.File; 57import java.io.FileInputStream; 58import java.io.FileNotFoundException; 59import java.io.IOException; 60import java.io.InputStream; 61import java.util.List; 62import java.util.concurrent.TimeoutException; 63 64@LargeTest 65public class PublicApiFunctionalTest extends AbstractPublicApiTest { 66 private static final String REDIRECTED_PATH = "/other_path"; 67 private static final String ETAG = "my_etag"; 68 69 protected File mTestDirectory; 70 private NotificationManager mNotifManager; 71 72 public PublicApiFunctionalTest() { 73 super(new FakeSystemFacade()); 74 } 75 76 @Override 77 protected void setUp() throws Exception { 78 super.setUp(); 79 80 mNotifManager = (NotificationManager) getContext() 81 .getSystemService(Context.NOTIFICATION_SERVICE); 82 83 mTestDirectory = new File(Environment.getExternalStorageDirectory() + File.separator 84 + "download_manager_functional_test"); 85 if (mTestDirectory.exists()) { 86 for (File file : mTestDirectory.listFiles()) { 87 file.delete(); 88 } 89 } else { 90 mTestDirectory.mkdir(); 91 } 92 } 93 94 @Override 95 protected void tearDown() throws Exception { 96 if (mTestDirectory != null && mTestDirectory.exists()) { 97 for (File file : mTestDirectory.listFiles()) { 98 file.delete(); 99 } 100 mTestDirectory.delete(); 101 } 102 super.tearDown(); 103 } 104 105 public void testBasicRequest() throws Exception { 106 enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); 107 108 Download download = enqueueRequest(getRequest()); 109 assertEquals(DownloadManager.STATUS_PENDING, 110 download.getLongField(DownloadManager.COLUMN_STATUS)); 111 assertEquals(getServerUri(REQUEST_PATH), 112 download.getStringField(DownloadManager.COLUMN_URI)); 113 assertEquals(download.mId, download.getLongField(DownloadManager.COLUMN_ID)); 114 assertEquals(mSystemFacade.currentTimeMillis(), 115 download.getLongField(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP)); 116 117 mSystemFacade.incrementTimeMillis(10); 118 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 119 RecordedRequest request = takeRequest(); 120 assertEquals("GET", request.getMethod()); 121 assertEquals(REQUEST_PATH, request.getPath()); 122 123 Uri localUri = Uri.parse(download.getStringField(DownloadManager.COLUMN_LOCAL_URI)); 124 assertEquals("content", localUri.getScheme()); 125 checkUriContent(localUri); 126 assertEquals("text/plain", download.getStringField(DownloadManager.COLUMN_MEDIA_TYPE)); 127 128 int size = FILE_CONTENT.length(); 129 assertEquals(size, download.getLongField(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); 130 assertEquals(size, download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); 131 assertEquals(mSystemFacade.currentTimeMillis(), 132 download.getLongField(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP)); 133 134 checkCompleteDownload(download); 135 } 136 137 @Suppress 138 public void testExtremelyLarge() throws Exception { 139 // NOTE: suppressed since this takes several minutes to run 140 final long length = 3 * GB_IN_BYTES; 141 final InputStream body = new FakeInputStream(length); 142 143 enqueueResponse(new MockResponse().setResponseCode(HTTP_OK).setBody(body, length) 144 .setHeader("Content-type", "text/plain") 145 .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END)); 146 147 final Download download = enqueueRequest(getRequest() 148 .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "extreme.bin")); 149 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL, 10 * DateUtils.MINUTE_IN_MILLIS); 150 151 assertEquals(length, download.getLongField(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); 152 assertEquals(length, download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); 153 } 154 155 private void checkUriContent(Uri uri) throws FileNotFoundException, IOException { 156 InputStream inputStream = mResolver.openInputStream(uri); 157 try { 158 assertEquals(FILE_CONTENT, readStream(inputStream)); 159 } finally { 160 inputStream.close(); 161 } 162 } 163 164 public void testTitleAndDescription() throws Exception { 165 Download download = enqueueRequest(getRequest() 166 .setTitle("my title") 167 .setDescription("my description")); 168 assertEquals("my title", download.getStringField(DownloadManager.COLUMN_TITLE)); 169 assertEquals("my description", 170 download.getStringField(DownloadManager.COLUMN_DESCRIPTION)); 171 } 172 173 public void testDownloadError() throws Exception { 174 enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND)); 175 runSimpleFailureTest(HTTP_NOT_FOUND); 176 } 177 178 public void testUnhandledHttpStatus() throws Exception { 179 enqueueResponse(buildEmptyResponse(1234)); // some invalid HTTP status 180 runSimpleFailureTest(DownloadManager.ERROR_UNHANDLED_HTTP_CODE); 181 } 182 183 public void testInterruptedDownload() throws Exception { 184 int initialLength = 5; 185 enqueueInterruptedDownloadResponses(initialLength); 186 187 Download download = enqueueRequest(getRequest()); 188 download.runUntilStatus(DownloadManager.STATUS_PAUSED); 189 assertEquals(initialLength, 190 download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); 191 assertEquals(FILE_CONTENT.length(), 192 download.getLongField(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); 193 takeRequest(); // get the first request out of the queue 194 195 mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS); 196 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 197 checkCompleteDownload(download); 198 199 List<String> headers = takeRequest().getHeaders(); 200 assertTrue("No Range header: " + headers, 201 headers.contains("Range: bytes=" + initialLength + "-")); 202 assertTrue("No ETag header: " + headers, headers.contains("If-Match: " + ETAG)); 203 } 204 205 public void testInterruptedExternalDownload() throws Exception { 206 enqueueInterruptedDownloadResponses(5); 207 Download download = enqueueRequest(getRequest().setDestinationUri(getExternalUri())); 208 download.runUntilStatus(DownloadManager.STATUS_PAUSED); 209 mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS); 210 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 211 checkCompleteDownload(download); 212 } 213 214 private void enqueueInterruptedDownloadResponses(int initialLength) { 215 // the first response has normal headers but unexpectedly closes after initialLength bytes 216 enqueueResponse(buildPartialResponse(0, initialLength)); 217 // the second response returns partial content for the rest of the data 218 enqueueResponse(buildPartialResponse(initialLength, FILE_CONTENT.length())); 219 } 220 221 private MockResponse buildPartialResponse(int start, int end) { 222 int totalLength = FILE_CONTENT.length(); 223 boolean isFirstResponse = (start == 0); 224 int status = isFirstResponse ? HTTP_OK : HTTP_PARTIAL; 225 MockResponse response = buildResponse(status, FILE_CONTENT.substring(start, end)) 226 .setHeader("Content-length", totalLength) 227 .setHeader("Etag", ETAG); 228 if (!isFirstResponse) { 229 response.setHeader( 230 "Content-range", "bytes " + start + "-" + totalLength + "/" + totalLength); 231 } 232 return response; 233 } 234 235 // enqueue a huge response to keep the receiveing thread in DownloadThread.java busy for a while 236 // give enough time to do something (cancel/remove etc) on that downloadrequest 237 // while it is in progress 238 private MockResponse buildContinuingResponse() { 239 int numPackets = 100; 240 int contentLength = STRING_1K.length() * numPackets; 241 return buildResponse(HTTP_OK, STRING_1K) 242 .setHeader("Content-length", contentLength) 243 .setHeader("Etag", ETAG) 244 .setBytesPerSecond(1024); 245 } 246 247 public void testFiltering() throws Exception { 248 enqueueResponse(buildEmptyResponse(HTTP_OK)); 249 enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND)); 250 251 Download download1 = enqueueRequest(getRequest()); 252 download1.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 253 254 mSystemFacade.incrementTimeMillis(1); // ensure downloads are correctly ordered by time 255 Download download2 = enqueueRequest(getRequest()); 256 download2.runUntilStatus(DownloadManager.STATUS_FAILED); 257 258 mSystemFacade.incrementTimeMillis(1); 259 Download download3 = enqueueRequest(getRequest()); 260 261 Cursor cursor = mManager.query(new DownloadManager.Query()); 262 checkAndCloseCursor(cursor, download3, download2, download1); 263 264 cursor = mManager.query(new DownloadManager.Query().setFilterById(download2.mId)); 265 checkAndCloseCursor(cursor, download2); 266 267 cursor = mManager.query(new DownloadManager.Query() 268 .setFilterByStatus(DownloadManager.STATUS_PENDING)); 269 checkAndCloseCursor(cursor, download3); 270 271 cursor = mManager.query(new DownloadManager.Query() 272 .setFilterByStatus(DownloadManager.STATUS_FAILED 273 | DownloadManager.STATUS_SUCCESSFUL)); 274 checkAndCloseCursor(cursor, download2, download1); 275 276 cursor = mManager.query(new DownloadManager.Query() 277 .setFilterByStatus(DownloadManager.STATUS_RUNNING)); 278 checkAndCloseCursor(cursor); 279 280 mSystemFacade.incrementTimeMillis(1); 281 Download invisibleDownload = enqueueRequest(getRequest().setVisibleInDownloadsUi(false)); 282 cursor = mManager.query(new DownloadManager.Query()); 283 checkAndCloseCursor(cursor, invisibleDownload, download3, download2, download1); 284 cursor = mManager.query(new DownloadManager.Query().setOnlyIncludeVisibleInDownloadsUi(true)); 285 checkAndCloseCursor(cursor, download3, download2, download1); 286 } 287 288 public void testOrdering() throws Exception { 289 enqueueResponse(buildResponse(HTTP_OK, "small contents")); 290 enqueueResponse(buildResponse(HTTP_OK, "large contents large contents")); 291 enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND)); 292 293 Download download1 = enqueueRequest(getRequest()); 294 download1.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 295 296 mSystemFacade.incrementTimeMillis(1); 297 Download download2 = enqueueRequest(getRequest()); 298 download2.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 299 300 mSystemFacade.incrementTimeMillis(1); 301 Download download3 = enqueueRequest(getRequest()); 302 download3.runUntilStatus(DownloadManager.STATUS_FAILED); 303 304 // default ordering -- by timestamp descending 305 Cursor cursor = mManager.query(new DownloadManager.Query()); 306 checkAndCloseCursor(cursor, download3, download2, download1); 307 308 cursor = mManager.query(new DownloadManager.Query() 309 .orderBy(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP, 310 DownloadManager.Query.ORDER_ASCENDING)); 311 checkAndCloseCursor(cursor, download1, download2, download3); 312 313 cursor = mManager.query(new DownloadManager.Query() 314 .orderBy(DownloadManager.COLUMN_TOTAL_SIZE_BYTES, 315 DownloadManager.Query.ORDER_DESCENDING)); 316 checkAndCloseCursor(cursor, download2, download1, download3); 317 318 cursor = mManager.query(new DownloadManager.Query() 319 .orderBy(DownloadManager.COLUMN_TOTAL_SIZE_BYTES, 320 DownloadManager.Query.ORDER_ASCENDING)); 321 checkAndCloseCursor(cursor, download3, download1, download2); 322 } 323 324 private void checkAndCloseCursor(Cursor cursor, Download... downloads) { 325 try { 326 int idIndex = cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID); 327 assertEquals(downloads.length, cursor.getCount()); 328 cursor.moveToFirst(); 329 for (Download download : downloads) { 330 assertEquals(download.mId, cursor.getLong(idIndex)); 331 cursor.moveToNext(); 332 } 333 } finally { 334 cursor.close(); 335 } 336 } 337 338 public void testInvalidUri() throws Exception { 339 try { 340 enqueueRequest(getRequest("/no_host")); 341 } catch (IllegalArgumentException exc) { // expected 342 return; 343 } 344 345 fail("No exception thrown for invalid URI"); 346 } 347 348 public void testDestination() throws Exception { 349 enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); 350 Uri destination = getExternalUri(); 351 Download download = enqueueRequest(getRequest().setDestinationUri(destination)); 352 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 353 354 Uri localUri = Uri.parse(download.getStringField(DownloadManager.COLUMN_LOCAL_URI)); 355 assertEquals(destination, localUri); 356 357 InputStream stream = new FileInputStream(destination.getPath()); 358 try { 359 assertEquals(FILE_CONTENT, readStream(stream)); 360 } finally { 361 stream.close(); 362 } 363 } 364 365 private Uri getExternalUri() { 366 return Uri.fromFile(mTestDirectory).buildUpon().appendPath("testfile.txt").build(); 367 } 368 369 public void testRequestHeaders() throws Exception { 370 enqueueResponse(buildEmptyResponse(HTTP_OK)); 371 Download download = enqueueRequest(getRequest().addRequestHeader("Header1", "value1") 372 .addRequestHeader("Header2", "value2")); 373 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 374 375 List<String> headers = takeRequest().getHeaders(); 376 assertTrue(headers.contains("Header1: value1")); 377 assertTrue(headers.contains("Header2: value2")); 378 } 379 380 public void testDelete() throws Exception { 381 Download download = enqueueRequest(getRequest().addRequestHeader("header", "value")); 382 mManager.remove(download.mId); 383 Cursor cursor = mManager.query(new DownloadManager.Query()); 384 try { 385 assertEquals(0, cursor.getCount()); 386 } finally { 387 cursor.close(); 388 } 389 } 390 391 public void testSizeLimitOverMobile() throws Exception { 392 enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); 393 enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); 394 395 mSystemFacade.mMaxBytesOverMobile = (long) FILE_CONTENT.length() - 1; 396 mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_MOBILE; 397 Download download = enqueueRequest(getRequest()); 398 download.runUntilStatus(DownloadManager.STATUS_PAUSED); 399 400 mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_WIFI; 401 // first response was read, but aborted after the DL manager processed the Content-Length 402 // header, so we need to enqueue a second one 403 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 404 } 405 406 public void testRedirect301() throws Exception { 407 RecordedRequest lastRequest = runRedirectionTest(301); 408 // for 301, upon retry/resume, we reuse the redirected URI 409 assertEquals(REDIRECTED_PATH, lastRequest.getPath()); 410 } 411 412 public void testRedirect302() throws Exception { 413 RecordedRequest lastRequest = runRedirectionTest(302); 414 // for 302, upon retry/resume, we use the original URI 415 assertEquals(REQUEST_PATH, lastRequest.getPath()); 416 } 417 418 public void testRunawayRedirect() throws Exception { 419 for (int i = 0; i < 16; i++) { 420 enqueueResponse(buildEmptyResponse(HTTP_MOVED_TEMP) 421 .setHeader("Location", mServer.getUrl("/" + i).toString())); 422 } 423 424 final Download download = enqueueRequest(getRequest()); 425 426 // Ensure that we arrive at failed download, instead of spinning forever 427 download.runUntilStatus(DownloadManager.STATUS_FAILED); 428 assertEquals(DownloadManager.ERROR_TOO_MANY_REDIRECTS, download.getReason()); 429 } 430 431 public void testRunawayUnavailable() throws Exception { 432 final int RETRY_DELAY = 120; 433 for (int i = 0; i < 16; i++) { 434 enqueueResponse( 435 buildEmptyResponse(HTTP_UNAVAILABLE).setHeader("Retry-after", RETRY_DELAY)); 436 } 437 438 final Download download = enqueueRequest(getRequest()); 439 for (int i = 0; i < Constants.MAX_RETRIES - 1; i++) { 440 download.runUntilStatus(DownloadManager.STATUS_PAUSED); 441 mSystemFacade.incrementTimeMillis((RETRY_DELAY + 60) * SECOND_IN_MILLIS); 442 } 443 444 // Ensure that we arrive at failed download, instead of spinning forever 445 download.runUntilStatus(DownloadManager.STATUS_FAILED); 446 } 447 448 public void testNoEtag() throws Exception { 449 enqueueResponse(buildPartialResponse(0, 5).removeHeader("Etag")); 450 runSimpleFailureTest(DownloadManager.ERROR_CANNOT_RESUME); 451 } 452 453 public void testEtagChanged() throws Exception { 454 final String A = "kittenz"; 455 final String B = "puppiez"; 456 457 // 1. Try downloading A, but partial result 458 enqueueResponse(buildResponse(HTTP_OK, A.substring(0, 2)) 459 .setHeader("Content-length", A.length()) 460 .setHeader("Etag", A)); 461 462 // 2. Try resuming A, but fail ETag check 463 enqueueResponse(buildEmptyResponse(HTTP_PRECON_FAILED)); 464 465 final Download download = enqueueRequest(getRequest()); 466 RecordedRequest req; 467 468 // 1. Try downloading A, but partial result 469 download.runUntilStatus(STATUS_PAUSED); 470 assertEquals(DownloadManager.PAUSED_WAITING_TO_RETRY, download.getReason()); 471 req = takeRequest(); 472 assertNull(getHeaderValue(req, "Range")); 473 assertNull(getHeaderValue(req, "If-Match")); 474 475 // 2. Try resuming A, but fail ETag check 476 mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS); 477 download.runUntilStatus(STATUS_FAILED); 478 assertEquals(HTTP_PRECON_FAILED, download.getReason()); 479 req = takeRequest(); 480 assertEquals("bytes=2-", getHeaderValue(req, "Range")); 481 assertEquals(A, getHeaderValue(req, "If-Match")); 482 } 483 484 public void testSanitizeMediaType() throws Exception { 485 enqueueResponse(buildEmptyResponse(HTTP_OK) 486 .setHeader("Content-Type", "text/html; charset=ISO-8859-4")); 487 Download download = enqueueRequest(getRequest()); 488 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 489 assertEquals("text/html", download.getStringField(DownloadManager.COLUMN_MEDIA_TYPE)); 490 } 491 492 public void testNoContentLength() throws Exception { 493 enqueueResponse(buildEmptyResponse(HTTP_OK).removeHeader("Content-length")); 494 runSimpleFailureTest(DownloadManager.ERROR_CANNOT_RESUME); 495 } 496 497 public void testInsufficientSpace() throws Exception { 498 // this would be better done by stubbing the system API to check available space, but in the 499 // meantime, just use an absurdly large header value 500 enqueueResponse(buildEmptyResponse(HTTP_OK) 501 .setHeader("Content-Length", 1024L * 1024 * 1024 * 1024 * 1024)); 502 runSimpleFailureTest(DownloadManager.ERROR_INSUFFICIENT_SPACE); 503 } 504 505 public void testCancel() throws Exception { 506 // return 'real time' from FakeSystemFacade so that DownloadThread will report progress 507 mSystemFacade.setReturnActualTime(true); 508 enqueueResponse(buildContinuingResponse()); 509 Download download = enqueueRequest(getRequest()); 510 // give the download time to get started and progress to 1% completion 511 // before cancelling it. 512 boolean rslt = download.runUntilProgress(1); 513 assertTrue(rslt); 514 mManager.remove(download.mId); 515 516 // Verify that row is removed from database 517 final long timeout = SystemClock.elapsedRealtime() + (15 * SECOND_IN_MILLIS); 518 while (download.getStatusIfExists() != -1) { 519 if (SystemClock.elapsedRealtime() > timeout) { 520 throw new TimeoutException("Row wasn't removed"); 521 } 522 SystemClock.sleep(100); 523 } 524 } 525 526 public void testDownloadCompleteBroadcast() throws Exception { 527 enqueueResponse(buildEmptyResponse(HTTP_OK)); 528 Download download = enqueueRequest(getRequest()); 529 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 530 531 assertEquals(1, mSystemFacade.mBroadcastsSent.size()); 532 Intent broadcast = mSystemFacade.mBroadcastsSent.get(0); 533 assertEquals(DownloadManager.ACTION_DOWNLOAD_COMPLETE, broadcast.getAction()); 534 assertEquals(PACKAGE_NAME, broadcast.getPackage()); 535 long intentId = broadcast.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID); 536 assertEquals(download.mId, intentId); 537 } 538 539 public void testNotificationClickedBroadcast() throws Exception { 540 Download download = enqueueRequest(getRequest()); 541 542 DownloadReceiver receiver = new DownloadReceiver(); 543 receiver.mSystemFacade = mSystemFacade; 544 Intent intent = new Intent(Constants.ACTION_LIST); 545 intent.setData(Uri.parse(Downloads.Impl.CONTENT_URI + "/" + download.mId)); 546 intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, 547 new long[] { download.mId }); 548 receiver.onReceive(mContext, intent); 549 550 assertEquals(1, mSystemFacade.mBroadcastsSent.size()); 551 Intent broadcast = mSystemFacade.mBroadcastsSent.get(0); 552 assertEquals(DownloadManager.ACTION_NOTIFICATION_CLICKED, broadcast.getAction()); 553 assertEquals(PACKAGE_NAME, broadcast.getPackage()); 554 } 555 556 public void testBasicConnectivityChanges() throws Exception { 557 enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); 558 559 // without connectivity, download immediately pauses 560 mSystemFacade.mActiveNetworkType = null; 561 Download download = enqueueRequest(getRequest()); 562 download.runUntilStatus(DownloadManager.STATUS_PAUSED); 563 564 // connecting should start the download 565 mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_WIFI; 566 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 567 } 568 569 public void testAllowedNetworkTypes() throws Exception { 570 enqueueResponse(buildEmptyResponse(HTTP_OK)); 571 enqueueResponse(buildEmptyResponse(HTTP_OK)); 572 573 mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_MOBILE; 574 575 // by default, use any connection 576 Download download = enqueueRequest(getRequest()); 577 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 578 579 // restrict a download to wifi... 580 download = enqueueRequest(getRequest() 581 .setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI)); 582 download.runUntilStatus(DownloadManager.STATUS_PAUSED); 583 // ...then enable wifi 584 mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_WIFI; 585 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 586 } 587 588 public void testRoaming() throws Exception { 589 enqueueResponse(buildEmptyResponse(HTTP_OK)); 590 enqueueResponse(buildEmptyResponse(HTTP_OK)); 591 592 mSystemFacade.mIsRoaming = true; 593 594 // by default, allow roaming 595 Download download = enqueueRequest(getRequest()); 596 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 597 598 // disallow roaming for a download... 599 download = enqueueRequest(getRequest().setAllowedOverRoaming(false)); 600 download.runUntilStatus(DownloadManager.STATUS_PAUSED); 601 // ...then turn off roaming 602 mSystemFacade.mIsRoaming = false; 603 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 604 } 605 606 public void testContentObserver() throws Exception { 607 enqueueResponse(buildEmptyResponse(HTTP_OK)); 608 mResolver.resetNotified(); 609 final Download download = enqueueRequest(getRequest()); 610 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 611 assertTrue(mResolver.mNotifyWasCalled); 612 } 613 614 public void testNotificationNever() throws Exception { 615 enqueueResponse(buildEmptyResponse(HTTP_OK)); 616 617 final Download download = enqueueRequest( 618 getRequest().setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN)); 619 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 620 621 verify(mNotifManager, times(1)).cancelAll(); 622 verify(mNotifManager, never()).notify(anyString(), anyInt(), isA(Notification.class)); 623 } 624 625 public void testNotificationVisible() throws Exception { 626 enqueueResponse(buildEmptyResponse(HTTP_OK)); 627 628 // only shows in-progress notifications 629 final Download download = enqueueRequest(getRequest()); 630 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 631 632 // TODO: verify different notif types with tags 633 verify(mNotifManager, times(1)).cancelAll(); 634 verify(mNotifManager, atLeastOnce()).notify(anyString(), anyInt(), isA(Notification.class)); 635 } 636 637 public void testNotificationVisibleComplete() throws Exception { 638 enqueueResponse(buildEmptyResponse(HTTP_OK)); 639 640 final Download download = enqueueRequest(getRequest().setNotificationVisibility( 641 DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)); 642 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 643 644 // TODO: verify different notif types with tags 645 verify(mNotifManager, times(1)).cancelAll(); 646 verify(mNotifManager, atLeastOnce()).notify(anyString(), anyInt(), isA(Notification.class)); 647 } 648 649 public void testRetryAfter() throws Exception { 650 final int delay = 120; 651 enqueueResponse( 652 buildEmptyResponse(HTTP_UNAVAILABLE).setHeader("Retry-after", delay)); 653 enqueueResponse(buildEmptyResponse(HTTP_OK)); 654 655 Download download = enqueueRequest(getRequest()); 656 download.runUntilStatus(DownloadManager.STATUS_PAUSED); 657 658 // download manager adds random 0-30s offset 659 mSystemFacade.incrementTimeMillis((delay + 31) * 1000); 660 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 661 } 662 663 public void testManyInterruptions() throws Exception { 664 final int length = FILE_CONTENT.length(); 665 for (int i = 0; i < length; i++) { 666 enqueueResponse(buildPartialResponse(i, i + 1)); 667 } 668 669 Download download = enqueueRequest(getRequest()); 670 for (int i = 0; i < length - 1; i++) { 671 download.runUntilStatus(DownloadManager.STATUS_PAUSED); 672 mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS); 673 } 674 675 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 676 checkCompleteDownload(download); 677 } 678 679 public void testExistingFile() throws Exception { 680 enqueueResponse(buildEmptyResponse(HTTP_OK)); 681 682 // download a file which already exists. 683 // downloadservice should simply create filename with "-" and a number attached 684 // at the end; i.e., download shouldnot fail. 685 Uri destination = getExternalUri(); 686 new File(destination.getPath()).createNewFile(); 687 688 Download download = enqueueRequest(getRequest().setDestinationUri(destination)); 689 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 690 } 691 692 public void testEmptyFields() throws Exception { 693 Download download = enqueueRequest(getRequest()); 694 assertEquals("", download.getStringField(DownloadManager.COLUMN_TITLE)); 695 assertEquals("", download.getStringField(DownloadManager.COLUMN_DESCRIPTION)); 696 assertNull(download.getStringField(DownloadManager.COLUMN_MEDIA_TYPE)); 697 assertEquals(0, download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); 698 assertEquals(-1, download.getLongField(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); 699 // just ensure no exception is thrown 700 download.getLongField(DownloadManager.COLUMN_REASON); 701 } 702 703 public void testRestart() throws Exception { 704 enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND)); 705 enqueueResponse(buildEmptyResponse(HTTP_OK)); 706 707 Download download = enqueueRequest(getRequest()); 708 download.runUntilStatus(DownloadManager.STATUS_FAILED); 709 710 mManager.restartDownload(download.mId); 711 assertEquals(DownloadManager.STATUS_PENDING, 712 download.getLongField(DownloadManager.COLUMN_STATUS)); 713 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 714 } 715 716 private void checkCompleteDownload(Download download) throws Exception { 717 assertEquals(FILE_CONTENT.length(), 718 download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); 719 assertEquals(FILE_CONTENT, download.getContents()); 720 } 721 722 private void runSimpleFailureTest(int expectedErrorCode) throws Exception { 723 Download download = enqueueRequest(getRequest()); 724 download.runUntilStatus(DownloadManager.STATUS_FAILED); 725 assertEquals(expectedErrorCode, 726 download.getLongField(DownloadManager.COLUMN_REASON)); 727 } 728 729 /** 730 * Run a redirection test consisting of 731 * 1) Request to REQUEST_PATH with 3xx response redirecting to another URI 732 * 2) Request to REDIRECTED_PATH with interrupted partial response 733 * 3) Resume request to complete download 734 * @return the last request sent to the server, resuming after the interruption 735 */ 736 private RecordedRequest runRedirectionTest(int status) throws Exception { 737 enqueueResponse(buildEmptyResponse(status) 738 .setHeader("Location", mServer.getUrl(REDIRECTED_PATH).toString())); 739 enqueueInterruptedDownloadResponses(5); 740 741 final Download download = enqueueRequest(getRequest()); 742 download.runUntilStatus(DownloadManager.STATUS_PAUSED); 743 mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS); 744 download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); 745 746 assertEquals(REQUEST_PATH, takeRequest().getPath()); 747 assertEquals(REDIRECTED_PATH, takeRequest().getPath()); 748 749 return takeRequest(); 750 } 751 752 /** 753 * Return value of requested HTTP header, if it exists. 754 */ 755 private static String getHeaderValue(RecordedRequest req, String header) { 756 header = header.toLowerCase() + ":"; 757 for (String h : req.getHeaders()) { 758 if (h.toLowerCase().startsWith(header)) { 759 return h.substring(header.length()).trim(); 760 } 761 } 762 return null; 763 } 764} 765