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 android.app.DownloadManager.Query;
20import android.app.DownloadManager.Request;
21import android.database.Cursor;
22import android.net.Uri;
23import android.os.Environment;
24import android.os.ParcelFileDescriptor;
25import android.test.suitebuilder.annotation.LargeTest;
26
27import com.google.mockwebserver.MockResponse;
28
29import java.io.File;
30import java.util.Iterator;
31import java.util.Set;
32
33/**
34 * Integration tests of the DownloadManager API.
35 */
36public class DownloadManagerFunctionalTest extends DownloadManagerBaseTest {
37    private static final String TAG = "DownloadManagerFunctionalTest";
38    private final static String CACHE_DIR =
39            Environment.getDownloadCacheDirectory().getAbsolutePath();
40    private final static String PROHIBITED_DIRECTORY =
41            Environment.getRootDirectory().getAbsolutePath();
42
43    /**
44     * {@inheritDoc}
45     */
46    @Override
47    public void setUp() throws Exception {
48        super.setUp();
49        setWiFiStateOn(true);
50        removeAllCurrentDownloads();
51    }
52
53    /**
54     * {@inheritDoc}
55     */
56    @Override
57    public void tearDown() throws Exception {
58        super.tearDown();
59        setWiFiStateOn(true);
60        removeAllCurrentDownloads();
61
62        if (mReceiver != null) {
63            mContext.unregisterReceiver(mReceiver);
64            mReceiver = null;
65        }
66    }
67
68    /**
69     * Verifies a particular error code was received from a download
70     *
71     * @param uri The uri to enqueue to the DownloadManager
72     * @param error The error code expected
73     * @throws Exception if the test fails
74     */
75    public void doErrorTest(Uri uri, int error) throws Exception {
76        Request request = new Request(uri);
77        request.setTitle(DEFAULT_FILENAME);
78
79        long dlRequest = mDownloadManager.enqueue(request);
80        waitForDownloadOrTimeout(dlRequest);
81
82        Cursor cursor = getCursor(dlRequest);
83        try {
84            verifyInt(cursor, DownloadManager.COLUMN_REASON, error);
85        } finally {
86            cursor.close();
87        }
88    }
89
90    /**
91     * Test a basic download of a binary file 500k in size.
92     */
93    @LargeTest
94    public void testBinaryDownloadToSystemCache() throws Exception {
95        int fileSize = 1024;
96        byte[] blobData = generateData(fileSize, DataType.BINARY);
97
98        long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
99        verifyDownload(dlRequest, blobData);
100        mDownloadManager.remove(dlRequest);
101    }
102
103    /**
104     * Tests the basic downloading of a text file 300000 bytes in size.
105     */
106    @LargeTest
107    public void testTextDownloadToSystemCache() throws Exception {
108        int fileSize = 1024;
109        byte[] blobData = generateData(fileSize, DataType.TEXT);
110
111        long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
112        verifyDownload(dlRequest, blobData);
113        mDownloadManager.remove(dlRequest);
114    }
115
116    /**
117     * Helper to verify a standard single-file download from the mock server, and clean up after
118     * verification
119     *
120     * Note that this also calls the Download manager's remove, which cleans up the file from cache.
121     *
122     * @param requestId The id of the download to remove
123     * @param fileData The data to verify the file contains
124     */
125    private void verifyDownload(long requestId, byte[] fileData)
126            throws Exception {
127        int fileSize = fileData.length;
128        ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(requestId);
129        Cursor cursor = mDownloadManager.query(new Query().setFilterById(requestId));
130        try {
131            assertEquals(1, cursor.getCount());
132            assertTrue(cursor.moveToFirst());
133
134            verifyFileSize(pfd, fileSize);
135            verifyFileContents(pfd, fileData);
136            int colIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
137            String fileName = cursor.getString(colIndex);
138            assertTrue(fileName.startsWith(CACHE_DIR));
139        } finally {
140            pfd.close();
141            cursor.close();
142        }
143    }
144
145    /**
146     * Tests trying to download to SD card when the file with same name already exists.
147     */
148    @LargeTest
149    public void testDownloadToExternal_fileExists() throws Exception {
150        File existentFile = createFileOnSD(null, 1, DataType.TEXT, null);
151        byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
152
153        // Prepare the mock server with a standard response
154        enqueueResponse(buildResponse(HTTP_OK, blobData));
155
156        try {
157            Uri uri = getServerUri(DEFAULT_FILENAME);
158            Request request = new Request(uri);
159
160            Uri localUri = Uri.fromFile(existentFile);
161            request.setDestinationUri(localUri);
162
163            long dlRequest = mDownloadManager.enqueue(request);
164
165            // wait for the download to complete
166            waitForDownloadOrTimeout(dlRequest);
167            Cursor cursor = getCursor(dlRequest);
168
169            try {
170                verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_SUCCESSFUL);
171            } finally {
172                cursor.close();
173            }
174        } finally {
175            existentFile.delete();
176        }
177    }
178
179    /**
180     * Tests trying to download a file to SD card.
181     */
182    @LargeTest
183    public void testDownloadToExternal() throws Exception {
184        String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
185        File downloadedFile = new File(localDownloadDirectory, DEFAULT_FILENAME);
186        // make sure the file doesn't already exist in the directory
187        downloadedFile.delete();
188
189        try {
190            byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
191
192            // Prepare the mock server with a standard response
193            enqueueResponse(buildResponse(HTTP_OK, blobData));
194
195            Uri uri = getServerUri(DEFAULT_FILENAME);
196            Request request = new Request(uri);
197
198            Uri localUri = Uri.fromFile(downloadedFile);
199            request.setDestinationUri(localUri);
200
201            long dlRequest = mDownloadManager.enqueue(request);
202
203            // wait for the download to complete
204            waitForDownloadOrTimeout(dlRequest);
205
206            verifyAndCleanupSingleFileDownload(dlRequest, blobData);
207
208            assertEquals(1, mReceiver.numDownloadsCompleted());
209        } finally {
210            downloadedFile.delete();
211        }
212    }
213
214    /**
215     * Tests trying to download a file to the system partition.
216     */
217    @LargeTest
218    public void testDownloadToProhibitedDirectory() throws Exception {
219        File downloadedFile = new File(PROHIBITED_DIRECTORY, DEFAULT_FILENAME);
220        try {
221            byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
222
223            // Prepare the mock server with a standard response
224            enqueueResponse(buildResponse(HTTP_OK, blobData));
225
226            Uri uri = getServerUri(DEFAULT_FILENAME);
227            Request request = new Request(uri);
228
229            Uri localUri = Uri.fromFile(downloadedFile);
230            request.setDestinationUri(localUri);
231
232            try {
233                mDownloadManager.enqueue(request);
234                fail("Failed to throw SecurityException when trying to write to /system.");
235            } catch (SecurityException s) {
236                assertFalse(downloadedFile.exists());
237            }
238        } finally {
239            // Just in case file somehow got created, make sure to delete it
240            downloadedFile.delete();
241        }
242    }
243
244    /**
245     * Tests that we get the correct download ID from the download notification.
246     */
247    @LargeTest
248    public void testGetDownloadIdOnNotification() throws Exception {
249        byte[] blobData = generateData(3000, DataType.TEXT);  // file size = 3000 bytes
250
251        enqueueResponse(buildResponse(HTTP_OK, blobData));
252        long dlRequest = doCommonStandardEnqueue();
253        waitForDownloadOrTimeout(dlRequest);
254
255        Set<Long> ids = mReceiver.getDownloadIds();
256        assertEquals(1, ids.size());
257        Iterator<Long> it = ids.iterator();
258        assertEquals("Download ID received from notification does not match initial id!",
259                dlRequest, it.next().longValue());
260    }
261
262    /**
263     * Tests the download failure error after too many redirects (>5).
264     */
265    @LargeTest
266    public void testErrorTooManyRedirects() throws Exception {
267        Uri uri = getServerUri(DEFAULT_FILENAME);
268
269        // force 6 redirects
270        for (int i = 0; i < 6; ++i) {
271            final MockResponse resp = buildResponse(HTTP_REDIRECT);
272            resp.setHeader("Location", uri.toString());
273            enqueueResponse(resp);
274        }
275        doErrorTest(uri, DownloadManager.ERROR_TOO_MANY_REDIRECTS);
276    }
277
278    /**
279     * Tests the download failure error from an unhandled HTTP status code
280     */
281    @LargeTest
282    public void testErrorUnhandledHttpCode() throws Exception {
283        Uri uri = getServerUri(DEFAULT_FILENAME);
284        enqueueResponse(buildResponse(HTTP_PARTIAL_CONTENT));
285
286        doErrorTest(uri, DownloadManager.ERROR_UNHANDLED_HTTP_CODE);
287    }
288
289    /**
290     * Tests the download failure error from an unhandled HTTP status code
291     */
292    @LargeTest
293    public void testErrorHttpDataError_invalidRedirect() throws Exception {
294        Uri uri = getServerUri(DEFAULT_FILENAME);
295        final MockResponse resp = buildResponse(HTTP_REDIRECT);
296        resp.setHeader("Location", "://blah.blah.blah.com");
297        enqueueResponse(resp);
298
299        doErrorTest(uri, DownloadManager.ERROR_HTTP_DATA_ERROR);
300    }
301
302    /**
303     * Tests that we can remove a download from the download manager.
304     */
305    @LargeTest
306    public void testRemoveDownload() throws Exception {
307        int fileSize = 1024;
308        byte[] blobData = generateData(fileSize, DataType.BINARY);
309
310        long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
311        Cursor cursor = mDownloadManager.query(new Query().setFilterById(dlRequest));
312        try {
313            assertEquals("The count of downloads with this ID is not 1!", 1, cursor.getCount());
314            mDownloadManager.remove(dlRequest);
315            cursor.requery();
316            assertEquals("The count of downloads with this ID is not 0!", 0, cursor.getCount());
317        } finally {
318            cursor.close();
319        }
320    }
321
322    /**
323     * Tests that we can set the title of a download.
324     */
325    @LargeTest
326    public void testSetTitle() throws Exception {
327        int fileSize = 1024;
328        byte[] blobData = generateData(fileSize, DataType.BINARY);
329        enqueueResponse(buildResponse(HTTP_OK, blobData));
330
331        // An arbitrary unicode string title
332        final String title = "\u00a5123;\"\u0152\u017d \u054b \u0a07 \ucce0 \u6820\u03a8\u5c34" +
333                "\uf4ad\u0da9\uc0c5\uc1a8 \uf4c5 \uf4aa\u0023\'";
334
335        Uri uri = getServerUri(DEFAULT_FILENAME);
336        Request request = new Request(uri);
337        request.setTitle(title);
338
339        long dlRequest = mDownloadManager.enqueue(request);
340        waitForDownloadOrTimeout(dlRequest);
341
342        Cursor cursor = getCursor(dlRequest);
343        try {
344            verifyString(cursor, DownloadManager.COLUMN_TITLE, title);
345        } finally {
346            cursor.close();
347        }
348    }
349
350    /**
351     * Tests that a download set for Wifi does not progress while Wifi is disabled, but resumes
352     * once Wifi is re-enabled.
353     */
354    @LargeTest
355    public void testDownloadNoWifi() throws Exception {
356        long timeout = 60 * 1000; // wait only 60 seconds before giving up
357        int fileSize = 1024;  // 140k
358        byte[] blobData = generateData(fileSize, DataType.TEXT);
359
360        setWiFiStateOn(false);
361        enqueueResponse(buildResponse(HTTP_OK, blobData));
362
363        try {
364            Uri uri = getServerUri(DEFAULT_FILENAME);
365            Request request = new Request(uri);
366            request.setAllowedNetworkTypes(Request.NETWORK_WIFI);
367
368            long dlRequest = mDownloadManager.enqueue(request);
369
370            // wait for the download to complete
371            boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest,
372                    WAIT_FOR_DOWNLOAD_POLL_TIME, timeout);
373            assertFalse("Download proceeded without Wifi connection!", success);
374
375            setWiFiStateOn(true);
376            waitForDownloadOrTimeout(dlRequest);
377
378            assertEquals(1, mReceiver.numDownloadsCompleted());
379        } finally {
380            setWiFiStateOn(true);
381        }
382    }
383
384    /**
385     * Tests that we get an error code when the server drops the connection during a download.
386     */
387    @LargeTest
388    public void testServerDropConnection_body() throws Exception {
389        byte[] blobData = generateData(25000, DataType.TEXT);  // file size = 25000 bytes
390
391        final MockResponse resp = buildResponse(HTTP_OK, blobData);
392        resp.setHeader("Content-Length", "50000");
393        enqueueResponse(resp);
394
395        long dlRequest = doCommonStandardEnqueue();
396        waitForDownloadOrTimeout(dlRequest);
397
398        Cursor cursor = getCursor(dlRequest);
399        try {
400            verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
401            verifyInt(cursor, DownloadManager.COLUMN_REASON,
402                    DownloadManager.ERROR_CANNOT_RESUME);
403        } finally {
404            cursor.close();
405        }
406        // Even tho the server drops the connection, we should still get a completed notification
407        assertEquals(1, mReceiver.numDownloadsCompleted());
408    }
409}
410