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