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