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