DownloadManagerFunctionalTest.java revision 82e891b3259350a92b55969a6380ca1240ee0829
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 android.app;
18
19import coretestutils.http.MockResponse;
20
21import android.app.DownloadManager.Query;
22import android.app.DownloadManager.Request;
23import android.app.DownloadManagerBaseTest.DataType;
24import android.database.Cursor;
25import android.net.Uri;
26import android.os.Environment;
27import android.os.ParcelFileDescriptor;
28import android.os.StatFs;
29import android.test.suitebuilder.annotation.LargeTest;
30import android.util.Log;
31
32import java.io.File;
33import java.io.FileOutputStream;
34import java.io.IOException;
35import java.util.Iterator;
36import java.util.Random;
37import java.util.Set;
38
39/**
40 * Integration tests of the DownloadManager API.
41 */
42public class DownloadManagerFunctionalTest extends DownloadManagerBaseTest {
43    private static final String TAG = "DownloadManagerFunctionalTest";
44    private final static String CACHE_DIR =
45            Environment.getDownloadCacheDirectory().getAbsolutePath();
46    private final static String PROHIBITED_DIRECTORY =
47            Environment.getRootDirectory().getAbsolutePath();
48
49    /**
50     * {@inheritDoc}
51     */
52    @Override
53    public void setUp() throws Exception {
54        super.setUp();
55        setWiFiStateOn(true);
56        mServer.play();
57        removeAllCurrentDownloads();
58    }
59
60    /**
61     * {@inheritDoc}
62     */
63    @Override
64    public void tearDown() throws Exception {
65        super.tearDown();
66        setWiFiStateOn(true);
67        removeAllCurrentDownloads();
68
69        if (mReceiver != null) {
70            mContext.unregisterReceiver(mReceiver);
71            mReceiver = null;
72        }
73    }
74
75    /**
76     * Verifies a particular error code was received from a download
77     *
78     * @param uri The uri to enqueue to the DownloadManager
79     * @param error The error code expected
80     * @throws Exception if the test fails
81     */
82    public void doErrorTest(Uri uri, int error) throws Exception {
83        Request request = new Request(uri);
84        request.setTitle(DEFAULT_FILENAME);
85
86        long dlRequest = mDownloadManager.enqueue(request);
87        waitForDownloadOrTimeout(dlRequest);
88
89        Cursor cursor = getCursor(dlRequest);
90        try {
91            verifyInt(cursor, DownloadManager.COLUMN_REASON, error);
92        } finally {
93            cursor.close();
94        }
95    }
96
97    /**
98     * Test a basic download of a binary file 500k in size.
99     */
100    @LargeTest
101    public void testBinaryDownloadToSystemCache() throws Exception {
102        int fileSize = 1024;
103        byte[] blobData = generateData(fileSize, DataType.BINARY);
104
105        long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
106        verifyDownload(dlRequest, blobData);
107        mDownloadManager.remove(dlRequest);
108    }
109
110    /**
111     * Tests the basic downloading of a text file 300000 bytes in size.
112     */
113    @LargeTest
114    public void testTextDownloadToSystemCache() throws Exception {
115        int fileSize = 1024;
116        byte[] blobData = generateData(fileSize, DataType.TEXT);
117
118        long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
119        verifyDownload(dlRequest, blobData);
120        mDownloadManager.remove(dlRequest);
121    }
122
123    /**
124     * Helper to verify a standard single-file download from the mock server, and clean up after
125     * verification
126     *
127     * Note that this also calls the Download manager's remove, which cleans up the file from cache.
128     *
129     * @param requestId The id of the download to remove
130     * @param fileData The data to verify the file contains
131     */
132    private void verifyDownload(long requestId, byte[] fileData)
133            throws Exception {
134        int fileSize = fileData.length;
135        ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(requestId);
136        Cursor cursor = mDownloadManager.query(new Query().setFilterById(requestId));
137        try {
138            assertEquals(1, cursor.getCount());
139            assertTrue(cursor.moveToFirst());
140
141            mServer.checkForExceptions();
142
143            verifyFileSize(pfd, fileSize);
144            verifyFileContents(pfd, fileData);
145            int colIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
146            String fileName = cursor.getString(colIndex);
147            assertTrue(fileName.startsWith(CACHE_DIR));
148        } finally {
149            pfd.close();
150            cursor.close();
151        }
152    }
153
154    /**
155     * Attempts to download several files simultaneously
156     */
157    @LargeTest
158    public void testMultipleDownloads() throws Exception {
159        // need to be sure all current downloads have stopped first
160        removeAllCurrentDownloads();
161        int NUM_FILES = 10;
162        int MAX_FILE_SIZE = 10 * 1024; // 10 kb
163
164        Random r = new LoggingRng();
165        for (int i=0; i<NUM_FILES; ++i) {
166            int size = r.nextInt(MAX_FILE_SIZE);
167            byte[] blobData = generateData(size, DataType.TEXT);
168
169            Uri uri = getServerUri(DEFAULT_FILENAME + i);
170            Request request = new Request(uri);
171            request.setTitle(String.format("%s--%d", DEFAULT_FILENAME + i, i));
172
173            // Prepare the mock server with a standard response
174            enqueueResponse(HTTP_OK, blobData);
175
176            long requestID = mDownloadManager.enqueue(request);
177        }
178
179        waitForDownloadsOrTimeout(WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
180        Cursor cursor = mDownloadManager.query(new Query());
181        try {
182            assertEquals(NUM_FILES, cursor.getCount());
183
184            if (cursor.moveToFirst()) {
185                do {
186                    int status = cursor.getInt(cursor.getColumnIndex(
187                            DownloadManager.COLUMN_STATUS));
188                    String filename = cursor.getString(cursor.getColumnIndex(
189                            DownloadManager.COLUMN_URI));
190                    String errorString = String.format(
191                            "File %s failed to download successfully. Status code: %d",
192                            filename, status);
193                    assertEquals(errorString, DownloadManager.STATUS_SUCCESSFUL, status);
194                } while (cursor.moveToNext());
195            }
196
197            assertEquals(NUM_FILES, mReceiver.numDownloadsCompleted());
198        } finally {
199            cursor.close();
200        }
201    }
202
203    /**
204     * Tests trying to download to SD card when the file with same name already exists.
205     */
206    @LargeTest
207    public void testDownloadToExternal_fileExists() throws Exception {
208        File existentFile = createFileOnSD(null, 1, DataType.TEXT, null);
209        byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
210
211        // Prepare the mock server with a standard response
212        enqueueResponse(HTTP_OK, blobData);
213
214        try {
215            Uri uri = getServerUri(DEFAULT_FILENAME);
216            Request request = new Request(uri);
217
218            Uri localUri = Uri.fromFile(existentFile);
219            request.setDestinationUri(localUri);
220
221            long dlRequest = mDownloadManager.enqueue(request);
222
223            // wait for the download to complete
224            waitForDownloadOrTimeout(dlRequest);
225            Cursor cursor = getCursor(dlRequest);
226
227            try {
228                verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_SUCCESSFUL);
229            } finally {
230                cursor.close();
231            }
232        } finally {
233            existentFile.delete();
234        }
235    }
236
237    /**
238     * Tests trying to download a file to SD card.
239     */
240    @LargeTest
241    public void testDownloadToExternal() throws Exception {
242        String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
243        File downloadedFile = new File(localDownloadDirectory, DEFAULT_FILENAME);
244        // make sure the file doesn't already exist in the directory
245        downloadedFile.delete();
246
247        try {
248            byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
249
250            // Prepare the mock server with a standard response
251            enqueueResponse(HTTP_OK, blobData);
252
253            Uri uri = getServerUri(DEFAULT_FILENAME);
254            Request request = new Request(uri);
255
256            Uri localUri = Uri.fromFile(downloadedFile);
257            request.setDestinationUri(localUri);
258
259            long dlRequest = mDownloadManager.enqueue(request);
260
261            // wait for the download to complete
262            waitForDownloadOrTimeout(dlRequest);
263
264            verifyAndCleanupSingleFileDownload(dlRequest, blobData);
265
266            assertEquals(1, mReceiver.numDownloadsCompleted());
267        } finally {
268            downloadedFile.delete();
269        }
270    }
271
272    /**
273     * Tests trying to download a file to the system partition.
274     */
275    @LargeTest
276    public void testDownloadToProhibitedDirectory() throws Exception {
277        File downloadedFile = new File(PROHIBITED_DIRECTORY, DEFAULT_FILENAME);
278        try {
279            byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
280
281            // Prepare the mock server with a standard response
282            enqueueResponse(HTTP_OK, blobData);
283
284            Uri uri = getServerUri(DEFAULT_FILENAME);
285            Request request = new Request(uri);
286
287            Uri localUri = Uri.fromFile(downloadedFile);
288            request.setDestinationUri(localUri);
289
290            try {
291                mDownloadManager.enqueue(request);
292                fail("Failed to throw SecurityException when trying to write to /system.");
293            } catch (SecurityException s) {
294                assertFalse(downloadedFile.exists());
295            }
296        } finally {
297            // Just in case file somehow got created, make sure to delete it
298            downloadedFile.delete();
299        }
300    }
301
302    /**
303     * Tests that we get the correct download ID from the download notification.
304     */
305    @LargeTest
306    public void testGetDownloadIdOnNotification() throws Exception {
307        byte[] blobData = generateData(3000, DataType.TEXT);  // file size = 3000 bytes
308
309        MockResponse response = enqueueResponse(HTTP_OK, blobData);
310        long dlRequest = doCommonStandardEnqueue();
311        waitForDownloadOrTimeout(dlRequest);
312
313        Set<Long> ids = mReceiver.getDownloadIds();
314        assertEquals(1, ids.size());
315        Iterator<Long> it = ids.iterator();
316        assertEquals("Download ID received from notification does not match initial id!",
317                dlRequest, it.next().longValue());
318    }
319
320    /**
321     * Tests the download failure error after too many redirects (>5).
322     */
323    @LargeTest
324    public void testErrorTooManyRedirects() throws Exception {
325        Uri uri = getServerUri(DEFAULT_FILENAME);
326
327        // force 6 redirects
328        for (int i = 0; i < 6; ++i) {
329            MockResponse response = enqueueResponse(HTTP_REDIRECT);
330            response.addHeader("Location", uri.toString());
331        }
332        doErrorTest(uri, DownloadManager.ERROR_TOO_MANY_REDIRECTS);
333    }
334
335    /**
336     * Tests the download failure error from an unhandled HTTP status code
337     */
338    @LargeTest
339    public void testErrorUnhandledHttpCode() throws Exception {
340        Uri uri = getServerUri(DEFAULT_FILENAME);
341        MockResponse response = enqueueResponse(HTTP_PARTIAL_CONTENT);
342
343        doErrorTest(uri, DownloadManager.ERROR_UNHANDLED_HTTP_CODE);
344    }
345
346    /**
347     * Tests the download failure error from an unhandled HTTP status code
348     */
349    @LargeTest
350    public void testErrorHttpDataError_invalidRedirect() throws Exception {
351        Uri uri = getServerUri(DEFAULT_FILENAME);
352        MockResponse response = enqueueResponse(HTTP_REDIRECT);
353        response.addHeader("Location", "://blah.blah.blah.com");
354
355        doErrorTest(uri, DownloadManager.ERROR_HTTP_DATA_ERROR);
356    }
357
358    /**
359     * Tests that we can remove a download from the download manager.
360     */
361    @LargeTest
362    public void testRemoveDownload() throws Exception {
363        int fileSize = 1024;
364        byte[] blobData = generateData(fileSize, DataType.BINARY);
365
366        long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
367        Cursor cursor = mDownloadManager.query(new Query().setFilterById(dlRequest));
368        try {
369            assertEquals("The count of downloads with this ID is not 1!", 1, cursor.getCount());
370            mDownloadManager.remove(dlRequest);
371            cursor.requery();
372            assertEquals("The count of downloads with this ID is not 0!", 0, cursor.getCount());
373        } finally {
374            cursor.close();
375        }
376    }
377
378    /**
379     * Tests that we can set the title of a download.
380     */
381    @LargeTest
382    public void testSetTitle() throws Exception {
383        int fileSize = 1024;
384        byte[] blobData = generateData(fileSize, DataType.BINARY);
385        MockResponse response = enqueueResponse(HTTP_OK, blobData);
386
387        // An arbitrary unicode string title
388        final String title = "\u00a5123;\"\u0152\u017d \u054b \u0a07 \ucce0 \u6820\u03a8\u5c34" +
389                "\uf4ad\u0da9\uc0c5\uc1a8 \uf4c5 \uf4aa\u0023\'";
390
391        Uri uri = getServerUri(DEFAULT_FILENAME);
392        Request request = new Request(uri);
393        request.setTitle(title);
394
395        long dlRequest = mDownloadManager.enqueue(request);
396        waitForDownloadOrTimeout(dlRequest);
397
398        Cursor cursor = getCursor(dlRequest);
399        try {
400            verifyString(cursor, DownloadManager.COLUMN_TITLE, title);
401        } finally {
402            cursor.close();
403        }
404    }
405
406    /**
407     * Tests that a download set for Wifi does not progress while Wifi is disabled, but resumes
408     * once Wifi is re-enabled.
409     */
410    @LargeTest
411    public void testDownloadNoWifi() throws Exception {
412        long timeout = 60 * 1000; // wait only 60 seconds before giving up
413        int fileSize = 1024;  // 140k
414        byte[] blobData = generateData(fileSize, DataType.TEXT);
415
416        setWiFiStateOn(false);
417        enqueueResponse(HTTP_OK, blobData);
418
419        try {
420            Uri uri = getServerUri(DEFAULT_FILENAME);
421            Request request = new Request(uri);
422            request.setAllowedNetworkTypes(Request.NETWORK_WIFI);
423
424            long dlRequest = mDownloadManager.enqueue(request);
425
426            // wait for the download to complete
427            boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest,
428                    WAIT_FOR_DOWNLOAD_POLL_TIME, timeout);
429            assertFalse("Download proceeded without Wifi connection!", success);
430
431            setWiFiStateOn(true);
432            waitForDownloadOrTimeout(dlRequest);
433
434            assertEquals(1, mReceiver.numDownloadsCompleted());
435        } finally {
436            setWiFiStateOn(true);
437        }
438    }
439
440    /**
441     * Tests when the server drops the connection after all headers (but before any data send).
442     */
443    @LargeTest
444    public void testDropConnection_headers() throws Exception {
445        byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
446
447        MockResponse response = enqueueResponse(HTTP_OK, blobData);
448        response.setCloseConnectionAfterHeader("content-length");
449        long dlRequest = doCommonStandardEnqueue();
450
451        // Download will never complete when header is dropped
452        boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest, DEFAULT_WAIT_POLL_TIME,
453                DEFAULT_MAX_WAIT_TIME);
454
455        assertFalse(success);
456    }
457
458    /**
459     * Tests that we get an error code when the server drops the connection during a download.
460     */
461    @LargeTest
462    public void testServerDropConnection_body() throws Exception {
463        byte[] blobData = generateData(25000, DataType.TEXT);  // file size = 25000 bytes
464
465        MockResponse response = enqueueResponse(HTTP_OK, blobData);
466        response.setCloseConnectionAfterXBytes(15382);
467        long dlRequest = doCommonStandardEnqueue();
468        waitForDownloadOrTimeout(dlRequest);
469
470        Cursor cursor = getCursor(dlRequest);
471        try {
472            verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
473            verifyInt(cursor, DownloadManager.COLUMN_REASON,
474                    DownloadManager.ERROR_CANNOT_RESUME);
475        } finally {
476            cursor.close();
477        }
478        // Even tho the server drops the connection, we should still get a completed notification
479        assertEquals(1, mReceiver.numDownloadsCompleted());
480    }
481
482    /**
483     * Tests downloading a file to system cache when there isn't enough space in the system cache
484     * to hold the entire file. DownloadManager deletes enough files to make space for the
485     * new download.
486     */
487    @LargeTest
488    public void testDownloadToCacheWithAlmostFullCache() throws Exception {
489        int DOWNLOAD_FILE_SIZE = 1024 * 1024; // 1MB
490
491        StatFs fs = new StatFs(CACHE_DIR);
492        int blockSize = fs.getBlockSize();
493        int availableBlocks = fs.getAvailableBlocks();
494        int availableBytes = blockSize * availableBlocks;
495        Log.i(TAG, "INITIAL stage, available space in /cache: " + availableBytes);
496        File outFile = File.createTempFile("DM_TEST", null, new File(CACHE_DIR));
497        byte[] buffer = new byte[blockSize];
498
499        try {
500            // fill cache to ensure we don't have enough space - take half the size of the
501            // download size, and leave that much freespace left on the cache partition
502            if (DOWNLOAD_FILE_SIZE <= availableBytes) {
503                int writeSizeBytes = availableBytes - (DOWNLOAD_FILE_SIZE / 2);
504
505                int writeSizeBlocks = writeSizeBytes / blockSize;
506                int remainderSizeBlocks = availableBlocks - writeSizeBlocks;
507
508                FileOutputStream fo = null;
509                try {
510                    fo = new FileOutputStream(outFile);
511                    while (fs.getAvailableBlocks() >= remainderSizeBlocks) {
512                        fo.write(buffer);
513                        fs.restat(CACHE_DIR);
514                    }
515                } catch (IOException e) {
516                    Log.e(LOG_TAG, "error filling file: ", e);
517                    throw e;
518                } finally {
519                    if (fo != null) {
520                        fo.close();
521                    }
522                }
523            }
524
525            // /cache should now be almost full.
526            long spaceAvailable = fs.getAvailableBlocks() * blockSize;
527            Log.i(TAG, "BEFORE download, available space in /cache: " + spaceAvailable);
528            assertTrue(DOWNLOAD_FILE_SIZE > spaceAvailable);
529
530            // try to download 1MB file into /cache - and it should succeed
531            byte[] blobData = generateData(DOWNLOAD_FILE_SIZE, DataType.TEXT);
532            long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
533            verifyAndCleanupSingleFileDownload(dlRequest, blobData);
534        } finally {
535            if (outFile != null) {
536                outFile.delete();
537            }
538        }
539    }
540
541    /**
542     * Tests that files are not deleted when DOWNLOAD_CACHE_NON_PURGEABLE is set, even if we've
543     * run out of space.
544     */
545    @LargeTest
546    public void testDownloadToCacheNonPurgeableWithFullCache() throws Exception {
547        int fileSize = 1024 * 1024; // 1MB
548        byte[] blobData = generateData(fileSize, DataType.BINARY);
549        long dlRequest = -1;
550
551        // Fill up the cache partition with DOWNLOAD_CACHE_NON_PURGEABLE downloads
552        // until 500KB is left.
553        boolean spaceAvailable = true;
554        while (spaceAvailable) {
555            // since this tests package has android.permission.DOWNLOAD_CACHE_NON_PURGEABLE
556            // permission, downloads are automatically set to be DOWNLOAD_CACHE_NON_PURGEABLE
557            dlRequest = enqueueDownloadRequest(blobData, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
558            waitForDownloadOrTimeout(dlRequest);
559
560            // Check if we've filled up the cache yet
561            StatFs fs = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
562            int availableBytes = fs.getBlockSize() * fs.getAvailableBlocks();
563            Log.i(TAG, "available space in /cache: " + availableBytes);
564            spaceAvailable = (availableBytes > fileSize) ? true : false;
565        }
566
567        // Now add one more 1MB download (should not fit in the space left over)
568        dlRequest = enqueueDownloadRequest(blobData, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
569        waitForDownloadOrTimeout(dlRequest);
570
571        // For the last download we should have failed b/c there is not enough space left in cache
572        Cursor cursor = getCursor(dlRequest);
573        try {
574            verifyInt(cursor, DownloadManager.COLUMN_REASON,
575                    DownloadManager.ERROR_INSUFFICIENT_SPACE);
576        } finally {
577            cursor.close();
578        }
579    }
580}
581