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 com.android.providers.downloads;
18
19import static android.app.DownloadManager.STATUS_FAILED;
20import static android.app.DownloadManager.STATUS_SUCCESSFUL;
21import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
22import static android.text.format.DateUtils.SECOND_IN_MILLIS;
23
24import android.app.DownloadManager;
25import android.database.Cursor;
26import android.net.Uri;
27import android.os.ParcelFileDescriptor;
28import android.os.SystemClock;
29import android.util.Log;
30
31import java.io.InputStream;
32import java.net.MalformedURLException;
33import java.net.UnknownHostException;
34import java.util.concurrent.TimeoutException;
35
36/**
37 * Code common to tests that use the download manager public API.
38 */
39public abstract class AbstractPublicApiTest extends AbstractDownloadProviderFunctionalTest {
40
41    class Download {
42        final long mId;
43
44        private Download(long downloadId) {
45            this.mId = downloadId;
46        }
47
48        public int getStatus() {
49            return (int) getLongField(DownloadManager.COLUMN_STATUS);
50        }
51
52        public int getReason() {
53            return (int) getLongField(DownloadManager.COLUMN_REASON);
54        }
55
56        public int getStatusIfExists() {
57            Cursor cursor = mManager.query(new DownloadManager.Query().setFilterById(mId));
58            try {
59                if (cursor.getCount() > 0) {
60                    cursor.moveToFirst();
61                    return (int) cursor.getLong(cursor.getColumnIndexOrThrow(
62                            DownloadManager.COLUMN_STATUS));
63                } else {
64                    // the row doesn't exist
65                    return -1;
66                }
67            } finally {
68                cursor.close();
69            }
70        }
71
72        String getStringField(String field) {
73            Cursor cursor = mManager.query(new DownloadManager.Query().setFilterById(mId));
74            try {
75                assertEquals(1, cursor.getCount());
76                cursor.moveToFirst();
77                return cursor.getString(cursor.getColumnIndexOrThrow(field));
78            } finally {
79                cursor.close();
80            }
81        }
82
83        long getLongField(String field) {
84            Cursor cursor = mManager.query(new DownloadManager.Query().setFilterById(mId));
85            try {
86                assertEquals(1, cursor.getCount());
87                cursor.moveToFirst();
88                return cursor.getLong(cursor.getColumnIndexOrThrow(field));
89            } finally {
90                cursor.close();
91            }
92        }
93
94        String getContents() throws Exception {
95            ParcelFileDescriptor downloadedFile = mManager.openDownloadedFile(mId);
96            assertTrue("Invalid file descriptor: " + downloadedFile,
97                       downloadedFile.getFileDescriptor().valid());
98            final InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(
99                    downloadedFile);
100            try {
101                return readStream(stream);
102            } finally {
103                stream.close();
104            }
105        }
106
107        void runUntilStatus(int status) throws TimeoutException {
108            final long startMillis = mSystemFacade.currentTimeMillis();
109            startService(null);
110            waitForStatus(status, startMillis);
111        }
112
113        void runUntilStatus(int status, long timeout) throws TimeoutException {
114            final long startMillis = mSystemFacade.currentTimeMillis();
115            startService(null);
116            waitForStatus(status, startMillis, timeout);
117        }
118
119        void waitForStatus(int expected, long afterMillis) throws TimeoutException {
120            waitForStatus(expected, afterMillis, 15 * SECOND_IN_MILLIS);
121        }
122
123        void waitForStatus(int expected, long afterMillis, long timeout) throws TimeoutException {
124            int actual = -1;
125
126            final long elapsedTimeout = SystemClock.elapsedRealtime() + timeout;
127            while (SystemClock.elapsedRealtime() < elapsedTimeout) {
128                if (getLongField(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP) >= afterMillis) {
129                    actual = getStatus();
130                    if (actual == STATUS_SUCCESSFUL || actual == STATUS_FAILED) {
131                        assertEquals(expected, actual);
132                        return;
133                    } else if (actual == expected) {
134                        return;
135                    }
136
137                    if (timeout > MINUTE_IN_MILLIS) {
138                        final int percent = (int) (100
139                                * getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)
140                                / getLongField(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
141                        Log.d(LOG_TAG, percent + "% complete");
142                    }
143                }
144
145                if (timeout > MINUTE_IN_MILLIS) {
146                    SystemClock.sleep(SECOND_IN_MILLIS * 3);
147                } else {
148                    SystemClock.sleep(100);
149                }
150            }
151
152            throw new TimeoutException("Expected status " + expected + "; only reached " + actual);
153        }
154
155        // max time to wait before giving up on the current download operation.
156        private static final int MAX_TIME_TO_WAIT_FOR_OPERATION = 5;
157        // while waiting for the above time period, sleep this long to yield to the
158        // download thread
159        private static final int TIME_TO_SLEEP = 1000;
160
161        // waits until progress_so_far is >= (progress)%
162        boolean runUntilProgress(int progress) throws InterruptedException {
163            startService(null);
164
165            int sleepCounter = MAX_TIME_TO_WAIT_FOR_OPERATION * 1000 / TIME_TO_SLEEP;
166            int numBytesReceivedSoFar = 0;
167            int totalBytes = 0;
168            for (int i = 0; i < sleepCounter; i++) {
169                Cursor cursor = mManager.query(new DownloadManager.Query().setFilterById(mId));
170                try {
171                    assertEquals(1, cursor.getCount());
172                    cursor.moveToFirst();
173                    numBytesReceivedSoFar = cursor.getInt(
174                            cursor.getColumnIndexOrThrow(
175                                    DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
176                    totalBytes = cursor.getInt(
177                            cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
178                } finally {
179                    cursor.close();
180                }
181                Log.i(LOG_TAG, "in runUntilProgress, numBytesReceivedSoFar: " +
182                        numBytesReceivedSoFar + ", totalBytes: " + totalBytes);
183                if (totalBytes == 0) {
184                    fail("total_bytes should not be zero");
185                    return false;
186                } else {
187                    if (numBytesReceivedSoFar * 100 / totalBytes >= progress) {
188                        // progress_so_far is >= progress%. we are done
189                        return true;
190                    }
191                }
192                // download not done yet. sleep a while and try again
193                Thread.sleep(TIME_TO_SLEEP);
194            }
195            Log.i(LOG_TAG, "FAILED in runUntilProgress, numBytesReceivedSoFar: " +
196                    numBytesReceivedSoFar + ", totalBytes: " + totalBytes);
197            return false; // failed
198        }
199    }
200
201    protected static final String PACKAGE_NAME = "my.package.name";
202    protected static final String REQUEST_PATH = "/path";
203
204    protected DownloadManager mManager;
205
206    public AbstractPublicApiTest(FakeSystemFacade systemFacade) {
207        super(systemFacade);
208    }
209
210    @Override
211    protected void setUp() throws Exception {
212        super.setUp();
213        mManager = new DownloadManager(mResolver, PACKAGE_NAME);
214    }
215
216    protected DownloadManager.Request getRequest()
217            throws MalformedURLException, UnknownHostException {
218        return getRequest(getServerUri(REQUEST_PATH));
219    }
220
221    protected DownloadManager.Request getRequest(String path) {
222        return new DownloadManager.Request(Uri.parse(path));
223    }
224
225    protected Download enqueueRequest(DownloadManager.Request request) {
226        return new Download(mManager.enqueue(request));
227    }
228}
229