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.frameworks.downloadmanagertests; 18 19import android.app.DownloadManager; 20import android.app.DownloadManager.Query; 21import android.app.DownloadManager.Request; 22import android.content.BroadcastReceiver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.IntentFilter; 26import android.database.Cursor; 27import android.net.ConnectivityManager; 28import android.net.NetworkInfo; 29import android.net.Uri; 30import android.net.wifi.WifiManager; 31import android.os.Bundle; 32import android.os.Environment; 33import android.os.ParcelFileDescriptor; 34import android.os.SystemClock; 35import android.os.ParcelFileDescriptor.AutoCloseInputStream; 36import android.provider.Settings; 37import android.test.InstrumentationTestCase; 38import android.util.Log; 39 40import java.io.DataInputStream; 41import java.io.DataOutputStream; 42import java.io.File; 43import java.io.FileInputStream; 44import java.io.FileOutputStream; 45import java.io.IOException; 46import java.net.URL; 47import java.util.concurrent.TimeoutException; 48import java.util.Collections; 49import java.util.HashSet; 50import java.util.Iterator; 51import java.util.List; 52import java.util.Random; 53import java.util.Set; 54import java.util.Vector; 55 56import junit.framework.AssertionFailedError; 57 58import coretestutils.http.MockResponse; 59import coretestutils.http.MockWebServer; 60 61/** 62 * Base class for Instrumented tests for the Download Manager. 63 */ 64public class DownloadManagerBaseTest extends InstrumentationTestCase { 65 66 protected DownloadManager mDownloadManager = null; 67 protected MockWebServer mServer = null; 68 protected String mFileType = "text/plain"; 69 protected Context mContext = null; 70 protected MultipleDownloadsCompletedReceiver mReceiver = null; 71 protected static final int DEFAULT_FILE_SIZE = 10 * 1024; // 10kb 72 protected static final int FILE_BLOCK_READ_SIZE = 1024 * 1024; 73 74 protected static final String LOG_TAG = "android.net.DownloadManagerBaseTest"; 75 protected static final int HTTP_OK = 200; 76 protected static final int HTTP_REDIRECT = 307; 77 protected static final int HTTP_PARTIAL_CONTENT = 206; 78 protected static final int HTTP_NOT_FOUND = 404; 79 protected static final int HTTP_SERVICE_UNAVAILABLE = 503; 80 protected String DEFAULT_FILENAME = "somefile.txt"; 81 82 protected static final int DEFAULT_MAX_WAIT_TIME = 2 * 60 * 1000; // 2 minutes 83 protected static final int DEFAULT_WAIT_POLL_TIME = 5 * 1000; // 5 seconds 84 85 protected static final int WAIT_FOR_DOWNLOAD_POLL_TIME = 1 * 1000; // 1 second 86 protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 5 * 60 * 1000; // 5 minutes 87 protected static final int MAX_WAIT_FOR_LARGE_DOWNLOAD_TIME = 15 * 60 * 1000; // 15 minutes 88 89 protected static final int DOWNLOAD_TO_SYSTEM_CACHE = 1; 90 protected static final int DOWNLOAD_TO_DOWNLOAD_CACHE_DIR = 2; 91 92 // Just a few popular file types used to return from a download 93 protected enum DownloadFileType { 94 PLAINTEXT, 95 APK, 96 GIF, 97 GARBAGE, 98 UNRECOGNIZED, 99 ZIP 100 } 101 102 protected enum DataType { 103 TEXT, 104 BINARY 105 } 106 107 public static class LoggingRng extends Random { 108 109 /** 110 * Constructor 111 * 112 * Creates RNG with self-generated seed value. 113 */ 114 public LoggingRng() { 115 this(SystemClock.uptimeMillis()); 116 } 117 118 /** 119 * Constructor 120 * 121 * Creats RNG with given initial seed value 122 123 * @param seed The initial seed value 124 */ 125 public LoggingRng(long seed) { 126 super(seed); 127 Log.i(LOG_TAG, "Seeding RNG with value: " + seed); 128 } 129 } 130 131 public static class MultipleDownloadsCompletedReceiver extends BroadcastReceiver { 132 private volatile int mNumDownloadsCompleted = 0; 133 private Set<Long> downloadIds = Collections.synchronizedSet(new HashSet<Long>()); 134 135 /** 136 * {@inheritDoc} 137 */ 138 @Override 139 public void onReceive(Context context, Intent intent) { 140 if (intent.getAction().equalsIgnoreCase(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) { 141 synchronized(this) { 142 long id = intent.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID); 143 Log.i(LOG_TAG, "Received Notification for download: " + id); 144 if (!downloadIds.contains(id)) { 145 ++mNumDownloadsCompleted; 146 Log.i(LOG_TAG, "MultipleDownloadsCompletedReceiver got intent: " + 147 intent.getAction() + " --> total count: " + mNumDownloadsCompleted); 148 downloadIds.add(id); 149 150 DownloadManager dm = (DownloadManager)context.getSystemService( 151 Context.DOWNLOAD_SERVICE); 152 153 Cursor cursor = dm.query(new Query().setFilterById(id)); 154 try { 155 if (cursor.moveToFirst()) { 156 int status = cursor.getInt(cursor.getColumnIndex( 157 DownloadManager.COLUMN_STATUS)); 158 Log.i(LOG_TAG, "Download status is: " + status); 159 } else { 160 fail("No status found for completed download!"); 161 } 162 } finally { 163 cursor.close(); 164 } 165 } else { 166 Log.i(LOG_TAG, "Notification for id: " + id + " has already been made."); 167 } 168 } 169 } 170 } 171 172 /** 173 * Gets the number of times the {@link #onReceive} callback has been called for the 174 * {@link DownloadManager.ACTION_DOWNLOAD_COMPLETED} action, indicating the number of 175 * downloads completed thus far. 176 * 177 * @return the number of downloads completed so far. 178 */ 179 public int numDownloadsCompleted() { 180 return mNumDownloadsCompleted; 181 } 182 183 /** 184 * Gets the list of download IDs. 185 * @return A Set<Long> with the ids of the completed downloads. 186 */ 187 public Set<Long> getDownloadIds() { 188 synchronized(this) { 189 Set<Long> returnIds = new HashSet<Long>(downloadIds); 190 return returnIds; 191 } 192 } 193 194 } 195 196 public static class WiFiChangedReceiver extends BroadcastReceiver { 197 private Context mContext = null; 198 199 /** 200 * Constructor 201 * 202 * Sets the current state of WiFi. 203 * 204 * @param context The current app {@link Context}. 205 */ 206 public WiFiChangedReceiver(Context context) { 207 mContext = context; 208 } 209 210 /** 211 * {@inheritDoc} 212 */ 213 @Override 214 public void onReceive(Context context, Intent intent) { 215 if (intent.getAction().equalsIgnoreCase(ConnectivityManager.CONNECTIVITY_ACTION)) { 216 Log.i(LOG_TAG, "ConnectivityManager state change: " + intent.getAction()); 217 synchronized (this) { 218 this.notify(); 219 } 220 } 221 } 222 223 /** 224 * Gets the current state of WiFi. 225 * 226 * @return Returns true if WiFi is on, false otherwise. 227 */ 228 public boolean getWiFiIsOn() { 229 ConnectivityManager connManager = (ConnectivityManager)mContext.getSystemService( 230 Context.CONNECTIVITY_SERVICE); 231 NetworkInfo info = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 232 Log.i(LOG_TAG, "WiFi Connection state is currently: " + info.isConnected()); 233 return info.isConnected(); 234 } 235 } 236 237 /** 238 * {@inheritDoc} 239 */ 240 @Override 241 public void setUp() throws Exception { 242 mContext = getInstrumentation().getContext(); 243 mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE); 244 mServer = new MockWebServer(); 245 mReceiver = registerNewMultipleDownloadsReceiver(); 246 // Note: callers overriding this should call mServer.play() with the desired port # 247 } 248 249 /** 250 * Helper to enqueue a response from the MockWebServer. 251 * 252 * @param status The HTTP status code to return for this response 253 * @param body The body to return in this response 254 * @return Returns the mock web server response that was queued (which can be modified) 255 */ 256 private MockResponse enqueueResponse(int status, byte[] body) { 257 return doEnqueueResponse(status).setBody(body); 258 259 } 260 261 /** 262 * Helper to enqueue a response from the MockWebServer. 263 * 264 * @param status The HTTP status code to return for this response 265 * @param bodyFile The body to return in this response 266 * @return Returns the mock web server response that was queued (which can be modified) 267 */ 268 private MockResponse enqueueResponse(int status, File bodyFile) { 269 return doEnqueueResponse(status).setBody(bodyFile); 270 } 271 272 /** 273 * Helper for enqueue'ing a response from the MockWebServer. 274 * 275 * @param status The HTTP status code to return for this response 276 * @return Returns the mock web server response that was queued (which can be modified) 277 */ 278 private MockResponse doEnqueueResponse(int status) { 279 MockResponse response = new MockResponse().setResponseCode(status); 280 response.addHeader("Content-type", mFileType); 281 mServer.enqueue(response); 282 return response; 283 } 284 285 /** 286 * Helper to generate a random blob of bytes using a given RNG. 287 * 288 * @param size The size of the data to generate 289 * @param type The type of data to generate: currently, one of {@link DataType.TEXT} or 290 * {@link DataType.BINARY}. 291 * @param rng (optional) The RNG to use; pass null to use 292 * @return The random data that is generated. 293 */ 294 private byte[] generateData(int size, DataType type, Random rng) { 295 int min = Byte.MIN_VALUE; 296 int max = Byte.MAX_VALUE; 297 298 // Only use chars in the HTTP ASCII printable character range for Text 299 if (type == DataType.TEXT) { 300 min = 32; 301 max = 126; 302 } 303 byte[] result = new byte[size]; 304 Log.i(LOG_TAG, "Generating data of size: " + size); 305 306 if (rng == null) { 307 rng = new LoggingRng(); 308 } 309 310 for (int i = 0; i < size; ++i) { 311 result[i] = (byte) (min + rng.nextInt(max - min + 1)); 312 } 313 return result; 314 } 315 316 /** 317 * Helper to verify the size of a file. 318 * 319 * @param pfd The input file to compare the size of 320 * @param size The expected size of the file 321 */ 322 protected void verifyFileSize(ParcelFileDescriptor pfd, long size) { 323 assertEquals(pfd.getStatSize(), size); 324 } 325 326 /** 327 * Helper to verify the contents of a downloaded file versus a byte[]. 328 * 329 * @param actual The file of whose contents to verify 330 * @param expected The data we expect to find in the aforementioned file 331 * @throws IOException if there was a problem reading from the file 332 */ 333 private void verifyFileContents(ParcelFileDescriptor actual, byte[] expected) 334 throws IOException { 335 AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(actual); 336 long fileSize = actual.getStatSize(); 337 338 assertTrue(fileSize <= Integer.MAX_VALUE); 339 assertEquals(expected.length, fileSize); 340 341 byte[] actualData = new byte[expected.length]; 342 assertEquals(input.read(actualData), fileSize); 343 compareByteArrays(actualData, expected); 344 } 345 346 /** 347 * Helper to compare 2 byte arrays. 348 * 349 * @param actual The array whose data we want to verify 350 * @param expected The array of data we expect to see 351 */ 352 private void compareByteArrays(byte[] actual, byte[] expected) { 353 assertEquals(actual.length, expected.length); 354 int length = actual.length; 355 for (int i = 0; i < length; ++i) { 356 // assert has a bit of overhead, so only do the assert when the values are not the same 357 if (actual[i] != expected[i]) { 358 fail("Byte arrays are not equal."); 359 } 360 } 361 } 362 363 /** 364 * Gets the MIME content string for a given type 365 * 366 * @param type The MIME type to return 367 * @return the String representation of that MIME content type 368 */ 369 protected String getMimeMapping(DownloadFileType type) { 370 switch (type) { 371 case APK: 372 return "application/vnd.android.package-archive"; 373 case GIF: 374 return "image/gif"; 375 case ZIP: 376 return "application/x-zip-compressed"; 377 case GARBAGE: 378 return "zip\\pidy/doo/da"; 379 case UNRECOGNIZED: 380 return "application/new.undefined.type.of.app"; 381 } 382 return "text/plain"; 383 } 384 385 /** 386 * Gets the Uri that should be used to access the mock server 387 * 388 * @param filename The name of the file to try to retrieve from the mock server 389 * @return the Uri to use for access the file on the mock server 390 */ 391 private Uri getServerUri(String filename) throws Exception { 392 URL url = mServer.getUrl("/" + filename); 393 return Uri.parse(url.toString()); 394 } 395 396 /** 397 * Helper to create and register a new MultipleDownloadCompletedReciever 398 * 399 * This is used to track many simultaneous downloads by keeping count of all the downloads 400 * that have completed. 401 * 402 * @return A new receiver that records and can be queried on how many downloads have completed. 403 */ 404 protected MultipleDownloadsCompletedReceiver registerNewMultipleDownloadsReceiver() { 405 MultipleDownloadsCompletedReceiver receiver = new MultipleDownloadsCompletedReceiver(); 406 mContext.registerReceiver(receiver, new IntentFilter( 407 DownloadManager.ACTION_DOWNLOAD_COMPLETE)); 408 return receiver; 409 } 410 411 /** 412 * Enables or disables WiFi. 413 * 414 * Note: Needs the following permissions: 415 * android.permission.ACCESS_WIFI_STATE 416 * android.permission.CHANGE_WIFI_STATE 417 * @param enable true if it should be enabled, false if it should be disabled 418 */ 419 protected void setWiFiStateOn(boolean enable) throws Exception { 420 Log.i(LOG_TAG, "Setting WiFi State to: " + enable); 421 WifiManager manager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE); 422 423 manager.setWifiEnabled(enable); 424 425 String timeoutMessage = "Timed out waiting for Wifi to be " 426 + (enable ? "enabled!" : "disabled!"); 427 428 WiFiChangedReceiver receiver = new WiFiChangedReceiver(mContext); 429 mContext.registerReceiver(receiver, new IntentFilter( 430 ConnectivityManager.CONNECTIVITY_ACTION)); 431 432 synchronized (receiver) { 433 long timeoutTime = SystemClock.elapsedRealtime() + DEFAULT_MAX_WAIT_TIME; 434 boolean timedOut = false; 435 436 while (receiver.getWiFiIsOn() != enable && !timedOut) { 437 try { 438 receiver.wait(DEFAULT_WAIT_POLL_TIME); 439 440 if (SystemClock.elapsedRealtime() > timeoutTime) { 441 timedOut = true; 442 } 443 } 444 catch (InterruptedException e) { 445 // ignore InterruptedExceptions 446 } 447 } 448 if (timedOut) { 449 fail(timeoutMessage); 450 } 451 } 452 assertEquals(enable, receiver.getWiFiIsOn()); 453 } 454 455 /** 456 * Helper to enables or disables airplane mode. If successful, it also broadcasts an intent 457 * indicating that the mode has changed. 458 * 459 * Note: Needs the following permission: 460 * android.permission.WRITE_SETTINGS 461 * @param enable true if airplane mode should be ON, false if it should be OFF 462 */ 463 protected void setAirplaneModeOn(boolean enable) throws Exception { 464 int state = enable ? 1 : 0; 465 466 // Change the system setting 467 Settings.System.putInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 468 state); 469 470 String timeoutMessage = "Timed out waiting for airplane mode to be " + 471 (enable ? "enabled!" : "disabled!"); 472 473 // wait for airplane mode to change state 474 int currentWaitTime = 0; 475 while (Settings.System.getInt(mContext.getContentResolver(), 476 Settings.System.AIRPLANE_MODE_ON, -1) != state) { 477 timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME, DEFAULT_MAX_WAIT_TIME, 478 timeoutMessage); 479 } 480 481 // Post the intent 482 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); 483 intent.putExtra("state", true); 484 mContext.sendBroadcast(intent); 485 } 486 487 /** 488 * Helper to wait for a particular download to finish, or else a timeout to occur 489 * 490 * Does not wait for a receiver notification of the download. 491 * 492 * @param id The download id to query on (wait for) 493 */ 494 protected void waitForDownloadOrTimeout_skipNotification(long id) throws TimeoutException, 495 InterruptedException { 496 doWaitForDownloadsOrTimeout(new Query().setFilterById(id), 497 WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME); 498 } 499 500 /** 501 * Helper to wait for a particular download to finish, or else a timeout to occur 502 * 503 * Also guarantees a notification has been posted for the download. 504 * 505 * @param id The download id to query on (wait for) 506 */ 507 protected void waitForDownloadOrTimeout(long id) throws TimeoutException, 508 InterruptedException { 509 waitForDownloadOrTimeout(id, WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME); 510 } 511 512 /** 513 * Helper to wait for a particular download to finish, or else a timeout to occur 514 * 515 * Also guarantees a notification has been posted for the download. 516 * 517 * @param id The download id to query on (wait for) 518 * @param poll The amount of time to wait 519 * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete 520 */ 521 protected void waitForDownloadOrTimeout(long id, long poll, long timeoutMillis) 522 throws TimeoutException, InterruptedException { 523 doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis); 524 waitForReceiverNotifications(1); 525 } 526 527 /** 528 * Helper to wait for all downloads to finish, or else a specified timeout to occur 529 * 530 * Makes no guaranee that notifications have been posted for all downloads. 531 * 532 * @param poll The amount of time to wait 533 * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete 534 */ 535 protected void waitForDownloadsOrTimeout(long poll, long timeoutMillis) throws TimeoutException, 536 InterruptedException { 537 doWaitForDownloadsOrTimeout(new Query(), poll, timeoutMillis); 538 } 539 540 /** 541 * Helper to wait for all downloads to finish, or else a timeout to occur, but does not throw 542 * 543 * Also guarantees a notification has been posted for the download. 544 * 545 * @param id The id of the download to query against 546 * @param poll The amount of time to wait 547 * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete 548 * @return true if download completed successfully (didn't timeout), false otherwise 549 */ 550 private boolean waitForDownloadOrTimeoutNoThrow(long id, long poll, long timeoutMillis) { 551 try { 552 doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis); 553 waitForReceiverNotifications(1); 554 } catch (TimeoutException e) { 555 return false; 556 } 557 return true; 558 } 559 560 /** 561 * Helper function to synchronously wait, or timeout if the maximum threshold has been exceeded. 562 * 563 * @param currentTotalWaitTime The total time waited so far 564 * @param poll The amount of time to wait 565 * @param maxTimeoutMillis The total wait time threshold; if we've waited more than this long, 566 * we timeout and fail 567 * @param timedOutMessage The message to display in the failure message if we timeout 568 * @return The new total amount of time we've waited so far 569 * @throws TimeoutException if timed out waiting for SD card to mount 570 */ 571 private int timeoutWait(int currentTotalWaitTime, long poll, long maxTimeoutMillis, 572 String timedOutMessage) throws TimeoutException { 573 long now = SystemClock.elapsedRealtime(); 574 long end = now + poll; 575 576 // if we get InterruptedException's, ignore them and just keep sleeping 577 while (now < end) { 578 try { 579 Thread.sleep(end - now); 580 } catch (InterruptedException e) { 581 // ignore interrupted exceptions 582 } 583 now = SystemClock.elapsedRealtime(); 584 } 585 586 currentTotalWaitTime += poll; 587 if (currentTotalWaitTime > maxTimeoutMillis) { 588 throw new TimeoutException(timedOutMessage); 589 } 590 return currentTotalWaitTime; 591 } 592 593 /** 594 * Helper to wait for all downloads to finish, or else a timeout to occur 595 * 596 * @param query The query to pass to the download manager 597 * @param poll The poll time to wait between checks 598 * @param timeoutMillis The max amount of time (in ms) to wait for the download(s) to complete 599 */ 600 private void doWaitForDownloadsOrTimeout(Query query, long poll, long timeoutMillis) 601 throws TimeoutException { 602 int currentWaitTime = 0; 603 while (true) { 604 query.setFilterByStatus(DownloadManager.STATUS_PENDING | DownloadManager.STATUS_PAUSED 605 | DownloadManager.STATUS_RUNNING); 606 Cursor cursor = mDownloadManager.query(query); 607 608 try { 609 if (cursor.getCount() == 0) { 610 Log.i(LOG_TAG, "All downloads should be done..."); 611 break; 612 } 613 currentWaitTime = timeoutWait(currentWaitTime, poll, timeoutMillis, 614 "Timed out waiting for all downloads to finish"); 615 } finally { 616 cursor.close(); 617 } 618 } 619 } 620 621 /** 622 * Synchronously waits for external store to be mounted (eg: SD Card). 623 * 624 * @throws InterruptedException if interrupted 625 * @throws Exception if timed out waiting for SD card to mount 626 */ 627 protected void waitForExternalStoreMount() throws Exception { 628 String extStorageState = Environment.getExternalStorageState(); 629 int currentWaitTime = 0; 630 while (!extStorageState.equals(Environment.MEDIA_MOUNTED)) { 631 Log.i(LOG_TAG, "Waiting for SD card..."); 632 currentWaitTime = timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME, 633 DEFAULT_MAX_WAIT_TIME, "Timed out waiting for SD Card to be ready!"); 634 extStorageState = Environment.getExternalStorageState(); 635 } 636 } 637 638 /** 639 * Synchronously waits for a download to start. 640 * 641 * @param dlRequest the download request id used by Download Manager to track the download. 642 * @throws Exception if timed out while waiting for SD card to mount 643 */ 644 protected void waitForDownloadToStart(long dlRequest) throws Exception { 645 Cursor cursor = getCursor(dlRequest); 646 try { 647 int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); 648 int value = cursor.getInt(columnIndex); 649 int currentWaitTime = 0; 650 651 while (value != DownloadManager.STATUS_RUNNING && 652 (value != DownloadManager.STATUS_FAILED) && 653 (value != DownloadManager.STATUS_SUCCESSFUL)) { 654 Log.i(LOG_TAG, "Waiting for download to start..."); 655 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME, 656 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download to start!"); 657 cursor.requery(); 658 assertTrue(cursor.moveToFirst()); 659 columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); 660 value = cursor.getInt(columnIndex); 661 } 662 assertFalse("Download failed immediately after start", 663 value == DownloadManager.STATUS_FAILED); 664 } finally { 665 cursor.close(); 666 } 667 } 668 669 /** 670 * Synchronously waits for our receiver to receive notification for a given number of 671 * downloads. 672 * 673 * @param targetNumber The number of notifications for unique downloads to wait for; pass in 674 * -1 to not wait for notification. 675 * @throws Exception if timed out while waiting 676 */ 677 private void waitForReceiverNotifications(int targetNumber) throws TimeoutException { 678 int count = mReceiver.numDownloadsCompleted(); 679 int currentWaitTime = 0; 680 681 while (count < targetNumber) { 682 Log.i(LOG_TAG, "Waiting for notification of downloads..."); 683 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME, 684 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download notifications!" 685 + " Received " + count + "notifications."); 686 count = mReceiver.numDownloadsCompleted(); 687 } 688 } 689 690 /** 691 * Synchronously waits for a file to increase in size (such as to monitor that a download is 692 * progressing). 693 * 694 * @param file The file whose size to track. 695 * @throws Exception if timed out while waiting for the file to grow in size. 696 */ 697 protected void waitForFileToGrow(File file) throws Exception { 698 int currentWaitTime = 0; 699 700 // File may not even exist yet, so wait until it does (or we timeout) 701 while (!file.exists()) { 702 Log.i(LOG_TAG, "Waiting for file to exist..."); 703 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME, 704 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be created."); 705 } 706 707 // Get original file size... 708 long originalSize = file.length(); 709 710 while (file.length() <= originalSize) { 711 Log.i(LOG_TAG, "Waiting for file to be written to..."); 712 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME, 713 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be written to."); 714 } 715 } 716 717 /** 718 * Helper to remove all downloads that are registered with the DL Manager. 719 * 720 * Note: This gives us a clean slate b/c it includes downloads that are pending, running, 721 * paused, or have completed. 722 */ 723 protected void removeAllCurrentDownloads() { 724 Log.i(LOG_TAG, "Removing all current registered downloads..."); 725 Cursor cursor = mDownloadManager.query(new Query()); 726 try { 727 if (cursor.moveToFirst()) { 728 do { 729 int index = cursor.getColumnIndex(DownloadManager.COLUMN_ID); 730 long downloadId = cursor.getLong(index); 731 732 mDownloadManager.remove(downloadId); 733 } while (cursor.moveToNext()); 734 } 735 } finally { 736 cursor.close(); 737 } 738 } 739 740 /** 741 * Helper to perform a standard enqueue of data to the mock server. 742 * download is performed to the downloads cache dir (NOT systemcache dir) 743 * 744 * @param body The body to return in the response from the server 745 */ 746 private long doStandardEnqueue(byte[] body) throws Exception { 747 // Prepare the mock server with a standard response 748 enqueueResponse(HTTP_OK, body); 749 return doCommonStandardEnqueue(); 750 } 751 752 /** 753 * Helper to perform a standard enqueue of data to the mock server. 754 * 755 * @param body The body to return in the response from the server, contained in the file 756 */ 757 private long doStandardEnqueue(File body) throws Exception { 758 // Prepare the mock server with a standard response 759 enqueueResponse(HTTP_OK, body); 760 return doCommonStandardEnqueue(); 761 } 762 763 /** 764 * Helper to do the additional steps (setting title and Uri of default filename) when 765 * doing a standard enqueue request to the server. 766 */ 767 private long doCommonStandardEnqueue() throws Exception { 768 Uri uri = getServerUri(DEFAULT_FILENAME); 769 Request request = new Request(uri).setTitle(DEFAULT_FILENAME); 770 return mDownloadManager.enqueue(request); 771 } 772 773 /** 774 * Helper to verify an int value in a Cursor 775 * 776 * @param cursor The cursor containing the query results 777 * @param columnName The name of the column to query 778 * @param expected The expected int value 779 */ 780 private void verifyInt(Cursor cursor, String columnName, int expected) { 781 int index = cursor.getColumnIndex(columnName); 782 int actual = cursor.getInt(index); 783 assertEquals(expected, actual); 784 } 785 786 /** 787 * Performs a query based on ID and returns a Cursor for the query. 788 * 789 * @param id The id of the download in DL Manager; pass -1 to query all downloads 790 * @return A cursor for the query results 791 */ 792 protected Cursor getCursor(long id) throws Exception { 793 Query query = new Query(); 794 if (id != -1) { 795 query.setFilterById(id); 796 } 797 798 Cursor cursor = mDownloadManager.query(query); 799 int currentWaitTime = 0; 800 801 try { 802 while (!cursor.moveToFirst()) { 803 Thread.sleep(DEFAULT_WAIT_POLL_TIME); 804 currentWaitTime += DEFAULT_WAIT_POLL_TIME; 805 if (currentWaitTime > DEFAULT_MAX_WAIT_TIME) { 806 fail("timed out waiting for a non-null query result"); 807 } 808 cursor.requery(); 809 } 810 } catch (Exception e) { 811 cursor.close(); 812 throw e; 813 } 814 return cursor; 815 } 816} 817