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 */
16package com.android.frameworks.downloadmanagertests;
17
18import android.app.DownloadManager;
19import android.app.DownloadManager.Request;
20import android.database.Cursor;
21import android.net.Uri;
22import android.os.Environment;
23import android.os.ParcelFileDescriptor;
24import android.util.Log;
25
26import java.io.DataInputStream;
27import java.io.DataOutputStream;
28import java.io.File;
29import java.io.FileInputStream;
30import java.io.FileOutputStream;
31import java.util.HashSet;
32
33/**
34 * Class to test downloading files from a real (not mock) external server.
35 */
36public class DownloadManagerTestApp extends DownloadManagerBaseTest {
37    protected static String DOWNLOAD_STARTED_FLAG = "DMTEST_DOWNLOAD_STARTED";
38    protected static String LOG_TAG =
39            "com.android.frameworks.downloadmanagertests.DownloadManagerTestApp";
40
41    protected static String DOWNLOAD_500K_FILENAME = "External541kb.apk";
42    protected static long DOWNLOAD_500K_FILESIZE = 570927;
43    protected static String DOWNLOAD_1MB_FILENAME = "External1mb.apk";
44    protected static long DOWNLOAD_1MB_FILESIZE = 1041262;
45    protected static String DOWNLOAD_5MB_FILENAME = "External5mb.apk";
46    protected static long DOWNLOAD_5MB_FILESIZE = 5138700;
47    protected static String DOWNLOAD_10MB_FILENAME = "External10mb.apk";
48    protected static long DOWNLOAD_10MB_FILESIZE = 10258741;
49
50    private static final String FILE_CONCURRENT_DOWNLOAD_FILE_PREFIX = "file";
51    private static final String FILE_CONCURRENT_DOWNLOAD_FILE_EXTENSION = ".bin";
52    protected static long CONCURRENT_DOWNLOAD_FILESIZE = 1000000;
53
54    // Values to be obtained from TestRunner
55    private String externalDownloadUriValue = null;
56    private String externalLargeDownloadUriValue = null;
57
58    /**
59     * {@inheritDoc }
60     */
61    @Override
62    public void setUp() throws Exception {
63        super.setUp();
64        DownloadManagerTestRunner mRunner = (DownloadManagerTestRunner)getInstrumentation();
65        externalDownloadUriValue = normalizeUri(mRunner.externalDownloadUriValue);
66        assertNotNull(externalDownloadUriValue);
67
68        externalLargeDownloadUriValue = normalizeUri(mRunner.externalDownloadUriValue);
69        assertNotNull(externalLargeDownloadUriValue);
70    }
71
72    /**
73     * Normalizes a uri to ensure it ends with a "/"
74     *
75     * @param uri The uri to normalize (or null)
76     * @return The normalized uri, or null if null was passed in
77     */
78    public String normalizeUri(String uri) {
79        if (uri != null && !uri.endsWith("/")) {
80            uri += "/";
81        }
82        return uri;
83    }
84
85    /**
86     * Gets the external URL of the file to download
87     *
88     * @return the Uri of the external file to download
89     */
90    private Uri getExternalFileUri(String file) {
91        return Uri.parse(externalDownloadUriValue + file);
92    }
93
94    /**
95     * Gets the path to the file that flags that a download has started. The file contains the
96     * DownloadManager id of the download being trackted between reboot sessions.
97     *
98     * @return The path of the file tracking that a download has started
99     * @throws InterruptedException if interrupted
100     * @throws Exception if timed out while waiting for SD card to mount
101     */
102    protected String getDownloadStartedFilePath() {
103        String path = Environment.getExternalStorageDirectory().getPath();
104        return path + File.separatorChar + DOWNLOAD_STARTED_FLAG;
105    }
106
107    /**
108     * Common setup steps for downloads.
109     *
110     * Note that these are not included in setUp, so that individual tests can control their own
111     * state between reboots, etc.
112     */
113    protected void doCommonDownloadSetup() throws Exception {
114        setWiFiStateOn(true);
115        setAirplaneModeOn(false);
116        waitForExternalStoreMount();
117        removeAllCurrentDownloads();
118    }
119
120    /**
121     * Initiates a download.
122     *
123     * Queues up a download to the download manager, and saves the DownloadManager's assigned
124     * download ID for this download to a file.
125     *
126     * @throws Exception if unsuccessful
127     */
128    public void initiateDownload() throws Exception {
129        String filename = DOWNLOAD_5MB_FILENAME;
130        mContext.deleteFile(DOWNLOAD_STARTED_FLAG);
131        FileOutputStream fileOutput = mContext.openFileOutput(DOWNLOAD_STARTED_FLAG, 0);
132        DataOutputStream outputFile = null;
133        doCommonDownloadSetup();
134
135        try {
136            long dlRequest = -1;
137
138            // Make sure there are no pending downloads currently going on
139            removeAllCurrentDownloads();
140
141            Uri remoteUri = getExternalFileUri(filename);
142            Request request = new Request(remoteUri);
143
144            dlRequest = mDownloadManager.enqueue(request);
145            waitForDownloadToStart(dlRequest);
146            assertTrue(dlRequest != -1);
147
148            // Store ID of download for later retrieval
149            outputFile = new DataOutputStream(fileOutput);
150            outputFile.writeLong(dlRequest);
151        } finally {
152            if (outputFile != null) {
153                outputFile.flush();
154                outputFile.close();
155            }
156        }
157    }
158
159    /**
160     * Waits for a previously-initiated download and verifies it has completed successfully.
161     *
162     * @throws Exception if unsuccessful
163     */
164    public void verifyFileDownloadSucceeded() throws Exception {
165        String filename = DOWNLOAD_5MB_FILENAME;
166        long filesize = DOWNLOAD_5MB_FILESIZE;
167        long dlRequest = -1;
168        boolean rebootMarkerValid = false;
169        DataInputStream dataInputFile = null;
170
171        setWiFiStateOn(true);
172        setAirplaneModeOn(false);
173
174        try {
175            FileInputStream inFile = mContext.openFileInput(DOWNLOAD_STARTED_FLAG);
176            dataInputFile = new DataInputStream(inFile);
177            dlRequest = dataInputFile.readLong();
178        } catch (Exception e) {
179            // The file was't valid so we just leave the flag false
180            Log.i(LOG_TAG, "Unable to determine initial download id.");
181            throw e;
182        } finally {
183            if (dataInputFile != null) {
184                dataInputFile.close();
185            }
186            mContext.deleteFile(DOWNLOAD_STARTED_FLAG);
187        }
188
189        assertTrue(dlRequest != -1);
190        Cursor cursor = getCursor(dlRequest);
191        ParcelFileDescriptor pfd = null;
192        try {
193            assertTrue("Unable to query last initiated download!", cursor.moveToFirst());
194
195            int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
196            int status = cursor.getInt(columnIndex);
197            int currentWaitTime = 0;
198
199            // Wait until the download finishes; don't wait for a notification b/c
200            // the download may well have been completed before the last reboot.
201            waitForDownloadOrTimeout_skipNotification(dlRequest);
202
203            Log.i(LOG_TAG, "Verifying download information...");
204            // Verify specific info about the file (size, name, etc)...
205            pfd = mDownloadManager.openDownloadedFile(dlRequest);
206            verifyFileSize(pfd, filesize);
207        } catch (Exception e) {
208            Log.i(LOG_TAG, "error: " + e.toString());
209            throw e;
210        } finally {
211            // Clean up...
212            cursor.close();
213            mDownloadManager.remove(dlRequest);
214            if (pfd != null) {
215                pfd.close();
216            }
217        }
218    }
219
220    /**
221     * Tests downloading a large file over WiFi (~10 Mb).
222     *
223     * @throws Exception if unsuccessful
224     */
225    public void runLargeDownloadOverWiFi() throws Exception {
226        String filename = DOWNLOAD_10MB_FILENAME;
227        long filesize = DOWNLOAD_10MB_FILESIZE;
228        long dlRequest = -1;
229        doCommonDownloadSetup();
230
231        // Make sure there are no pending downloads currently going on
232        removeAllCurrentDownloads();
233
234        Uri remoteUri = getExternalFileUri(filename);
235        Request request = new Request(remoteUri);
236        request.setMimeType("application/vnd.android.package-archive");
237
238        dlRequest = mDownloadManager.enqueue(request);
239
240        // Rather large file, so wait up to 15 mins...
241        waitForDownloadOrTimeout(dlRequest, WAIT_FOR_DOWNLOAD_POLL_TIME, 15 * 60 * 1000);
242
243        Cursor cursor = getCursor(dlRequest);
244        ParcelFileDescriptor pfd = null;
245        try {
246            Log.i(LOG_TAG, "Verifying download information...");
247            // Verify specific info about the file (size, name, etc)...
248            pfd = mDownloadManager.openDownloadedFile(dlRequest);
249            verifyFileSize(pfd, filesize);
250        } finally {
251            if (pfd != null) {
252                pfd.close();
253            }
254            mDownloadManager.remove(dlRequest);
255            cursor.close();
256        }
257    }
258
259    /**
260     * Tests that downloads resume when switching back and forth from having connectivity to
261     * having no connectivity using both WiFi and airplane mode.
262     *
263     * Note: Device has no mobile access when running this test.
264     *
265     * @throws Exception if unsuccessful
266     */
267    public void runDownloadMultipleSwitching() throws Exception {
268        String filename = DOWNLOAD_500K_FILENAME;
269        long filesize = DOWNLOAD_500K_FILESIZE;
270        doCommonDownloadSetup();
271
272        String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
273        File downloadedFile = new File(localDownloadDirectory, filename);
274
275        long dlRequest = -1;
276        try {
277            downloadedFile.delete();
278
279            // Make sure there are no pending downloads currently going on
280            removeAllCurrentDownloads();
281
282            Uri remoteUri = getExternalFileUri(filename);
283            Request request = new Request(remoteUri);
284
285            // Local destination of downloaded file
286            Uri localUri = Uri.fromFile(downloadedFile);
287            Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
288            request.setDestinationUri(localUri);
289
290            request.setAllowedNetworkTypes(Request.NETWORK_MOBILE | Request.NETWORK_WIFI);
291
292            dlRequest = mDownloadManager.enqueue(request);
293            waitForDownloadToStart(dlRequest);
294            // make sure we're starting to download some data...
295            waitForFileToGrow(downloadedFile);
296
297            // download disable
298            setWiFiStateOn(false);
299
300            // download disable
301            Log.i(LOG_TAG, "Turning on airplane mode...");
302            setAirplaneModeOn(true);
303            Thread.sleep(30 * 1000);  // wait 30 secs
304
305            // download disable
306            setWiFiStateOn(true);
307            Thread.sleep(30 * 1000);  // wait 30 secs
308
309            // download enable
310            Log.i(LOG_TAG, "Turning off airplane mode...");
311            setAirplaneModeOn(false);
312            Thread.sleep(5 * 1000);  // wait 5 seconds
313
314            // download disable
315            Log.i(LOG_TAG, "Turning off WiFi...");
316            setWiFiStateOn(false);
317            Thread.sleep(30 * 1000);  // wait 30 secs
318
319            // finally, turn WiFi back on and finish up the download
320            Log.i(LOG_TAG, "Turning on WiFi...");
321            setWiFiStateOn(true);
322            Log.i(LOG_TAG, "Waiting up to 3 minutes for download to complete...");
323            waitForDownloadsOrTimeout(dlRequest, 3 * 60 * 1000);
324            ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest);
325            verifyFileSize(pfd, filesize);
326        } finally {
327            Log.i(LOG_TAG, "Cleaning up files...");
328            if (dlRequest != -1) {
329                mDownloadManager.remove(dlRequest);
330            }
331            downloadedFile.delete();
332        }
333    }
334
335    /**
336     * Tests that downloads resume when switching on/off WiFi at various intervals.
337     *
338     * Note: Device has no mobile access when running this test.
339     *
340     * @throws Exception if unsuccessful
341     */
342    public void runDownloadMultipleWiFiEnableDisable() throws Exception {
343        String filename = DOWNLOAD_500K_FILENAME;
344        long filesize = DOWNLOAD_500K_FILESIZE;
345        doCommonDownloadSetup();
346
347        String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
348        File downloadedFile = new File(localDownloadDirectory, filename);
349        long dlRequest = -1;
350        try {
351            downloadedFile.delete();
352
353            // Make sure there are no pending downloads currently going on
354            removeAllCurrentDownloads();
355
356            Uri remoteUri = getExternalFileUri(filename);
357            Request request = new Request(remoteUri);
358
359            // Local destination of downloaded file
360            Uri localUri = Uri.fromFile(downloadedFile);
361            Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
362            request.setDestinationUri(localUri);
363
364            request.setAllowedNetworkTypes(Request.NETWORK_WIFI);
365
366            dlRequest = mDownloadManager.enqueue(request);
367            waitForDownloadToStart(dlRequest);
368            // are we making any progress?
369            waitForFileToGrow(downloadedFile);
370
371            // download disable
372            Log.i(LOG_TAG, "Turning off WiFi...");
373            setWiFiStateOn(false);
374            Thread.sleep(40 * 1000);  // wait 40 seconds
375
376            // enable download...
377            Log.i(LOG_TAG, "Turning on WiFi again...");
378            setWiFiStateOn(true);
379            waitForFileToGrow(downloadedFile);
380
381            // download disable
382            Log.i(LOG_TAG, "Turning off WiFi...");
383            setWiFiStateOn(false);
384            Thread.sleep(20 * 1000);  // wait 20 seconds
385
386            // enable download...
387            Log.i(LOG_TAG, "Turning on WiFi again...");
388            setWiFiStateOn(true);
389
390            Log.i(LOG_TAG, "Waiting up to 3 minutes for download to complete...");
391            waitForDownloadsOrTimeout(dlRequest, 3 * 60 * 1000);
392            ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest);
393            verifyFileSize(pfd, filesize);
394        } finally {
395            Log.i(LOG_TAG, "Cleaning up files...");
396            if (dlRequest != -1) {
397                mDownloadManager.remove(dlRequest);
398            }
399            downloadedFile.delete();
400        }
401    }
402
403    /**
404     * Tests that downloads resume when switching on/off Airplane mode numerous times at
405     * various intervals.
406     *
407     * Note: Device has no mobile access when running this test.
408     *
409     * @throws Exception if unsuccessful
410     */
411    public void runDownloadMultipleAirplaneModeEnableDisable() throws Exception {
412        String filename = DOWNLOAD_500K_FILENAME;
413        long filesize = DOWNLOAD_500K_FILESIZE;
414        // make sure WiFi is enabled, and airplane mode is not on
415        doCommonDownloadSetup();
416
417        String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
418        File downloadedFile = new File(localDownloadDirectory, filename);
419        long dlRequest = -1;
420        try {
421            downloadedFile.delete();
422
423            // Make sure there are no pending downloads currently going on
424            removeAllCurrentDownloads();
425
426            Uri remoteUri = getExternalFileUri(filename);
427            Request request = new Request(remoteUri);
428
429            // Local destination of downloaded file
430            Uri localUri = Uri.fromFile(downloadedFile);
431            Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
432            request.setDestinationUri(localUri);
433
434            request.setAllowedNetworkTypes(Request.NETWORK_WIFI);
435
436            dlRequest = mDownloadManager.enqueue(request);
437            waitForDownloadToStart(dlRequest);
438            // are we making any progress?
439            waitForFileToGrow(downloadedFile);
440
441            // download disable
442            Log.i(LOG_TAG, "Turning on Airplane mode...");
443            setAirplaneModeOn(true);
444            Thread.sleep(60 * 1000);  // wait 1 minute
445
446            // download enable
447            Log.i(LOG_TAG, "Turning off Airplane mode...");
448            setAirplaneModeOn(false);
449            // make sure we're starting to download some data...
450            waitForFileToGrow(downloadedFile);
451
452            // reenable the connection to start up the download again
453            Log.i(LOG_TAG, "Turning on Airplane mode again...");
454            setAirplaneModeOn(true);
455            Thread.sleep(20 * 1000);  // wait 20 seconds
456
457            // Finish up the download...
458            Log.i(LOG_TAG, "Turning off Airplane mode again...");
459            setAirplaneModeOn(false);
460
461            Log.i(LOG_TAG, "Waiting up to 3 minutes for donwload to complete...");
462            waitForDownloadsOrTimeout(dlRequest, 180 * 1000);  // wait up to 3 mins before timeout
463            ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest);
464            verifyFileSize(pfd, filesize);
465        } finally {
466            Log.i(LOG_TAG, "Cleaning up files...");
467            if (dlRequest != -1) {
468                mDownloadManager.remove(dlRequest);
469            }
470            downloadedFile.delete();
471        }
472    }
473
474    /**
475     * Tests 15 concurrent downloads of 1,000,000-byte files.
476     *
477     * @throws Exception if test failed
478     */
479    public void runDownloadMultipleSimultaneously() throws Exception {
480        final int TOTAL_DOWNLOADS = 15;
481        HashSet<Long> downloadIds = new HashSet<Long>(TOTAL_DOWNLOADS);
482        MultipleDownloadsCompletedReceiver receiver = registerNewMultipleDownloadsReceiver();
483
484        // Make sure there are no pending downloads currently going on
485        removeAllCurrentDownloads();
486
487        try {
488            for (int i = 0; i < TOTAL_DOWNLOADS; ++i) {
489                long dlRequest = -1;
490                String filename = FILE_CONCURRENT_DOWNLOAD_FILE_PREFIX + i
491                        + FILE_CONCURRENT_DOWNLOAD_FILE_EXTENSION;
492                Uri remoteUri = getExternalFileUri(filename);
493                Request request = new Request(remoteUri);
494                request.setTitle(filename);
495                dlRequest = mDownloadManager.enqueue(request);
496                assertTrue(dlRequest != -1);
497                downloadIds.add(dlRequest);
498            }
499
500            waitForDownloadsOrTimeout(DEFAULT_WAIT_POLL_TIME, 15 * 60 * 2000);  // wait 15 mins max
501            assertEquals(TOTAL_DOWNLOADS, receiver.numDownloadsCompleted());
502        } finally {
503            removeAllCurrentDownloads();
504        }
505    }
506}
507