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