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