blob: af2a9446f348317708bb526b270b797d1d7d44e9 [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
84 protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 5 * 60 * 1000; // 5 minutes
Neal Nguyenbd06f022010-10-20 17:43:15 -070085 protected static final int MAX_WAIT_FOR_LARGE_DOWNLOAD_TIME = 15 * 60 * 1000; // 15 minutes
Neal Nguyen40ef0f42010-08-09 14:08:26 -070086
Vasu Nori82e891b2010-12-15 14:42:30 -080087 protected static final int DOWNLOAD_TO_SYSTEM_CACHE = 1;
88 protected static final int DOWNLOAD_TO_DOWNLOAD_CACHE_DIR = 2;
89
Neal Nguyen40ef0f42010-08-09 14:08:26 -070090 // Just a few popular file types used to return from a download
91 protected enum DownloadFileType {
92 PLAINTEXT,
93 APK,
94 GIF,
95 GARBAGE,
96 UNRECOGNIZED,
97 ZIP
98 }
99
100 protected enum DataType {
101 TEXT,
102 BINARY
103 }
104
105 public static class LoggingRng extends Random {
106
107 /**
108 * Constructor
109 *
110 * Creates RNG with self-generated seed value.
111 */
112 public LoggingRng() {
113 this(SystemClock.uptimeMillis());
114 }
115
116 /**
117 * Constructor
118 *
119 * Creats RNG with given initial seed value
120
121 * @param seed The initial seed value
122 */
123 public LoggingRng(long seed) {
124 super(seed);
125 Log.i(LOG_TAG, "Seeding RNG with value: " + seed);
126 }
127 }
128
129 public static class MultipleDownloadsCompletedReceiver extends BroadcastReceiver {
130 private volatile int mNumDownloadsCompleted = 0;
Neal Nguyendf7a8652010-09-09 14:54:26 -0700131 private Set<Long> downloadIds = Collections.synchronizedSet(new HashSet<Long>());
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700132
133 /**
134 * {@inheritDoc}
135 */
136 @Override
137 public void onReceive(Context context, Intent intent) {
138 if (intent.getAction().equalsIgnoreCase(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
Neal Nguyen63e5d792010-10-04 11:13:33 -0700139 synchronized(this) {
Neal Nguyen65c36e62010-10-24 18:02:45 -0700140 long id = intent.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
141 Log.i(LOG_TAG, "Received Notification for download: " + id);
142 if (!downloadIds.contains(id)) {
143 ++mNumDownloadsCompleted;
144 Log.i(LOG_TAG, "MultipleDownloadsCompletedReceiver got intent: " +
145 intent.getAction() + " --> total count: " + mNumDownloadsCompleted);
146 downloadIds.add(id);
147
148 DownloadManager dm = (DownloadManager)context.getSystemService(
149 Context.DOWNLOAD_SERVICE);
150
151 Cursor cursor = dm.query(new Query().setFilterById(id));
152 try {
153 if (cursor.moveToFirst()) {
154 int status = cursor.getInt(cursor.getColumnIndex(
155 DownloadManager.COLUMN_STATUS));
156 Log.i(LOG_TAG, "Download status is: " + status);
157 } else {
158 fail("No status found for completed download!");
159 }
160 } finally {
161 cursor.close();
162 }
163 } else {
164 Log.i(LOG_TAG, "Notification for id: " + id + " has already been made.");
165 }
Neal Nguyen63e5d792010-10-04 11:13:33 -0700166 }
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700167 }
168 }
169
170 /**
171 * Gets the number of times the {@link #onReceive} callback has been called for the
172 * {@link DownloadManager.ACTION_DOWNLOAD_COMPLETED} action, indicating the number of
173 * downloads completed thus far.
174 *
175 * @return the number of downloads completed so far.
176 */
177 public int numDownloadsCompleted() {
178 return mNumDownloadsCompleted;
179 }
Neal Nguyendf7a8652010-09-09 14:54:26 -0700180
181 /**
182 * Gets the list of download IDs.
183 * @return A Set<Long> with the ids of the completed downloads.
184 */
185 public Set<Long> getDownloadIds() {
Neal Nguyenbd06f022010-10-20 17:43:15 -0700186 synchronized(this) {
Neal Nguyendf7a8652010-09-09 14:54:26 -0700187 Set<Long> returnIds = new HashSet<Long>(downloadIds);
188 return returnIds;
189 }
190 }
191
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700192 }
193
194 public static class WiFiChangedReceiver extends BroadcastReceiver {
195 private Context mContext = null;
196
197 /**
198 * Constructor
199 *
200 * Sets the current state of WiFi.
201 *
202 * @param context The current app {@link Context}.
203 */
204 public WiFiChangedReceiver(Context context) {
205 mContext = context;
206 }
207
208 /**
209 * {@inheritDoc}
210 */
211 @Override
212 public void onReceive(Context context, Intent intent) {
213 if (intent.getAction().equalsIgnoreCase(ConnectivityManager.CONNECTIVITY_ACTION)) {
214 Log.i(LOG_TAG, "ConnectivityManager state change: " + intent.getAction());
215 synchronized (this) {
216 this.notify();
217 }
218 }
219 }
220
221 /**
222 * Gets the current state of WiFi.
223 *
224 * @return Returns true if WiFi is on, false otherwise.
225 */
226 public boolean getWiFiIsOn() {
227 ConnectivityManager connManager = (ConnectivityManager)mContext.getSystemService(
228 Context.CONNECTIVITY_SERVICE);
229 NetworkInfo info = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
Neal Nguyenbd06f022010-10-20 17:43:15 -0700230 Log.i(LOG_TAG, "WiFi Connection state is currently: " + info.isConnected());
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700231 return info.isConnected();
232 }
233 }
234
235 /**
236 * {@inheritDoc}
237 */
238 @Override
239 public void setUp() throws Exception {
240 mContext = getInstrumentation().getContext();
241 mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE);
242 mServer = new MockWebServer();
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700243 mServer.play();
Neal Nguyen63e5d792010-10-04 11:13:33 -0700244 mReceiver = registerNewMultipleDownloadsReceiver();
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700245 // Note: callers overriding this should call mServer.play() with the desired port #
246 }
247
248 /**
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700249 * Helper to build a response from the MockWebServer with no body.
Neal Nguyendf7a8652010-09-09 14:54:26 -0700250 *
251 * @param status The HTTP status code to return for this response
252 * @return Returns the mock web server response that was queued (which can be modified)
253 */
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700254 protected MockResponse buildResponse(int status) {
255 MockResponse response = new MockResponse().setResponseCode(status);
256 response.setHeader("Content-type", mFileType);
257 return response;
Neal Nguyendf7a8652010-09-09 14:54:26 -0700258 }
259
260 /**
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700261 * Helper to build a response from the MockWebServer.
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700262 *
263 * @param status The HTTP status code to return for this response
264 * @param body The body to return in this response
265 * @return Returns the mock web server response that was queued (which can be modified)
266 */
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700267 protected MockResponse buildResponse(int status, byte[] body) {
268 return buildResponse(status).setBody(body);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700269 }
270
271 /**
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700272 * Helper to build a response from the MockWebServer.
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700273 *
274 * @param status The HTTP status code to return for this response
275 * @param bodyFile The body to return in this response
276 * @return Returns the mock web server response that was queued (which can be modified)
277 */
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700278 protected MockResponse buildResponse(int status, File bodyFile)
279 throws FileNotFoundException, IOException {
280 final byte[] body = Streams.readFully(new FileInputStream(bodyFile));
281 return buildResponse(status).setBody(body);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700282 }
283
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700284 protected void enqueueResponse(MockResponse resp) {
285 mServer.enqueue(resp);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700286 }
287
288 /**
289 * Helper to generate a random blob of bytes.
290 *
291 * @param size The size of the data to generate
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700292 * @param type The type of data to generate: currently, one of {@link DataType#TEXT} or
293 * {@link DataType#BINARY}.
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700294 * @return The random data that is generated.
295 */
296 protected byte[] generateData(int size, DataType type) {
297 return generateData(size, type, null);
298 }
299
300 /**
301 * Helper to generate a random blob of bytes using a given RNG.
302 *
303 * @param size The size of the data to generate
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700304 * @param type The type of data to generate: currently, one of {@link DataType#TEXT} or
305 * {@link DataType#BINARY}.
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700306 * @param rng (optional) The RNG to use; pass null to use
307 * @return The random data that is generated.
308 */
309 protected byte[] generateData(int size, DataType type, Random rng) {
310 int min = Byte.MIN_VALUE;
311 int max = Byte.MAX_VALUE;
312
313 // Only use chars in the HTTP ASCII printable character range for Text
314 if (type == DataType.TEXT) {
315 min = 32;
316 max = 126;
317 }
318 byte[] result = new byte[size];
319 Log.i(LOG_TAG, "Generating data of size: " + size);
320
321 if (rng == null) {
322 rng = new LoggingRng();
323 }
324
325 for (int i = 0; i < size; ++i) {
326 result[i] = (byte) (min + rng.nextInt(max - min + 1));
327 }
328 return result;
329 }
330
331 /**
332 * Helper to verify the size of a file.
333 *
334 * @param pfd The input file to compare the size of
335 * @param size The expected size of the file
336 */
337 protected void verifyFileSize(ParcelFileDescriptor pfd, long size) {
338 assertEquals(pfd.getStatSize(), size);
339 }
340
341 /**
342 * Helper to verify the contents of a downloaded file versus a byte[].
343 *
344 * @param actual The file of whose contents to verify
345 * @param expected The data we expect to find in the aforementioned file
346 * @throws IOException if there was a problem reading from the file
347 */
348 protected void verifyFileContents(ParcelFileDescriptor actual, byte[] expected)
349 throws IOException {
350 AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(actual);
351 long fileSize = actual.getStatSize();
352
353 assertTrue(fileSize <= Integer.MAX_VALUE);
354 assertEquals(expected.length, fileSize);
355
356 byte[] actualData = new byte[expected.length];
357 assertEquals(input.read(actualData), fileSize);
358 compareByteArrays(actualData, expected);
359 }
360
361 /**
362 * Helper to compare 2 byte arrays.
363 *
364 * @param actual The array whose data we want to verify
365 * @param expected The array of data we expect to see
366 */
367 protected void compareByteArrays(byte[] actual, byte[] expected) {
368 assertEquals(actual.length, expected.length);
369 int length = actual.length;
370 for (int i = 0; i < length; ++i) {
371 // assert has a bit of overhead, so only do the assert when the values are not the same
372 if (actual[i] != expected[i]) {
373 fail("Byte arrays are not equal.");
374 }
375 }
376 }
377
378 /**
379 * Verifies the contents of a downloaded file versus the contents of a File.
380 *
381 * @param pfd The file whose data we want to verify
382 * @param file The file containing the data we expect to see in the aforementioned file
383 * @throws IOException If there was a problem reading either of the two files
384 */
385 protected void verifyFileContents(ParcelFileDescriptor pfd, File file) throws IOException {
386 byte[] actual = new byte[FILE_BLOCK_READ_SIZE];
387 byte[] expected = new byte[FILE_BLOCK_READ_SIZE];
388
389 AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
390
391 assertEquals(file.length(), pfd.getStatSize());
392
393 DataInputStream inFile = new DataInputStream(new FileInputStream(file));
394 int actualRead = 0;
395 int expectedRead = 0;
396
397 while (((actualRead = input.read(actual)) != -1) &&
398 ((expectedRead = inFile.read(expected)) != -1)) {
399 assertEquals(actualRead, expectedRead);
400 compareByteArrays(actual, expected);
401 }
402 }
403
404 /**
405 * Sets the MIME type of file that will be served from the mock server
406 *
407 * @param type The MIME type to return from the server
408 */
409 protected void setServerMimeType(DownloadFileType type) {
410 mFileType = getMimeMapping(type);
411 }
412
413 /**
414 * Gets the MIME content string for a given type
415 *
416 * @param type The MIME type to return
417 * @return the String representation of that MIME content type
418 */
419 protected String getMimeMapping(DownloadFileType type) {
420 switch (type) {
421 case APK:
422 return "application/vnd.android.package-archive";
423 case GIF:
424 return "image/gif";
425 case ZIP:
426 return "application/x-zip-compressed";
427 case GARBAGE:
428 return "zip\\pidy/doo/da";
429 case UNRECOGNIZED:
430 return "application/new.undefined.type.of.app";
431 }
432 return "text/plain";
433 }
434
435 /**
436 * Gets the Uri that should be used to access the mock server
437 *
438 * @param filename The name of the file to try to retrieve from the mock server
439 * @return the Uri to use for access the file on the mock server
440 */
441 protected Uri getServerUri(String filename) throws Exception {
442 URL url = mServer.getUrl("/" + filename);
443 return Uri.parse(url.toString());
444 }
445
446 /**
447 * Gets the Uri that should be used to access the mock server
448 *
449 * @param filename The name of the file to try to retrieve from the mock server
450 * @return the Uri to use for access the file on the mock server
451 */
452 protected void logDBColumnData(Cursor cursor, String column) {
453 int index = cursor.getColumnIndex(column);
454 Log.i(LOG_TAG, "columnName: " + column);
455 Log.i(LOG_TAG, "columnValue: " + cursor.getString(index));
456 }
457
458 /**
459 * Helper to create and register a new MultipleDownloadCompletedReciever
460 *
461 * This is used to track many simultaneous downloads by keeping count of all the downloads
462 * that have completed.
463 *
464 * @return A new receiver that records and can be queried on how many downloads have completed.
465 */
466 protected MultipleDownloadsCompletedReceiver registerNewMultipleDownloadsReceiver() {
467 MultipleDownloadsCompletedReceiver receiver = new MultipleDownloadsCompletedReceiver();
468 mContext.registerReceiver(receiver, new IntentFilter(
469 DownloadManager.ACTION_DOWNLOAD_COMPLETE));
470 return receiver;
471 }
472
473 /**
474 * Helper to verify a standard single-file download from the mock server, and clean up after
475 * verification
476 *
477 * Note that this also calls the Download manager's remove, which cleans up the file from cache.
478 *
479 * @param requestId The id of the download to remove
480 * @param fileData The data to verify the file contains
481 */
482 protected void verifyAndCleanupSingleFileDownload(long requestId, byte[] fileData)
483 throws Exception {
484 int fileSize = fileData.length;
485 ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(requestId);
486 Cursor cursor = mDownloadManager.query(new Query().setFilterById(requestId));
487
488 try {
489 assertEquals(1, cursor.getCount());
490 assertTrue(cursor.moveToFirst());
491
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700492 verifyFileSize(pfd, fileSize);
493 verifyFileContents(pfd, fileData);
494 } finally {
495 pfd.close();
496 cursor.close();
497 mDownloadManager.remove(requestId);
498 }
499 }
500
501 /**
502 * Enables or disables WiFi.
503 *
504 * Note: Needs the following permissions:
505 * android.permission.ACCESS_WIFI_STATE
506 * android.permission.CHANGE_WIFI_STATE
507 * @param enable true if it should be enabled, false if it should be disabled
508 */
509 protected void setWiFiStateOn(boolean enable) throws Exception {
Neal Nguyenbd06f022010-10-20 17:43:15 -0700510 Log.i(LOG_TAG, "Setting WiFi State to: " + enable);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700511 WifiManager manager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
512
513 manager.setWifiEnabled(enable);
514
515 String timeoutMessage = "Timed out waiting for Wifi to be "
516 + (enable ? "enabled!" : "disabled!");
517
518 WiFiChangedReceiver receiver = new WiFiChangedReceiver(mContext);
519 mContext.registerReceiver(receiver, new IntentFilter(
520 ConnectivityManager.CONNECTIVITY_ACTION));
521
522 synchronized (receiver) {
523 long timeoutTime = SystemClock.elapsedRealtime() + DEFAULT_MAX_WAIT_TIME;
524 boolean timedOut = false;
525
526 while (receiver.getWiFiIsOn() != enable && !timedOut) {
527 try {
Neal Nguyenbd06f022010-10-20 17:43:15 -0700528 receiver.wait(DEFAULT_WAIT_POLL_TIME);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700529
530 if (SystemClock.elapsedRealtime() > timeoutTime) {
531 timedOut = true;
532 }
533 }
534 catch (InterruptedException e) {
535 // ignore InterruptedExceptions
536 }
537 }
538 if (timedOut) {
539 fail(timeoutMessage);
540 }
541 }
542 assertEquals(enable, receiver.getWiFiIsOn());
543 }
544
545 /**
546 * Helper to enables or disables airplane mode. If successful, it also broadcasts an intent
547 * indicating that the mode has changed.
548 *
549 * Note: Needs the following permission:
550 * android.permission.WRITE_SETTINGS
551 * @param enable true if airplane mode should be ON, false if it should be OFF
552 */
553 protected void setAirplaneModeOn(boolean enable) throws Exception {
554 int state = enable ? 1 : 0;
555
556 // Change the system setting
Christopher Tatec09cdce2012-09-10 16:50:14 -0700557 Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON,
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700558 state);
559
560 String timeoutMessage = "Timed out waiting for airplane mode to be " +
561 (enable ? "enabled!" : "disabled!");
562
563 // wait for airplane mode to change state
564 int currentWaitTime = 0;
Christopher Tatec09cdce2012-09-10 16:50:14 -0700565 while (Settings.Global.getInt(mContext.getContentResolver(),
566 Settings.Global.AIRPLANE_MODE_ON, -1) != state) {
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700567 timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME, DEFAULT_MAX_WAIT_TIME,
568 timeoutMessage);
569 }
570
571 // Post the intent
572 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
573 intent.putExtra("state", true);
Christopher Tatec09cdce2012-09-10 16:50:14 -0700574 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700575 }
576
577 /**
578 * Helper to create a large file of random data on the SD card.
579 *
580 * @param filename (optional) The name of the file to create on the SD card; pass in null to
581 * use a default temp filename.
582 * @param type The type of file to create
583 * @param subdirectory If not null, the subdirectory under the SD card where the file should go
584 * @return The File that was created
585 * @throws IOException if there was an error while creating the file.
586 */
587 protected File createFileOnSD(String filename, long fileSize, DataType type,
588 String subdirectory) throws IOException {
589
590 // Build up the file path and name
591 String sdPath = Environment.getExternalStorageDirectory().getPath();
592 StringBuilder fullPath = new StringBuilder(sdPath);
593 if (subdirectory != null) {
594 fullPath.append(File.separatorChar).append(subdirectory);
595 }
596
597 File file = null;
598 if (filename == null) {
599 file = File.createTempFile("DMTEST_", null, new File(fullPath.toString()));
600 }
601 else {
602 fullPath.append(File.separatorChar).append(filename);
603 file = new File(fullPath.toString());
604 file.createNewFile();
605 }
606
607 // Fill the file with random data
608 DataOutputStream output = new DataOutputStream(new FileOutputStream(file));
609 final int CHUNK_SIZE = 1000000; // copy random data in 1000000-char chunks
610 long remaining = fileSize;
611 int nextChunkSize = CHUNK_SIZE;
612 byte[] randomData = null;
613 Random rng = new LoggingRng();
Vasu Nori6f35c0e2010-12-17 17:08:58 -0800614 byte[] chunkSizeData = generateData(nextChunkSize, type, rng);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700615
616 try {
617 while (remaining > 0) {
618 if (remaining < CHUNK_SIZE) {
619 nextChunkSize = (int)remaining;
620 remaining = 0;
Vasu Nori6f35c0e2010-12-17 17:08:58 -0800621 randomData = generateData(nextChunkSize, type, rng);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700622 }
623 else {
624 remaining -= CHUNK_SIZE;
Vasu Nori6f35c0e2010-12-17 17:08:58 -0800625 randomData = chunkSizeData;
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700626 }
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700627 output.write(randomData);
Vasu Nori6f35c0e2010-12-17 17:08:58 -0800628 Log.i(TAG, "while creating " + fileSize + " file, " +
629 "remaining bytes to be written: " + remaining);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700630 }
631 } catch (IOException e) {
632 Log.e(LOG_TAG, "Error writing to file " + file.getAbsolutePath());
633 file.delete();
634 throw e;
635 } finally {
636 output.close();
637 }
638 return file;
639 }
640
641 /**
642 * Helper to wait for a particular download to finish, or else a timeout to occur
643 *
Neal Nguyen65c36e62010-10-24 18:02:45 -0700644 * Does not wait for a receiver notification of the download.
645 *
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700646 * @param id The download id to query on (wait for)
647 */
Neal Nguyen65c36e62010-10-24 18:02:45 -0700648 protected void waitForDownloadOrTimeout_skipNotification(long id) throws TimeoutException,
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700649 InterruptedException {
650 waitForDownloadOrTimeout(id, WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
651 }
652
653 /**
654 * Helper to wait for a particular download to finish, or else a timeout to occur
655 *
Neal Nguyen65c36e62010-10-24 18:02:45 -0700656 * Also guarantees a notification has been posted for the download.
657 *
658 * @param id The download id to query on (wait for)
659 */
660 protected void waitForDownloadOrTimeout(long id) throws TimeoutException,
661 InterruptedException {
662 waitForDownloadOrTimeout_skipNotification(id);
663 waitForReceiverNotifications(1);
664 }
665
666 /**
667 * Helper to wait for a particular download to finish, or else a timeout to occur
668 *
669 * Also guarantees a notification has been posted for the download.
670 *
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700671 * @param id The download id to query on (wait for)
672 * @param poll The amount of time to wait
673 * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
674 */
675 protected void waitForDownloadOrTimeout(long id, long poll, long timeoutMillis)
676 throws TimeoutException, InterruptedException {
677 doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
Neal Nguyen65c36e62010-10-24 18:02:45 -0700678 waitForReceiverNotifications(1);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700679 }
680
681 /**
682 * Helper to wait for all downloads to finish, or else a specified timeout to occur
683 *
Neal Nguyen65c36e62010-10-24 18:02:45 -0700684 * Makes no guaranee that notifications have been posted for all downloads.
685 *
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700686 * @param poll The amount of time to wait
687 * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
688 */
689 protected void waitForDownloadsOrTimeout(long poll, long timeoutMillis) throws TimeoutException,
690 InterruptedException {
691 doWaitForDownloadsOrTimeout(new Query(), poll, timeoutMillis);
692 }
693
694 /**
695 * Helper to wait for all downloads to finish, or else a timeout to occur, but does not throw
696 *
Neal Nguyen65c36e62010-10-24 18:02:45 -0700697 * Also guarantees a notification has been posted for the download.
698 *
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700699 * @param id The id of the download to query against
700 * @param poll The amount of time to wait
701 * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
702 * @return true if download completed successfully (didn't timeout), false otherwise
703 */
704 protected boolean waitForDownloadOrTimeoutNoThrow(long id, long poll, long timeoutMillis) {
705 try {
706 doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
Neal Nguyen65c36e62010-10-24 18:02:45 -0700707 waitForReceiverNotifications(1);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700708 } catch (TimeoutException e) {
709 return false;
710 }
711 return true;
712 }
713
714 /**
715 * Helper function to synchronously wait, or timeout if the maximum threshold has been exceeded.
716 *
717 * @param currentTotalWaitTime The total time waited so far
718 * @param poll The amount of time to wait
719 * @param maxTimeoutMillis The total wait time threshold; if we've waited more than this long,
720 * we timeout and fail
721 * @param timedOutMessage The message to display in the failure message if we timeout
722 * @return The new total amount of time we've waited so far
723 * @throws TimeoutException if timed out waiting for SD card to mount
724 */
725 protected int timeoutWait(int currentTotalWaitTime, long poll, long maxTimeoutMillis,
726 String timedOutMessage) throws TimeoutException {
727 long now = SystemClock.elapsedRealtime();
728 long end = now + poll;
729
730 // if we get InterruptedException's, ignore them and just keep sleeping
731 while (now < end) {
732 try {
733 Thread.sleep(end - now);
734 } catch (InterruptedException e) {
735 // ignore interrupted exceptions
736 }
737 now = SystemClock.elapsedRealtime();
738 }
739
740 currentTotalWaitTime += poll;
741 if (currentTotalWaitTime > maxTimeoutMillis) {
742 throw new TimeoutException(timedOutMessage);
743 }
744 return currentTotalWaitTime;
745 }
746
747 /**
748 * Helper to wait for all downloads to finish, or else a timeout to occur
749 *
750 * @param query The query to pass to the download manager
751 * @param poll The poll time to wait between checks
752 * @param timeoutMillis The max amount of time (in ms) to wait for the download(s) to complete
753 */
754 protected void doWaitForDownloadsOrTimeout(Query query, long poll, long timeoutMillis)
755 throws TimeoutException {
756 int currentWaitTime = 0;
757 while (true) {
758 query.setFilterByStatus(DownloadManager.STATUS_PENDING | DownloadManager.STATUS_PAUSED
759 | DownloadManager.STATUS_RUNNING);
760 Cursor cursor = mDownloadManager.query(query);
761
762 try {
Neal Nguyen65c36e62010-10-24 18:02:45 -0700763 if (cursor.getCount() == 0) {
764 Log.i(LOG_TAG, "All downloads should be done...");
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700765 break;
766 }
767 currentWaitTime = timeoutWait(currentWaitTime, poll, timeoutMillis,
768 "Timed out waiting for all downloads to finish");
769 } finally {
770 cursor.close();
771 }
772 }
773 }
774
775 /**
776 * Synchronously waits for external store to be mounted (eg: SD Card).
777 *
778 * @throws InterruptedException if interrupted
779 * @throws Exception if timed out waiting for SD card to mount
780 */
781 protected void waitForExternalStoreMount() throws Exception {
782 String extStorageState = Environment.getExternalStorageState();
783 int currentWaitTime = 0;
784 while (!extStorageState.equals(Environment.MEDIA_MOUNTED)) {
785 Log.i(LOG_TAG, "Waiting for SD card...");
786 currentWaitTime = timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME,
787 DEFAULT_MAX_WAIT_TIME, "Timed out waiting for SD Card to be ready!");
788 extStorageState = Environment.getExternalStorageState();
789 }
790 }
791
792 /**
793 * Synchronously waits for a download to start.
794 *
795 * @param dlRequest the download request id used by Download Manager to track the download.
796 * @throws Exception if timed out while waiting for SD card to mount
797 */
798 protected void waitForDownloadToStart(long dlRequest) throws Exception {
799 Cursor cursor = getCursor(dlRequest);
800 try {
801 int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
802 int value = cursor.getInt(columnIndex);
803 int currentWaitTime = 0;
804
805 while (value != DownloadManager.STATUS_RUNNING &&
806 (value != DownloadManager.STATUS_FAILED) &&
807 (value != DownloadManager.STATUS_SUCCESSFUL)) {
808 Log.i(LOG_TAG, "Waiting for download to start...");
809 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
810 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download to start!");
811 cursor.requery();
812 assertTrue(cursor.moveToFirst());
813 columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
814 value = cursor.getInt(columnIndex);
815 }
816 assertFalse("Download failed immediately after start",
817 value == DownloadManager.STATUS_FAILED);
818 } finally {
819 cursor.close();
820 }
821 }
822
823 /**
Neal Nguyen65c36e62010-10-24 18:02:45 -0700824 * Convenience function to wait for just 1 notification of a download.
825 *
826 * @throws Exception if timed out while waiting
827 */
828 protected void waitForReceiverNotification() throws Exception {
829 waitForReceiverNotifications(1);
830 }
831
832 /**
833 * Synchronously waits for our receiver to receive notification for a given number of
834 * downloads.
835 *
836 * @param targetNumber The number of notifications for unique downloads to wait for; pass in
837 * -1 to not wait for notification.
838 * @throws Exception if timed out while waiting
839 */
840 protected void waitForReceiverNotifications(int targetNumber) throws TimeoutException {
841 int count = mReceiver.numDownloadsCompleted();
842 int currentWaitTime = 0;
843
844 while (count < targetNumber) {
845 Log.i(LOG_TAG, "Waiting for notification of downloads...");
846 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
847 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download notifications!"
848 + " Received " + count + "notifications.");
849 count = mReceiver.numDownloadsCompleted();
850 }
851 }
852
853 /**
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700854 * Synchronously waits for a file to increase in size (such as to monitor that a download is
855 * progressing).
856 *
857 * @param file The file whose size to track.
858 * @throws Exception if timed out while waiting for the file to grow in size.
859 */
860 protected void waitForFileToGrow(File file) throws Exception {
861 int currentWaitTime = 0;
862
863 // File may not even exist yet, so wait until it does (or we timeout)
864 while (!file.exists()) {
865 Log.i(LOG_TAG, "Waiting for file to exist...");
866 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
867 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be created.");
868 }
869
870 // Get original file size...
871 long originalSize = file.length();
872
873 while (file.length() <= originalSize) {
874 Log.i(LOG_TAG, "Waiting for file to be written to...");
875 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
876 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be written to.");
877 }
878 }
879
880 /**
881 * Helper to remove all downloads that are registered with the DL Manager.
882 *
883 * Note: This gives us a clean slate b/c it includes downloads that are pending, running,
884 * paused, or have completed.
885 */
886 protected void removeAllCurrentDownloads() {
887 Log.i(LOG_TAG, "Removing all current registered downloads...");
Vasu Nori82e891b2010-12-15 14:42:30 -0800888 ArrayList<Long> ids = new ArrayList<Long>();
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700889 Cursor cursor = mDownloadManager.query(new Query());
890 try {
891 if (cursor.moveToFirst()) {
892 do {
893 int index = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
894 long downloadId = cursor.getLong(index);
Vasu Nori82e891b2010-12-15 14:42:30 -0800895 ids.add(downloadId);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700896 } while (cursor.moveToNext());
897 }
898 } finally {
899 cursor.close();
900 }
Vasu Nori82e891b2010-12-15 14:42:30 -0800901 // delete all ids
902 for (long id : ids) {
903 mDownloadManager.remove(id);
904 }
905 // make sure the database is empty
906 cursor = mDownloadManager.query(new Query());
907 try {
908 assertEquals(0, cursor.getCount());
909 } finally {
910 cursor.close();
911 }
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700912 }
913
914 /**
915 * Helper to perform a standard enqueue of data to the mock server.
Vasu Nori82e891b2010-12-15 14:42:30 -0800916 * download is performed to the downloads cache dir (NOT systemcache dir)
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700917 *
918 * @param body The body to return in the response from the server
919 */
920 protected long doStandardEnqueue(byte[] body) throws Exception {
Vasu Nori82e891b2010-12-15 14:42:30 -0800921 return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
922 }
923
924 protected long enqueueDownloadRequest(byte[] body, int location) throws Exception {
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700925 // Prepare the mock server with a standard response
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700926 mServer.enqueue(buildResponse(HTTP_OK, body));
Vasu Nori82e891b2010-12-15 14:42:30 -0800927 return doEnqueue(location);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700928 }
929
930 /**
931 * Helper to perform a standard enqueue of data to the mock server.
932 *
933 * @param body The body to return in the response from the server, contained in the file
934 */
935 protected long doStandardEnqueue(File body) throws Exception {
Vasu Nori82e891b2010-12-15 14:42:30 -0800936 return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
937 }
938
939 protected long enqueueDownloadRequest(File body, int location) throws Exception {
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700940 // Prepare the mock server with a standard response
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700941 mServer.enqueue(buildResponse(HTTP_OK, body));
Vasu Nori82e891b2010-12-15 14:42:30 -0800942 return doEnqueue(location);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700943 }
944
945 /**
946 * Helper to do the additional steps (setting title and Uri of default filename) when
947 * doing a standard enqueue request to the server.
948 */
949 protected long doCommonStandardEnqueue() throws Exception {
Vasu Nori82e891b2010-12-15 14:42:30 -0800950 return doEnqueue(DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
951 }
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700952
Vasu Nori82e891b2010-12-15 14:42:30 -0800953 private long doEnqueue(int location) throws Exception {
954 Uri uri = getServerUri(DEFAULT_FILENAME);
955 Request request = new Request(uri).setTitle(DEFAULT_FILENAME);
956 if (location == DOWNLOAD_TO_SYSTEM_CACHE) {
957 request.setDestinationToSystemCache();
958 }
959
960 return mDownloadManager.enqueue(request);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700961 }
962
963 /**
964 * Helper to verify an int value in a Cursor
965 *
966 * @param cursor The cursor containing the query results
967 * @param columnName The name of the column to query
968 * @param expected The expected int value
969 */
970 protected void verifyInt(Cursor cursor, String columnName, int expected) {
971 int index = cursor.getColumnIndex(columnName);
972 int actual = cursor.getInt(index);
973 assertEquals(expected, actual);
974 }
975
976 /**
977 * Helper to verify a String value in a Cursor
978 *
979 * @param cursor The cursor containing the query results
980 * @param columnName The name of the column to query
981 * @param expected The expected String value
982 */
983 protected void verifyString(Cursor cursor, String columnName, String expected) {
984 int index = cursor.getColumnIndex(columnName);
985 String actual = cursor.getString(index);
986 Log.i(LOG_TAG, ": " + actual);
987 assertEquals(expected, actual);
988 }
989
990 /**
991 * Performs a query based on ID and returns a Cursor for the query.
992 *
993 * @param id The id of the download in DL Manager; pass -1 to query all downloads
994 * @return A cursor for the query results
995 */
996 protected Cursor getCursor(long id) throws Exception {
997 Query query = new Query();
998 if (id != -1) {
999 query.setFilterById(id);
1000 }
1001
1002 Cursor cursor = mDownloadManager.query(query);
1003 int currentWaitTime = 0;
1004
1005 try {
1006 while (!cursor.moveToFirst()) {
1007 Thread.sleep(DEFAULT_WAIT_POLL_TIME);
1008 currentWaitTime += DEFAULT_WAIT_POLL_TIME;
1009 if (currentWaitTime > DEFAULT_MAX_WAIT_TIME) {
1010 fail("timed out waiting for a non-null query result");
1011 }
1012 cursor.requery();
1013 }
1014 } catch (Exception e) {
1015 cursor.close();
1016 throw e;
1017 }
1018 return cursor;
1019 }
1020
Vasu Nori82e891b2010-12-15 14:42:30 -08001021 /**
1022 * Helper that does the actual basic download verification.
1023 */
1024 protected long doBasicDownload(byte[] blobData, int location) throws Exception {
1025 long dlRequest = enqueueDownloadRequest(blobData, location);
1026
1027 // wait for the download to complete
1028 waitForDownloadOrTimeout(dlRequest);
1029
1030 assertEquals(1, mReceiver.numDownloadsCompleted());
1031 return dlRequest;
1032 }
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -07001033}