blob: bae4ecc14469d8e39dd139d27c5103db21502e73 [file] [log] [blame]
Neal Nguyen40ef0f42010-08-09 14:08:26 -07001/*
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
Steve Howard31fd85f2010-09-27 16:32:39 -070017package android.app;
Neal Nguyen40ef0f42010-08-09 14:08:26 -070018
Steve Howard31fd85f2010-09-27 16:32:39 -070019import android.app.DownloadManager.Query;
20import android.app.DownloadManager.Request;
Neal Nguyen40ef0f42010-08-09 14:08:26 -070021import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.database.Cursor;
26import android.net.ConnectivityManager;
Neal Nguyen40ef0f42010-08-09 14:08:26 -070027import android.net.NetworkInfo;
Steve Howard31fd85f2010-09-27 16:32:39 -070028import android.net.Uri;
Neal Nguyen40ef0f42010-08-09 14:08:26 -070029import android.net.wifi.WifiManager;
30import android.os.Environment;
31import android.os.ParcelFileDescriptor;
Christopher Tatec09cdce2012-09-10 16:50:14 -070032import android.os.UserHandle;
Neal Nguyen40ef0f42010-08-09 14:08:26 -070033import android.os.ParcelFileDescriptor.AutoCloseInputStream;
Vasu Nori82e891b2010-12-15 14:42:30 -080034import android.os.SystemClock;
Neal Nguyen40ef0f42010-08-09 14:08:26 -070035import android.provider.Settings;
36import android.test.InstrumentationTestCase;
37import android.util.Log;
38
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -070039import com.google.mockwebserver.MockResponse;
40import com.google.mockwebserver.MockWebServer;
41
Neal Nguyen40ef0f42010-08-09 14:08:26 -070042import java.io.DataInputStream;
43import java.io.DataOutputStream;
44import java.io.File;
45import java.io.FileInputStream;
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -070046import java.io.FileNotFoundException;
Neal Nguyen40ef0f42010-08-09 14:08:26 -070047import java.io.FileOutputStream;
48import java.io.IOException;
49import java.net.URL;
Vasu Nori82e891b2010-12-15 14:42:30 -080050import java.util.ArrayList;
Neal Nguyendf7a8652010-09-09 14:54:26 -070051import java.util.Collections;
52import java.util.HashSet;
Neal Nguyen40ef0f42010-08-09 14:08:26 -070053import java.util.Random;
Neal Nguyendf7a8652010-09-09 14:54:26 -070054import java.util.Set;
Vasu Nori82e891b2010-12-15 14:42:30 -080055import java.util.concurrent.TimeoutException;
Neal Nguyen40ef0f42010-08-09 14:08:26 -070056
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -070057import libcore.io.Streams;
58
Neal Nguyen40ef0f42010-08-09 14:08:26 -070059/**
60 * Base class for Instrumented tests for the Download Manager.
61 */
62public class DownloadManagerBaseTest extends InstrumentationTestCase {
Vasu Nori6f35c0e2010-12-17 17:08:58 -080063 private static final String TAG = "DownloadManagerBaseTest";
Neal Nguyen40ef0f42010-08-09 14:08:26 -070064 protected DownloadManager mDownloadManager = null;
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -070065 private MockWebServer mServer = null;
Neal Nguyen40ef0f42010-08-09 14:08:26 -070066 protected String mFileType = "text/plain";
67 protected Context mContext = null;
Neal Nguyen63e5d792010-10-04 11:13:33 -070068 protected MultipleDownloadsCompletedReceiver mReceiver = null;
Vasu Nori82e891b2010-12-15 14:42:30 -080069 protected static final int DEFAULT_FILE_SIZE = 10 * 1024; // 10kb
Neal Nguyen40ef0f42010-08-09 14:08:26 -070070 protected static final int FILE_BLOCK_READ_SIZE = 1024 * 1024;
71
72 protected static final String LOG_TAG = "android.net.DownloadManagerBaseTest";
73 protected static final int HTTP_OK = 200;
Neal Nguyendf7a8652010-09-09 14:54:26 -070074 protected static final int HTTP_REDIRECT = 307;
Neal Nguyen40ef0f42010-08-09 14:08:26 -070075 protected static final int HTTP_PARTIAL_CONTENT = 206;
76 protected static final int HTTP_NOT_FOUND = 404;
77 protected static final int HTTP_SERVICE_UNAVAILABLE = 503;
78 protected String DEFAULT_FILENAME = "somefile.txt";
79
80 protected static final int DEFAULT_MAX_WAIT_TIME = 2 * 60 * 1000; // 2 minutes
81 protected static final int DEFAULT_WAIT_POLL_TIME = 5 * 1000; // 5 seconds
82
83 protected static final int WAIT_FOR_DOWNLOAD_POLL_TIME = 1 * 1000; // 1 second
Md Haquec6272b92016-03-08 14:51:33 -080084 protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 30 * 1000; // 30 seconds
Neal Nguyen40ef0f42010-08-09 14:08:26 -070085
Vasu Nori82e891b2010-12-15 14:42:30 -080086 protected static final int DOWNLOAD_TO_SYSTEM_CACHE = 1;
87 protected static final int DOWNLOAD_TO_DOWNLOAD_CACHE_DIR = 2;
88
Neal Nguyen40ef0f42010-08-09 14:08:26 -070089 // Just a few popular file types used to return from a download
90 protected enum DownloadFileType {
91 PLAINTEXT,
92 APK,
93 GIF,
94 GARBAGE,
95 UNRECOGNIZED,
96 ZIP
97 }
98
99 protected enum DataType {
100 TEXT,
101 BINARY
102 }
103
104 public static class LoggingRng extends Random {
105
106 /**
107 * Constructor
108 *
109 * Creates RNG with self-generated seed value.
110 */
111 public LoggingRng() {
112 this(SystemClock.uptimeMillis());
113 }
114
115 /**
116 * Constructor
117 *
118 * Creats RNG with given initial seed value
119
120 * @param seed The initial seed value
121 */
122 public LoggingRng(long seed) {
123 super(seed);
124 Log.i(LOG_TAG, "Seeding RNG with value: " + seed);
125 }
126 }
127
128 public static class MultipleDownloadsCompletedReceiver extends BroadcastReceiver {
129 private volatile int mNumDownloadsCompleted = 0;
Neal Nguyendf7a8652010-09-09 14:54:26 -0700130 private Set<Long> downloadIds = Collections.synchronizedSet(new HashSet<Long>());
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700131
132 /**
133 * {@inheritDoc}
134 */
135 @Override
136 public void onReceive(Context context, Intent intent) {
137 if (intent.getAction().equalsIgnoreCase(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
Neal Nguyen63e5d792010-10-04 11:13:33 -0700138 synchronized(this) {
Neal Nguyen65c36e62010-10-24 18:02:45 -0700139 long id = intent.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
140 Log.i(LOG_TAG, "Received Notification for download: " + id);
141 if (!downloadIds.contains(id)) {
142 ++mNumDownloadsCompleted;
143 Log.i(LOG_TAG, "MultipleDownloadsCompletedReceiver got intent: " +
144 intent.getAction() + " --> total count: " + mNumDownloadsCompleted);
145 downloadIds.add(id);
146
147 DownloadManager dm = (DownloadManager)context.getSystemService(
148 Context.DOWNLOAD_SERVICE);
149
150 Cursor cursor = dm.query(new Query().setFilterById(id));
151 try {
152 if (cursor.moveToFirst()) {
153 int status = cursor.getInt(cursor.getColumnIndex(
154 DownloadManager.COLUMN_STATUS));
155 Log.i(LOG_TAG, "Download status is: " + status);
156 } else {
157 fail("No status found for completed download!");
158 }
159 } finally {
160 cursor.close();
161 }
162 } else {
163 Log.i(LOG_TAG, "Notification for id: " + id + " has already been made.");
164 }
Neal Nguyen63e5d792010-10-04 11:13:33 -0700165 }
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700166 }
167 }
168
169 /**
170 * Gets the number of times the {@link #onReceive} callback has been called for the
171 * {@link DownloadManager.ACTION_DOWNLOAD_COMPLETED} action, indicating the number of
172 * downloads completed thus far.
173 *
174 * @return the number of downloads completed so far.
175 */
176 public int numDownloadsCompleted() {
177 return mNumDownloadsCompleted;
178 }
Neal Nguyendf7a8652010-09-09 14:54:26 -0700179
180 /**
181 * Gets the list of download IDs.
182 * @return A Set<Long> with the ids of the completed downloads.
183 */
184 public Set<Long> getDownloadIds() {
Neal Nguyenbd06f022010-10-20 17:43:15 -0700185 synchronized(this) {
Neal Nguyendf7a8652010-09-09 14:54:26 -0700186 Set<Long> returnIds = new HashSet<Long>(downloadIds);
187 return returnIds;
188 }
189 }
190
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700191 }
192
193 public static class WiFiChangedReceiver extends BroadcastReceiver {
194 private Context mContext = null;
195
196 /**
197 * Constructor
198 *
199 * Sets the current state of WiFi.
200 *
201 * @param context The current app {@link Context}.
202 */
203 public WiFiChangedReceiver(Context context) {
204 mContext = context;
205 }
206
207 /**
208 * {@inheritDoc}
209 */
210 @Override
211 public void onReceive(Context context, Intent intent) {
212 if (intent.getAction().equalsIgnoreCase(ConnectivityManager.CONNECTIVITY_ACTION)) {
213 Log.i(LOG_TAG, "ConnectivityManager state change: " + intent.getAction());
214 synchronized (this) {
215 this.notify();
216 }
217 }
218 }
219
220 /**
221 * Gets the current state of WiFi.
222 *
223 * @return Returns true if WiFi is on, false otherwise.
224 */
225 public boolean getWiFiIsOn() {
226 ConnectivityManager connManager = (ConnectivityManager)mContext.getSystemService(
227 Context.CONNECTIVITY_SERVICE);
228 NetworkInfo info = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
Neal Nguyenbd06f022010-10-20 17:43:15 -0700229 Log.i(LOG_TAG, "WiFi Connection state is currently: " + info.isConnected());
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700230 return info.isConnected();
231 }
232 }
233
234 /**
235 * {@inheritDoc}
236 */
237 @Override
238 public void setUp() throws Exception {
239 mContext = getInstrumentation().getContext();
240 mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE);
241 mServer = new MockWebServer();
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700242 mServer.play();
Neal Nguyen63e5d792010-10-04 11:13:33 -0700243 mReceiver = registerNewMultipleDownloadsReceiver();
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700244 // Note: callers overriding this should call mServer.play() with the desired port #
245 }
246
247 /**
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700248 * Helper to build a response from the MockWebServer with no body.
Neal Nguyendf7a8652010-09-09 14:54:26 -0700249 *
250 * @param status The HTTP status code to return for this response
251 * @return Returns the mock web server response that was queued (which can be modified)
252 */
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700253 protected MockResponse buildResponse(int status) {
254 MockResponse response = new MockResponse().setResponseCode(status);
255 response.setHeader("Content-type", mFileType);
256 return response;
Neal Nguyendf7a8652010-09-09 14:54:26 -0700257 }
258
259 /**
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700260 * Helper to build a response from the MockWebServer.
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700261 *
262 * @param status The HTTP status code to return for this response
263 * @param body The body to return in this response
264 * @return Returns the mock web server response that was queued (which can be modified)
265 */
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700266 protected MockResponse buildResponse(int status, byte[] body) {
267 return buildResponse(status).setBody(body);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700268 }
269
270 /**
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700271 * Helper to build a response from the MockWebServer.
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700272 *
273 * @param status The HTTP status code to return for this response
274 * @param bodyFile The body to return in this response
275 * @return Returns the mock web server response that was queued (which can be modified)
276 */
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700277 protected MockResponse buildResponse(int status, File bodyFile)
278 throws FileNotFoundException, IOException {
279 final byte[] body = Streams.readFully(new FileInputStream(bodyFile));
280 return buildResponse(status).setBody(body);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700281 }
282
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700283 protected void enqueueResponse(MockResponse resp) {
284 mServer.enqueue(resp);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700285 }
286
287 /**
288 * Helper to generate a random blob of bytes.
289 *
290 * @param size The size of the data to generate
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700291 * @param type The type of data to generate: currently, one of {@link DataType#TEXT} or
292 * {@link DataType#BINARY}.
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700293 * @return The random data that is generated.
294 */
295 protected byte[] generateData(int size, DataType type) {
296 return generateData(size, type, null);
297 }
298
299 /**
300 * Helper to generate a random blob of bytes using a given RNG.
301 *
302 * @param size The size of the data to generate
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700303 * @param type The type of data to generate: currently, one of {@link DataType#TEXT} or
304 * {@link DataType#BINARY}.
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700305 * @param rng (optional) The RNG to use; pass null to use
306 * @return The random data that is generated.
307 */
308 protected byte[] generateData(int size, DataType type, Random rng) {
309 int min = Byte.MIN_VALUE;
310 int max = Byte.MAX_VALUE;
311
312 // Only use chars in the HTTP ASCII printable character range for Text
313 if (type == DataType.TEXT) {
314 min = 32;
315 max = 126;
316 }
317 byte[] result = new byte[size];
318 Log.i(LOG_TAG, "Generating data of size: " + size);
319
320 if (rng == null) {
321 rng = new LoggingRng();
322 }
323
324 for (int i = 0; i < size; ++i) {
325 result[i] = (byte) (min + rng.nextInt(max - min + 1));
326 }
327 return result;
328 }
329
330 /**
331 * Helper to verify the size of a file.
332 *
333 * @param pfd The input file to compare the size of
334 * @param size The expected size of the file
335 */
336 protected void verifyFileSize(ParcelFileDescriptor pfd, long size) {
337 assertEquals(pfd.getStatSize(), size);
338 }
339
340 /**
341 * Helper to verify the contents of a downloaded file versus a byte[].
342 *
343 * @param actual The file of whose contents to verify
344 * @param expected The data we expect to find in the aforementioned file
345 * @throws IOException if there was a problem reading from the file
346 */
347 protected void verifyFileContents(ParcelFileDescriptor actual, byte[] expected)
348 throws IOException {
349 AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(actual);
350 long fileSize = actual.getStatSize();
351
352 assertTrue(fileSize <= Integer.MAX_VALUE);
353 assertEquals(expected.length, fileSize);
354
355 byte[] actualData = new byte[expected.length];
356 assertEquals(input.read(actualData), fileSize);
357 compareByteArrays(actualData, expected);
358 }
359
360 /**
361 * Helper to compare 2 byte arrays.
362 *
363 * @param actual The array whose data we want to verify
364 * @param expected The array of data we expect to see
365 */
366 protected void compareByteArrays(byte[] actual, byte[] expected) {
367 assertEquals(actual.length, expected.length);
368 int length = actual.length;
369 for (int i = 0; i < length; ++i) {
370 // assert has a bit of overhead, so only do the assert when the values are not the same
371 if (actual[i] != expected[i]) {
372 fail("Byte arrays are not equal.");
373 }
374 }
375 }
376
377 /**
378 * Verifies the contents of a downloaded file versus the contents of a File.
379 *
380 * @param pfd The file whose data we want to verify
381 * @param file The file containing the data we expect to see in the aforementioned file
382 * @throws IOException If there was a problem reading either of the two files
383 */
384 protected void verifyFileContents(ParcelFileDescriptor pfd, File file) throws IOException {
385 byte[] actual = new byte[FILE_BLOCK_READ_SIZE];
386 byte[] expected = new byte[FILE_BLOCK_READ_SIZE];
387
388 AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
389
390 assertEquals(file.length(), pfd.getStatSize());
391
392 DataInputStream inFile = new DataInputStream(new FileInputStream(file));
393 int actualRead = 0;
394 int expectedRead = 0;
395
396 while (((actualRead = input.read(actual)) != -1) &&
397 ((expectedRead = inFile.read(expected)) != -1)) {
398 assertEquals(actualRead, expectedRead);
399 compareByteArrays(actual, expected);
400 }
401 }
402
403 /**
404 * Sets the MIME type of file that will be served from the mock server
405 *
406 * @param type The MIME type to return from the server
407 */
408 protected void setServerMimeType(DownloadFileType type) {
409 mFileType = getMimeMapping(type);
410 }
411
412 /**
413 * Gets the MIME content string for a given type
414 *
415 * @param type The MIME type to return
416 * @return the String representation of that MIME content type
417 */
418 protected String getMimeMapping(DownloadFileType type) {
419 switch (type) {
420 case APK:
421 return "application/vnd.android.package-archive";
422 case GIF:
423 return "image/gif";
424 case ZIP:
425 return "application/x-zip-compressed";
426 case GARBAGE:
427 return "zip\\pidy/doo/da";
428 case UNRECOGNIZED:
429 return "application/new.undefined.type.of.app";
430 }
431 return "text/plain";
432 }
433
434 /**
435 * Gets the Uri that should be used to access the mock server
436 *
437 * @param filename The name of the file to try to retrieve from the mock server
438 * @return the Uri to use for access the file on the mock server
439 */
440 protected Uri getServerUri(String filename) throws Exception {
441 URL url = mServer.getUrl("/" + filename);
442 return Uri.parse(url.toString());
443 }
444
445 /**
446 * Gets the Uri that should be used to access the mock server
447 *
448 * @param filename The name of the file to try to retrieve from the mock server
449 * @return the Uri to use for access the file on the mock server
450 */
451 protected void logDBColumnData(Cursor cursor, String column) {
452 int index = cursor.getColumnIndex(column);
453 Log.i(LOG_TAG, "columnName: " + column);
454 Log.i(LOG_TAG, "columnValue: " + cursor.getString(index));
455 }
456
457 /**
458 * Helper to create and register a new MultipleDownloadCompletedReciever
459 *
460 * This is used to track many simultaneous downloads by keeping count of all the downloads
461 * that have completed.
462 *
463 * @return A new receiver that records and can be queried on how many downloads have completed.
464 */
465 protected MultipleDownloadsCompletedReceiver registerNewMultipleDownloadsReceiver() {
466 MultipleDownloadsCompletedReceiver receiver = new MultipleDownloadsCompletedReceiver();
467 mContext.registerReceiver(receiver, new IntentFilter(
468 DownloadManager.ACTION_DOWNLOAD_COMPLETE));
469 return receiver;
470 }
471
472 /**
473 * Helper to verify a standard single-file download from the mock server, and clean up after
474 * verification
475 *
476 * Note that this also calls the Download manager's remove, which cleans up the file from cache.
477 *
478 * @param requestId The id of the download to remove
479 * @param fileData The data to verify the file contains
480 */
481 protected void verifyAndCleanupSingleFileDownload(long requestId, byte[] fileData)
482 throws Exception {
483 int fileSize = fileData.length;
484 ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(requestId);
485 Cursor cursor = mDownloadManager.query(new Query().setFilterById(requestId));
486
487 try {
488 assertEquals(1, cursor.getCount());
489 assertTrue(cursor.moveToFirst());
490
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700491 verifyFileSize(pfd, fileSize);
492 verifyFileContents(pfd, fileData);
493 } finally {
494 pfd.close();
495 cursor.close();
496 mDownloadManager.remove(requestId);
497 }
498 }
499
500 /**
501 * Enables or disables WiFi.
502 *
503 * Note: Needs the following permissions:
504 * android.permission.ACCESS_WIFI_STATE
505 * android.permission.CHANGE_WIFI_STATE
506 * @param enable true if it should be enabled, false if it should be disabled
507 */
508 protected void setWiFiStateOn(boolean enable) throws Exception {
Neal Nguyenbd06f022010-10-20 17:43:15 -0700509 Log.i(LOG_TAG, "Setting WiFi State to: " + enable);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700510 WifiManager manager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
511
512 manager.setWifiEnabled(enable);
513
514 String timeoutMessage = "Timed out waiting for Wifi to be "
515 + (enable ? "enabled!" : "disabled!");
516
517 WiFiChangedReceiver receiver = new WiFiChangedReceiver(mContext);
518 mContext.registerReceiver(receiver, new IntentFilter(
519 ConnectivityManager.CONNECTIVITY_ACTION));
520
521 synchronized (receiver) {
522 long timeoutTime = SystemClock.elapsedRealtime() + DEFAULT_MAX_WAIT_TIME;
523 boolean timedOut = false;
524
525 while (receiver.getWiFiIsOn() != enable && !timedOut) {
526 try {
Neal Nguyenbd06f022010-10-20 17:43:15 -0700527 receiver.wait(DEFAULT_WAIT_POLL_TIME);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700528
529 if (SystemClock.elapsedRealtime() > timeoutTime) {
530 timedOut = true;
531 }
532 }
533 catch (InterruptedException e) {
534 // ignore InterruptedExceptions
535 }
536 }
537 if (timedOut) {
538 fail(timeoutMessage);
539 }
540 }
541 assertEquals(enable, receiver.getWiFiIsOn());
542 }
543
544 /**
545 * Helper to enables or disables airplane mode. If successful, it also broadcasts an intent
546 * indicating that the mode has changed.
547 *
548 * Note: Needs the following permission:
549 * android.permission.WRITE_SETTINGS
550 * @param enable true if airplane mode should be ON, false if it should be OFF
551 */
552 protected void setAirplaneModeOn(boolean enable) throws Exception {
553 int state = enable ? 1 : 0;
554
555 // Change the system setting
Christopher Tatec09cdce2012-09-10 16:50:14 -0700556 Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON,
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700557 state);
558
559 String timeoutMessage = "Timed out waiting for airplane mode to be " +
560 (enable ? "enabled!" : "disabled!");
561
562 // wait for airplane mode to change state
563 int currentWaitTime = 0;
Christopher Tatec09cdce2012-09-10 16:50:14 -0700564 while (Settings.Global.getInt(mContext.getContentResolver(),
565 Settings.Global.AIRPLANE_MODE_ON, -1) != state) {
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700566 timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME, DEFAULT_MAX_WAIT_TIME,
567 timeoutMessage);
568 }
569
570 // Post the intent
571 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
572 intent.putExtra("state", true);
Christopher Tatec09cdce2012-09-10 16:50:14 -0700573 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700574 }
575
576 /**
577 * Helper to create a large file of random data on the SD card.
578 *
579 * @param filename (optional) The name of the file to create on the SD card; pass in null to
580 * use a default temp filename.
581 * @param type The type of file to create
582 * @param subdirectory If not null, the subdirectory under the SD card where the file should go
583 * @return The File that was created
584 * @throws IOException if there was an error while creating the file.
585 */
586 protected File createFileOnSD(String filename, long fileSize, DataType type,
587 String subdirectory) throws IOException {
588
589 // Build up the file path and name
590 String sdPath = Environment.getExternalStorageDirectory().getPath();
591 StringBuilder fullPath = new StringBuilder(sdPath);
592 if (subdirectory != null) {
593 fullPath.append(File.separatorChar).append(subdirectory);
594 }
595
596 File file = null;
597 if (filename == null) {
598 file = File.createTempFile("DMTEST_", null, new File(fullPath.toString()));
599 }
600 else {
601 fullPath.append(File.separatorChar).append(filename);
602 file = new File(fullPath.toString());
603 file.createNewFile();
604 }
605
606 // Fill the file with random data
607 DataOutputStream output = new DataOutputStream(new FileOutputStream(file));
608 final int CHUNK_SIZE = 1000000; // copy random data in 1000000-char chunks
609 long remaining = fileSize;
610 int nextChunkSize = CHUNK_SIZE;
611 byte[] randomData = null;
612 Random rng = new LoggingRng();
Vasu Nori6f35c0e2010-12-17 17:08:58 -0800613 byte[] chunkSizeData = generateData(nextChunkSize, type, rng);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700614
615 try {
616 while (remaining > 0) {
617 if (remaining < CHUNK_SIZE) {
618 nextChunkSize = (int)remaining;
619 remaining = 0;
Vasu Nori6f35c0e2010-12-17 17:08:58 -0800620 randomData = generateData(nextChunkSize, type, rng);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700621 }
622 else {
623 remaining -= CHUNK_SIZE;
Vasu Nori6f35c0e2010-12-17 17:08:58 -0800624 randomData = chunkSizeData;
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700625 }
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700626 output.write(randomData);
Vasu Nori6f35c0e2010-12-17 17:08:58 -0800627 Log.i(TAG, "while creating " + fileSize + " file, " +
628 "remaining bytes to be written: " + remaining);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700629 }
630 } catch (IOException e) {
631 Log.e(LOG_TAG, "Error writing to file " + file.getAbsolutePath());
632 file.delete();
633 throw e;
634 } finally {
635 output.close();
636 }
637 return file;
638 }
639
640 /**
641 * Helper to wait for a particular download to finish, or else a timeout to occur
642 *
Neal Nguyen65c36e62010-10-24 18:02:45 -0700643 * Does not wait for a receiver notification of the download.
644 *
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700645 * @param id The download id to query on (wait for)
646 */
Neal Nguyen65c36e62010-10-24 18:02:45 -0700647 protected void waitForDownloadOrTimeout_skipNotification(long id) throws TimeoutException,
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700648 InterruptedException {
649 waitForDownloadOrTimeout(id, WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
650 }
651
652 /**
653 * Helper to wait for a particular download to finish, or else a timeout to occur
654 *
Neal Nguyen65c36e62010-10-24 18:02:45 -0700655 * Also guarantees a notification has been posted for the download.
656 *
657 * @param id The download id to query on (wait for)
658 */
659 protected void waitForDownloadOrTimeout(long id) throws TimeoutException,
660 InterruptedException {
661 waitForDownloadOrTimeout_skipNotification(id);
662 waitForReceiverNotifications(1);
663 }
664
665 /**
666 * Helper to wait for a particular download to finish, or else a timeout to occur
667 *
668 * Also guarantees a notification has been posted for the download.
669 *
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700670 * @param id The download id to query on (wait for)
671 * @param poll The amount of time to wait
672 * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
673 */
674 protected void waitForDownloadOrTimeout(long id, long poll, long timeoutMillis)
675 throws TimeoutException, InterruptedException {
676 doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
Neal Nguyen65c36e62010-10-24 18:02:45 -0700677 waitForReceiverNotifications(1);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700678 }
679
680 /**
681 * Helper to wait for all downloads to finish, or else a specified timeout to occur
682 *
Neal Nguyen65c36e62010-10-24 18:02:45 -0700683 * Makes no guaranee that notifications have been posted for all downloads.
684 *
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700685 * @param poll The amount of time to wait
686 * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
687 */
688 protected void waitForDownloadsOrTimeout(long poll, long timeoutMillis) throws TimeoutException,
689 InterruptedException {
690 doWaitForDownloadsOrTimeout(new Query(), poll, timeoutMillis);
691 }
692
693 /**
694 * Helper to wait for all downloads to finish, or else a timeout to occur, but does not throw
695 *
Neal Nguyen65c36e62010-10-24 18:02:45 -0700696 * Also guarantees a notification has been posted for the download.
697 *
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700698 * @param id The id of the download to query against
699 * @param poll The amount of time to wait
700 * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
701 * @return true if download completed successfully (didn't timeout), false otherwise
702 */
703 protected boolean waitForDownloadOrTimeoutNoThrow(long id, long poll, long timeoutMillis) {
704 try {
705 doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
Neal Nguyen65c36e62010-10-24 18:02:45 -0700706 waitForReceiverNotifications(1);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700707 } catch (TimeoutException e) {
708 return false;
709 }
710 return true;
711 }
712
713 /**
714 * Helper function to synchronously wait, or timeout if the maximum threshold has been exceeded.
715 *
716 * @param currentTotalWaitTime The total time waited so far
717 * @param poll The amount of time to wait
718 * @param maxTimeoutMillis The total wait time threshold; if we've waited more than this long,
719 * we timeout and fail
720 * @param timedOutMessage The message to display in the failure message if we timeout
721 * @return The new total amount of time we've waited so far
722 * @throws TimeoutException if timed out waiting for SD card to mount
723 */
724 protected int timeoutWait(int currentTotalWaitTime, long poll, long maxTimeoutMillis,
725 String timedOutMessage) throws TimeoutException {
726 long now = SystemClock.elapsedRealtime();
727 long end = now + poll;
728
729 // if we get InterruptedException's, ignore them and just keep sleeping
730 while (now < end) {
731 try {
732 Thread.sleep(end - now);
733 } catch (InterruptedException e) {
734 // ignore interrupted exceptions
735 }
736 now = SystemClock.elapsedRealtime();
737 }
738
739 currentTotalWaitTime += poll;
740 if (currentTotalWaitTime > maxTimeoutMillis) {
741 throw new TimeoutException(timedOutMessage);
742 }
743 return currentTotalWaitTime;
744 }
745
746 /**
747 * Helper to wait for all downloads to finish, or else a timeout to occur
748 *
749 * @param query The query to pass to the download manager
750 * @param poll The poll time to wait between checks
751 * @param timeoutMillis The max amount of time (in ms) to wait for the download(s) to complete
752 */
753 protected void doWaitForDownloadsOrTimeout(Query query, long poll, long timeoutMillis)
754 throws TimeoutException {
755 int currentWaitTime = 0;
756 while (true) {
757 query.setFilterByStatus(DownloadManager.STATUS_PENDING | DownloadManager.STATUS_PAUSED
758 | DownloadManager.STATUS_RUNNING);
759 Cursor cursor = mDownloadManager.query(query);
760
761 try {
Neal Nguyen65c36e62010-10-24 18:02:45 -0700762 if (cursor.getCount() == 0) {
763 Log.i(LOG_TAG, "All downloads should be done...");
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700764 break;
765 }
766 currentWaitTime = timeoutWait(currentWaitTime, poll, timeoutMillis,
767 "Timed out waiting for all downloads to finish");
768 } finally {
769 cursor.close();
770 }
771 }
772 }
773
774 /**
775 * Synchronously waits for external store to be mounted (eg: SD Card).
776 *
777 * @throws InterruptedException if interrupted
778 * @throws Exception if timed out waiting for SD card to mount
779 */
780 protected void waitForExternalStoreMount() throws Exception {
781 String extStorageState = Environment.getExternalStorageState();
782 int currentWaitTime = 0;
783 while (!extStorageState.equals(Environment.MEDIA_MOUNTED)) {
784 Log.i(LOG_TAG, "Waiting for SD card...");
785 currentWaitTime = timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME,
786 DEFAULT_MAX_WAIT_TIME, "Timed out waiting for SD Card to be ready!");
787 extStorageState = Environment.getExternalStorageState();
788 }
789 }
790
791 /**
792 * Synchronously waits for a download to start.
793 *
794 * @param dlRequest the download request id used by Download Manager to track the download.
795 * @throws Exception if timed out while waiting for SD card to mount
796 */
797 protected void waitForDownloadToStart(long dlRequest) throws Exception {
798 Cursor cursor = getCursor(dlRequest);
799 try {
800 int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
801 int value = cursor.getInt(columnIndex);
802 int currentWaitTime = 0;
803
804 while (value != DownloadManager.STATUS_RUNNING &&
805 (value != DownloadManager.STATUS_FAILED) &&
806 (value != DownloadManager.STATUS_SUCCESSFUL)) {
807 Log.i(LOG_TAG, "Waiting for download to start...");
808 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
809 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download to start!");
810 cursor.requery();
811 assertTrue(cursor.moveToFirst());
812 columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
813 value = cursor.getInt(columnIndex);
814 }
815 assertFalse("Download failed immediately after start",
816 value == DownloadManager.STATUS_FAILED);
817 } finally {
818 cursor.close();
819 }
820 }
821
822 /**
Neal Nguyen65c36e62010-10-24 18:02:45 -0700823 * Convenience function to wait for just 1 notification of a download.
824 *
825 * @throws Exception if timed out while waiting
826 */
827 protected void waitForReceiverNotification() throws Exception {
828 waitForReceiverNotifications(1);
829 }
830
831 /**
832 * Synchronously waits for our receiver to receive notification for a given number of
833 * downloads.
834 *
835 * @param targetNumber The number of notifications for unique downloads to wait for; pass in
836 * -1 to not wait for notification.
837 * @throws Exception if timed out while waiting
838 */
839 protected void waitForReceiverNotifications(int targetNumber) throws TimeoutException {
840 int count = mReceiver.numDownloadsCompleted();
841 int currentWaitTime = 0;
842
843 while (count < targetNumber) {
844 Log.i(LOG_TAG, "Waiting for notification of downloads...");
845 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
846 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download notifications!"
847 + " Received " + count + "notifications.");
848 count = mReceiver.numDownloadsCompleted();
849 }
850 }
851
852 /**
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700853 * Synchronously waits for a file to increase in size (such as to monitor that a download is
854 * progressing).
855 *
856 * @param file The file whose size to track.
857 * @throws Exception if timed out while waiting for the file to grow in size.
858 */
859 protected void waitForFileToGrow(File file) throws Exception {
860 int currentWaitTime = 0;
861
862 // File may not even exist yet, so wait until it does (or we timeout)
863 while (!file.exists()) {
864 Log.i(LOG_TAG, "Waiting for file to exist...");
865 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
866 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be created.");
867 }
868
869 // Get original file size...
870 long originalSize = file.length();
871
872 while (file.length() <= originalSize) {
873 Log.i(LOG_TAG, "Waiting for file to be written to...");
874 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
875 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be written to.");
876 }
877 }
878
879 /**
880 * Helper to remove all downloads that are registered with the DL Manager.
881 *
882 * Note: This gives us a clean slate b/c it includes downloads that are pending, running,
883 * paused, or have completed.
884 */
885 protected void removeAllCurrentDownloads() {
886 Log.i(LOG_TAG, "Removing all current registered downloads...");
Vasu Nori82e891b2010-12-15 14:42:30 -0800887 ArrayList<Long> ids = new ArrayList<Long>();
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700888 Cursor cursor = mDownloadManager.query(new Query());
889 try {
890 if (cursor.moveToFirst()) {
891 do {
892 int index = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
893 long downloadId = cursor.getLong(index);
Vasu Nori82e891b2010-12-15 14:42:30 -0800894 ids.add(downloadId);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700895 } while (cursor.moveToNext());
896 }
897 } finally {
898 cursor.close();
899 }
Vasu Nori82e891b2010-12-15 14:42:30 -0800900 // delete all ids
901 for (long id : ids) {
902 mDownloadManager.remove(id);
903 }
904 // make sure the database is empty
905 cursor = mDownloadManager.query(new Query());
906 try {
907 assertEquals(0, cursor.getCount());
908 } finally {
909 cursor.close();
910 }
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700911 }
912
913 /**
914 * Helper to perform a standard enqueue of data to the mock server.
Vasu Nori82e891b2010-12-15 14:42:30 -0800915 * download is performed to the downloads cache dir (NOT systemcache dir)
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700916 *
917 * @param body The body to return in the response from the server
918 */
919 protected long doStandardEnqueue(byte[] body) throws Exception {
Vasu Nori82e891b2010-12-15 14:42:30 -0800920 return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
921 }
922
923 protected long enqueueDownloadRequest(byte[] body, int location) throws Exception {
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700924 // Prepare the mock server with a standard response
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700925 mServer.enqueue(buildResponse(HTTP_OK, body));
Vasu Nori82e891b2010-12-15 14:42:30 -0800926 return doEnqueue(location);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700927 }
928
929 /**
930 * Helper to perform a standard enqueue of data to the mock server.
931 *
932 * @param body The body to return in the response from the server, contained in the file
933 */
934 protected long doStandardEnqueue(File body) throws Exception {
Vasu Nori82e891b2010-12-15 14:42:30 -0800935 return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
936 }
937
938 protected long enqueueDownloadRequest(File body, int location) throws Exception {
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700939 // Prepare the mock server with a standard response
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700940 mServer.enqueue(buildResponse(HTTP_OK, body));
Vasu Nori82e891b2010-12-15 14:42:30 -0800941 return doEnqueue(location);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700942 }
943
944 /**
945 * Helper to do the additional steps (setting title and Uri of default filename) when
946 * doing a standard enqueue request to the server.
947 */
948 protected long doCommonStandardEnqueue() throws Exception {
Vasu Nori82e891b2010-12-15 14:42:30 -0800949 return doEnqueue(DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
950 }
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700951
Vasu Nori82e891b2010-12-15 14:42:30 -0800952 private long doEnqueue(int location) throws Exception {
953 Uri uri = getServerUri(DEFAULT_FILENAME);
954 Request request = new Request(uri).setTitle(DEFAULT_FILENAME);
955 if (location == DOWNLOAD_TO_SYSTEM_CACHE) {
956 request.setDestinationToSystemCache();
957 }
958
959 return mDownloadManager.enqueue(request);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700960 }
961
962 /**
963 * Helper to verify an int value in a Cursor
964 *
965 * @param cursor The cursor containing the query results
966 * @param columnName The name of the column to query
967 * @param expected The expected int value
968 */
969 protected void verifyInt(Cursor cursor, String columnName, int expected) {
970 int index = cursor.getColumnIndex(columnName);
971 int actual = cursor.getInt(index);
Md Haquec6272b92016-03-08 14:51:33 -0800972 assertEquals(String.format("Expected = %d : Actual = %d", expected, actual), expected, actual);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700973 }
974
975 /**
976 * Helper to verify a String value in a Cursor
977 *
978 * @param cursor The cursor containing the query results
979 * @param columnName The name of the column to query
980 * @param expected The expected String value
981 */
982 protected void verifyString(Cursor cursor, String columnName, String expected) {
983 int index = cursor.getColumnIndex(columnName);
984 String actual = cursor.getString(index);
985 Log.i(LOG_TAG, ": " + actual);
986 assertEquals(expected, actual);
987 }
988
989 /**
990 * Performs a query based on ID and returns a Cursor for the query.
991 *
992 * @param id The id of the download in DL Manager; pass -1 to query all downloads
993 * @return A cursor for the query results
994 */
995 protected Cursor getCursor(long id) throws Exception {
996 Query query = new Query();
997 if (id != -1) {
998 query.setFilterById(id);
999 }
1000
1001 Cursor cursor = mDownloadManager.query(query);
1002 int currentWaitTime = 0;
1003
1004 try {
1005 while (!cursor.moveToFirst()) {
1006 Thread.sleep(DEFAULT_WAIT_POLL_TIME);
1007 currentWaitTime += DEFAULT_WAIT_POLL_TIME;
1008 if (currentWaitTime > DEFAULT_MAX_WAIT_TIME) {
1009 fail("timed out waiting for a non-null query result");
1010 }
1011 cursor.requery();
1012 }
1013 } catch (Exception e) {
1014 cursor.close();
1015 throw e;
1016 }
1017 return cursor;
1018 }
1019
Vasu Nori82e891b2010-12-15 14:42:30 -08001020 /**
1021 * Helper that does the actual basic download verification.
1022 */
1023 protected long doBasicDownload(byte[] blobData, int location) throws Exception {
1024 long dlRequest = enqueueDownloadRequest(blobData, location);
1025
1026 // wait for the download to complete
1027 waitForDownloadOrTimeout(dlRequest);
1028
1029 assertEquals(1, mReceiver.numDownloadsCompleted());
1030 return dlRequest;
1031 }
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -07001032}