blob: 42b06f587e928eaa6fd8ffc613ffb9be46078296 [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
Neil Fuller6f04b2e2016-06-28 18:29:03 +0100247 @Override
248 public void tearDown() throws Exception {
249 mServer.shutdown();
250 super.tearDown();
251 }
252
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700253 /**
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700254 * Helper to build a response from the MockWebServer with no body.
Neal Nguyendf7a8652010-09-09 14:54:26 -0700255 *
256 * @param status The HTTP status code to return for this response
257 * @return Returns the mock web server response that was queued (which can be modified)
258 */
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700259 protected MockResponse buildResponse(int status) {
260 MockResponse response = new MockResponse().setResponseCode(status);
261 response.setHeader("Content-type", mFileType);
262 return response;
Neal Nguyendf7a8652010-09-09 14:54:26 -0700263 }
264
265 /**
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700266 * Helper to build a response from the MockWebServer.
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700267 *
268 * @param status The HTTP status code to return for this response
269 * @param body The body to return in this response
270 * @return Returns the mock web server response that was queued (which can be modified)
271 */
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700272 protected MockResponse buildResponse(int status, byte[] body) {
273 return buildResponse(status).setBody(body);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700274 }
275
276 /**
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700277 * Helper to build a response from the MockWebServer.
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700278 *
279 * @param status The HTTP status code to return for this response
280 * @param bodyFile The body to return in this response
281 * @return Returns the mock web server response that was queued (which can be modified)
282 */
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700283 protected MockResponse buildResponse(int status, File bodyFile)
284 throws FileNotFoundException, IOException {
285 final byte[] body = Streams.readFully(new FileInputStream(bodyFile));
286 return buildResponse(status).setBody(body);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700287 }
288
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700289 protected void enqueueResponse(MockResponse resp) {
290 mServer.enqueue(resp);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700291 }
292
293 /**
294 * Helper to generate a random blob of bytes.
295 *
296 * @param size The size of the data to generate
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700297 * @param type The type of data to generate: currently, one of {@link DataType#TEXT} or
298 * {@link DataType#BINARY}.
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700299 * @return The random data that is generated.
300 */
301 protected byte[] generateData(int size, DataType type) {
302 return generateData(size, type, null);
303 }
304
305 /**
306 * Helper to generate a random blob of bytes using a given RNG.
307 *
308 * @param size The size of the data to generate
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700309 * @param type The type of data to generate: currently, one of {@link DataType#TEXT} or
310 * {@link DataType#BINARY}.
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700311 * @param rng (optional) The RNG to use; pass null to use
312 * @return The random data that is generated.
313 */
314 protected byte[] generateData(int size, DataType type, Random rng) {
315 int min = Byte.MIN_VALUE;
316 int max = Byte.MAX_VALUE;
317
318 // Only use chars in the HTTP ASCII printable character range for Text
319 if (type == DataType.TEXT) {
320 min = 32;
321 max = 126;
322 }
323 byte[] result = new byte[size];
324 Log.i(LOG_TAG, "Generating data of size: " + size);
325
326 if (rng == null) {
327 rng = new LoggingRng();
328 }
329
330 for (int i = 0; i < size; ++i) {
331 result[i] = (byte) (min + rng.nextInt(max - min + 1));
332 }
333 return result;
334 }
335
336 /**
337 * Helper to verify the size of a file.
338 *
339 * @param pfd The input file to compare the size of
340 * @param size The expected size of the file
341 */
342 protected void verifyFileSize(ParcelFileDescriptor pfd, long size) {
343 assertEquals(pfd.getStatSize(), size);
344 }
345
346 /**
347 * Helper to verify the contents of a downloaded file versus a byte[].
348 *
349 * @param actual The file of whose contents to verify
350 * @param expected The data we expect to find in the aforementioned file
351 * @throws IOException if there was a problem reading from the file
352 */
353 protected void verifyFileContents(ParcelFileDescriptor actual, byte[] expected)
354 throws IOException {
355 AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(actual);
356 long fileSize = actual.getStatSize();
357
358 assertTrue(fileSize <= Integer.MAX_VALUE);
359 assertEquals(expected.length, fileSize);
360
361 byte[] actualData = new byte[expected.length];
362 assertEquals(input.read(actualData), fileSize);
363 compareByteArrays(actualData, expected);
364 }
365
366 /**
367 * Helper to compare 2 byte arrays.
368 *
369 * @param actual The array whose data we want to verify
370 * @param expected The array of data we expect to see
371 */
372 protected void compareByteArrays(byte[] actual, byte[] expected) {
373 assertEquals(actual.length, expected.length);
374 int length = actual.length;
375 for (int i = 0; i < length; ++i) {
376 // assert has a bit of overhead, so only do the assert when the values are not the same
377 if (actual[i] != expected[i]) {
378 fail("Byte arrays are not equal.");
379 }
380 }
381 }
382
383 /**
384 * Verifies the contents of a downloaded file versus the contents of a File.
385 *
386 * @param pfd The file whose data we want to verify
387 * @param file The file containing the data we expect to see in the aforementioned file
388 * @throws IOException If there was a problem reading either of the two files
389 */
390 protected void verifyFileContents(ParcelFileDescriptor pfd, File file) throws IOException {
391 byte[] actual = new byte[FILE_BLOCK_READ_SIZE];
392 byte[] expected = new byte[FILE_BLOCK_READ_SIZE];
393
394 AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
395
396 assertEquals(file.length(), pfd.getStatSize());
397
398 DataInputStream inFile = new DataInputStream(new FileInputStream(file));
399 int actualRead = 0;
400 int expectedRead = 0;
401
402 while (((actualRead = input.read(actual)) != -1) &&
403 ((expectedRead = inFile.read(expected)) != -1)) {
404 assertEquals(actualRead, expectedRead);
405 compareByteArrays(actual, expected);
406 }
407 }
408
409 /**
410 * Sets the MIME type of file that will be served from the mock server
411 *
412 * @param type The MIME type to return from the server
413 */
414 protected void setServerMimeType(DownloadFileType type) {
415 mFileType = getMimeMapping(type);
416 }
417
418 /**
419 * Gets the MIME content string for a given type
420 *
421 * @param type The MIME type to return
422 * @return the String representation of that MIME content type
423 */
424 protected String getMimeMapping(DownloadFileType type) {
425 switch (type) {
426 case APK:
427 return "application/vnd.android.package-archive";
428 case GIF:
429 return "image/gif";
430 case ZIP:
431 return "application/x-zip-compressed";
432 case GARBAGE:
433 return "zip\\pidy/doo/da";
434 case UNRECOGNIZED:
435 return "application/new.undefined.type.of.app";
436 }
437 return "text/plain";
438 }
439
440 /**
441 * Gets the Uri that should be used to access the mock server
442 *
443 * @param filename The name of the file to try to retrieve from the mock server
444 * @return the Uri to use for access the file on the mock server
445 */
446 protected Uri getServerUri(String filename) throws Exception {
447 URL url = mServer.getUrl("/" + filename);
448 return Uri.parse(url.toString());
449 }
450
451 /**
452 * Gets the Uri that should be used to access the mock server
453 *
454 * @param filename The name of the file to try to retrieve from the mock server
455 * @return the Uri to use for access the file on the mock server
456 */
457 protected void logDBColumnData(Cursor cursor, String column) {
458 int index = cursor.getColumnIndex(column);
459 Log.i(LOG_TAG, "columnName: " + column);
460 Log.i(LOG_TAG, "columnValue: " + cursor.getString(index));
461 }
462
463 /**
464 * Helper to create and register a new MultipleDownloadCompletedReciever
465 *
466 * This is used to track many simultaneous downloads by keeping count of all the downloads
467 * that have completed.
468 *
469 * @return A new receiver that records and can be queried on how many downloads have completed.
470 */
471 protected MultipleDownloadsCompletedReceiver registerNewMultipleDownloadsReceiver() {
472 MultipleDownloadsCompletedReceiver receiver = new MultipleDownloadsCompletedReceiver();
473 mContext.registerReceiver(receiver, new IntentFilter(
474 DownloadManager.ACTION_DOWNLOAD_COMPLETE));
475 return receiver;
476 }
477
478 /**
479 * Helper to verify a standard single-file download from the mock server, and clean up after
480 * verification
481 *
482 * Note that this also calls the Download manager's remove, which cleans up the file from cache.
483 *
484 * @param requestId The id of the download to remove
485 * @param fileData The data to verify the file contains
486 */
487 protected void verifyAndCleanupSingleFileDownload(long requestId, byte[] fileData)
488 throws Exception {
489 int fileSize = fileData.length;
490 ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(requestId);
491 Cursor cursor = mDownloadManager.query(new Query().setFilterById(requestId));
492
493 try {
494 assertEquals(1, cursor.getCount());
495 assertTrue(cursor.moveToFirst());
496
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700497 verifyFileSize(pfd, fileSize);
498 verifyFileContents(pfd, fileData);
499 } finally {
500 pfd.close();
501 cursor.close();
502 mDownloadManager.remove(requestId);
503 }
504 }
505
506 /**
507 * Enables or disables WiFi.
508 *
509 * Note: Needs the following permissions:
510 * android.permission.ACCESS_WIFI_STATE
511 * android.permission.CHANGE_WIFI_STATE
512 * @param enable true if it should be enabled, false if it should be disabled
513 */
514 protected void setWiFiStateOn(boolean enable) throws Exception {
Neal Nguyenbd06f022010-10-20 17:43:15 -0700515 Log.i(LOG_TAG, "Setting WiFi State to: " + enable);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700516 WifiManager manager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
517
518 manager.setWifiEnabled(enable);
519
520 String timeoutMessage = "Timed out waiting for Wifi to be "
521 + (enable ? "enabled!" : "disabled!");
522
523 WiFiChangedReceiver receiver = new WiFiChangedReceiver(mContext);
524 mContext.registerReceiver(receiver, new IntentFilter(
525 ConnectivityManager.CONNECTIVITY_ACTION));
526
527 synchronized (receiver) {
528 long timeoutTime = SystemClock.elapsedRealtime() + DEFAULT_MAX_WAIT_TIME;
529 boolean timedOut = false;
530
531 while (receiver.getWiFiIsOn() != enable && !timedOut) {
532 try {
Neal Nguyenbd06f022010-10-20 17:43:15 -0700533 receiver.wait(DEFAULT_WAIT_POLL_TIME);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700534
535 if (SystemClock.elapsedRealtime() > timeoutTime) {
536 timedOut = true;
537 }
538 }
539 catch (InterruptedException e) {
540 // ignore InterruptedExceptions
541 }
542 }
543 if (timedOut) {
544 fail(timeoutMessage);
545 }
546 }
547 assertEquals(enable, receiver.getWiFiIsOn());
548 }
549
550 /**
551 * Helper to enables or disables airplane mode. If successful, it also broadcasts an intent
552 * indicating that the mode has changed.
553 *
554 * Note: Needs the following permission:
555 * android.permission.WRITE_SETTINGS
556 * @param enable true if airplane mode should be ON, false if it should be OFF
557 */
558 protected void setAirplaneModeOn(boolean enable) throws Exception {
559 int state = enable ? 1 : 0;
560
561 // Change the system setting
Christopher Tatec09cdce2012-09-10 16:50:14 -0700562 Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON,
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700563 state);
564
565 String timeoutMessage = "Timed out waiting for airplane mode to be " +
566 (enable ? "enabled!" : "disabled!");
567
568 // wait for airplane mode to change state
569 int currentWaitTime = 0;
Christopher Tatec09cdce2012-09-10 16:50:14 -0700570 while (Settings.Global.getInt(mContext.getContentResolver(),
571 Settings.Global.AIRPLANE_MODE_ON, -1) != state) {
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700572 timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME, DEFAULT_MAX_WAIT_TIME,
573 timeoutMessage);
574 }
575
576 // Post the intent
577 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
578 intent.putExtra("state", true);
Christopher Tatec09cdce2012-09-10 16:50:14 -0700579 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700580 }
581
582 /**
583 * Helper to create a large file of random data on the SD card.
584 *
585 * @param filename (optional) The name of the file to create on the SD card; pass in null to
586 * use a default temp filename.
587 * @param type The type of file to create
588 * @param subdirectory If not null, the subdirectory under the SD card where the file should go
589 * @return The File that was created
590 * @throws IOException if there was an error while creating the file.
591 */
592 protected File createFileOnSD(String filename, long fileSize, DataType type,
593 String subdirectory) throws IOException {
594
595 // Build up the file path and name
596 String sdPath = Environment.getExternalStorageDirectory().getPath();
597 StringBuilder fullPath = new StringBuilder(sdPath);
598 if (subdirectory != null) {
599 fullPath.append(File.separatorChar).append(subdirectory);
600 }
601
602 File file = null;
603 if (filename == null) {
604 file = File.createTempFile("DMTEST_", null, new File(fullPath.toString()));
605 }
606 else {
607 fullPath.append(File.separatorChar).append(filename);
608 file = new File(fullPath.toString());
609 file.createNewFile();
610 }
611
612 // Fill the file with random data
613 DataOutputStream output = new DataOutputStream(new FileOutputStream(file));
614 final int CHUNK_SIZE = 1000000; // copy random data in 1000000-char chunks
615 long remaining = fileSize;
616 int nextChunkSize = CHUNK_SIZE;
617 byte[] randomData = null;
618 Random rng = new LoggingRng();
Vasu Nori6f35c0e2010-12-17 17:08:58 -0800619 byte[] chunkSizeData = generateData(nextChunkSize, type, rng);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700620
621 try {
622 while (remaining > 0) {
623 if (remaining < CHUNK_SIZE) {
624 nextChunkSize = (int)remaining;
625 remaining = 0;
Vasu Nori6f35c0e2010-12-17 17:08:58 -0800626 randomData = generateData(nextChunkSize, type, rng);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700627 }
628 else {
629 remaining -= CHUNK_SIZE;
Vasu Nori6f35c0e2010-12-17 17:08:58 -0800630 randomData = chunkSizeData;
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700631 }
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700632 output.write(randomData);
Vasu Nori6f35c0e2010-12-17 17:08:58 -0800633 Log.i(TAG, "while creating " + fileSize + " file, " +
634 "remaining bytes to be written: " + remaining);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700635 }
636 } catch (IOException e) {
637 Log.e(LOG_TAG, "Error writing to file " + file.getAbsolutePath());
638 file.delete();
639 throw e;
640 } finally {
641 output.close();
642 }
643 return file;
644 }
645
646 /**
647 * Helper to wait for a particular download to finish, or else a timeout to occur
648 *
Neal Nguyen65c36e62010-10-24 18:02:45 -0700649 * Does not wait for a receiver notification of the download.
650 *
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700651 * @param id The download id to query on (wait for)
652 */
Neal Nguyen65c36e62010-10-24 18:02:45 -0700653 protected void waitForDownloadOrTimeout_skipNotification(long id) throws TimeoutException,
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700654 InterruptedException {
655 waitForDownloadOrTimeout(id, WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
656 }
657
658 /**
659 * Helper to wait for a particular download to finish, or else a timeout to occur
660 *
Neal Nguyen65c36e62010-10-24 18:02:45 -0700661 * Also guarantees a notification has been posted for the download.
662 *
663 * @param id The download id to query on (wait for)
664 */
665 protected void waitForDownloadOrTimeout(long id) throws TimeoutException,
666 InterruptedException {
667 waitForDownloadOrTimeout_skipNotification(id);
668 waitForReceiverNotifications(1);
669 }
670
671 /**
672 * Helper to wait for a particular download to finish, or else a timeout to occur
673 *
674 * Also guarantees a notification has been posted for the download.
675 *
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700676 * @param id The download id to query on (wait for)
677 * @param poll The amount of time to wait
678 * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
679 */
680 protected void waitForDownloadOrTimeout(long id, long poll, long timeoutMillis)
681 throws TimeoutException, InterruptedException {
682 doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
Neal Nguyen65c36e62010-10-24 18:02:45 -0700683 waitForReceiverNotifications(1);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700684 }
685
686 /**
687 * Helper to wait for all downloads to finish, or else a specified timeout to occur
688 *
Neal Nguyen65c36e62010-10-24 18:02:45 -0700689 * Makes no guaranee that notifications have been posted for all downloads.
690 *
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700691 * @param poll The amount of time to wait
692 * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
693 */
694 protected void waitForDownloadsOrTimeout(long poll, long timeoutMillis) throws TimeoutException,
695 InterruptedException {
696 doWaitForDownloadsOrTimeout(new Query(), poll, timeoutMillis);
697 }
698
699 /**
700 * Helper to wait for all downloads to finish, or else a timeout to occur, but does not throw
701 *
Neal Nguyen65c36e62010-10-24 18:02:45 -0700702 * Also guarantees a notification has been posted for the download.
703 *
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700704 * @param id The id of the download to query against
705 * @param poll The amount of time to wait
706 * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
707 * @return true if download completed successfully (didn't timeout), false otherwise
708 */
709 protected boolean waitForDownloadOrTimeoutNoThrow(long id, long poll, long timeoutMillis) {
710 try {
711 doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
Neal Nguyen65c36e62010-10-24 18:02:45 -0700712 waitForReceiverNotifications(1);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700713 } catch (TimeoutException e) {
714 return false;
715 }
716 return true;
717 }
718
719 /**
720 * Helper function to synchronously wait, or timeout if the maximum threshold has been exceeded.
721 *
722 * @param currentTotalWaitTime The total time waited so far
723 * @param poll The amount of time to wait
724 * @param maxTimeoutMillis The total wait time threshold; if we've waited more than this long,
725 * we timeout and fail
726 * @param timedOutMessage The message to display in the failure message if we timeout
727 * @return The new total amount of time we've waited so far
728 * @throws TimeoutException if timed out waiting for SD card to mount
729 */
730 protected int timeoutWait(int currentTotalWaitTime, long poll, long maxTimeoutMillis,
731 String timedOutMessage) throws TimeoutException {
732 long now = SystemClock.elapsedRealtime();
733 long end = now + poll;
734
735 // if we get InterruptedException's, ignore them and just keep sleeping
736 while (now < end) {
737 try {
738 Thread.sleep(end - now);
739 } catch (InterruptedException e) {
740 // ignore interrupted exceptions
741 }
742 now = SystemClock.elapsedRealtime();
743 }
744
745 currentTotalWaitTime += poll;
746 if (currentTotalWaitTime > maxTimeoutMillis) {
747 throw new TimeoutException(timedOutMessage);
748 }
749 return currentTotalWaitTime;
750 }
751
752 /**
753 * Helper to wait for all downloads to finish, or else a timeout to occur
754 *
755 * @param query The query to pass to the download manager
756 * @param poll The poll time to wait between checks
757 * @param timeoutMillis The max amount of time (in ms) to wait for the download(s) to complete
758 */
759 protected void doWaitForDownloadsOrTimeout(Query query, long poll, long timeoutMillis)
760 throws TimeoutException {
761 int currentWaitTime = 0;
762 while (true) {
763 query.setFilterByStatus(DownloadManager.STATUS_PENDING | DownloadManager.STATUS_PAUSED
764 | DownloadManager.STATUS_RUNNING);
765 Cursor cursor = mDownloadManager.query(query);
766
767 try {
Neal Nguyen65c36e62010-10-24 18:02:45 -0700768 if (cursor.getCount() == 0) {
769 Log.i(LOG_TAG, "All downloads should be done...");
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700770 break;
771 }
772 currentWaitTime = timeoutWait(currentWaitTime, poll, timeoutMillis,
773 "Timed out waiting for all downloads to finish");
774 } finally {
775 cursor.close();
776 }
777 }
778 }
779
780 /**
781 * Synchronously waits for external store to be mounted (eg: SD Card).
782 *
783 * @throws InterruptedException if interrupted
784 * @throws Exception if timed out waiting for SD card to mount
785 */
786 protected void waitForExternalStoreMount() throws Exception {
787 String extStorageState = Environment.getExternalStorageState();
788 int currentWaitTime = 0;
789 while (!extStorageState.equals(Environment.MEDIA_MOUNTED)) {
790 Log.i(LOG_TAG, "Waiting for SD card...");
791 currentWaitTime = timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME,
792 DEFAULT_MAX_WAIT_TIME, "Timed out waiting for SD Card to be ready!");
793 extStorageState = Environment.getExternalStorageState();
794 }
795 }
796
797 /**
798 * Synchronously waits for a download to start.
799 *
800 * @param dlRequest the download request id used by Download Manager to track the download.
801 * @throws Exception if timed out while waiting for SD card to mount
802 */
803 protected void waitForDownloadToStart(long dlRequest) throws Exception {
804 Cursor cursor = getCursor(dlRequest);
805 try {
806 int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
807 int value = cursor.getInt(columnIndex);
808 int currentWaitTime = 0;
809
810 while (value != DownloadManager.STATUS_RUNNING &&
811 (value != DownloadManager.STATUS_FAILED) &&
812 (value != DownloadManager.STATUS_SUCCESSFUL)) {
813 Log.i(LOG_TAG, "Waiting for download to start...");
814 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
815 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download to start!");
816 cursor.requery();
817 assertTrue(cursor.moveToFirst());
818 columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
819 value = cursor.getInt(columnIndex);
820 }
821 assertFalse("Download failed immediately after start",
822 value == DownloadManager.STATUS_FAILED);
823 } finally {
824 cursor.close();
825 }
826 }
827
828 /**
Neal Nguyen65c36e62010-10-24 18:02:45 -0700829 * Convenience function to wait for just 1 notification of a download.
830 *
831 * @throws Exception if timed out while waiting
832 */
833 protected void waitForReceiverNotification() throws Exception {
834 waitForReceiverNotifications(1);
835 }
836
837 /**
838 * Synchronously waits for our receiver to receive notification for a given number of
839 * downloads.
840 *
841 * @param targetNumber The number of notifications for unique downloads to wait for; pass in
842 * -1 to not wait for notification.
843 * @throws Exception if timed out while waiting
844 */
845 protected void waitForReceiverNotifications(int targetNumber) throws TimeoutException {
846 int count = mReceiver.numDownloadsCompleted();
847 int currentWaitTime = 0;
848
849 while (count < targetNumber) {
850 Log.i(LOG_TAG, "Waiting for notification of downloads...");
851 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
852 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download notifications!"
853 + " Received " + count + "notifications.");
854 count = mReceiver.numDownloadsCompleted();
855 }
856 }
857
858 /**
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700859 * Synchronously waits for a file to increase in size (such as to monitor that a download is
860 * progressing).
861 *
862 * @param file The file whose size to track.
863 * @throws Exception if timed out while waiting for the file to grow in size.
864 */
865 protected void waitForFileToGrow(File file) throws Exception {
866 int currentWaitTime = 0;
867
868 // File may not even exist yet, so wait until it does (or we timeout)
869 while (!file.exists()) {
870 Log.i(LOG_TAG, "Waiting for file to exist...");
871 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
872 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be created.");
873 }
874
875 // Get original file size...
876 long originalSize = file.length();
877
878 while (file.length() <= originalSize) {
879 Log.i(LOG_TAG, "Waiting for file to be written to...");
880 currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
881 MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be written to.");
882 }
883 }
884
885 /**
886 * Helper to remove all downloads that are registered with the DL Manager.
887 *
888 * Note: This gives us a clean slate b/c it includes downloads that are pending, running,
889 * paused, or have completed.
890 */
891 protected void removeAllCurrentDownloads() {
892 Log.i(LOG_TAG, "Removing all current registered downloads...");
Vasu Nori82e891b2010-12-15 14:42:30 -0800893 ArrayList<Long> ids = new ArrayList<Long>();
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700894 Cursor cursor = mDownloadManager.query(new Query());
895 try {
896 if (cursor.moveToFirst()) {
897 do {
898 int index = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
899 long downloadId = cursor.getLong(index);
Vasu Nori82e891b2010-12-15 14:42:30 -0800900 ids.add(downloadId);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700901 } while (cursor.moveToNext());
902 }
903 } finally {
904 cursor.close();
905 }
Vasu Nori82e891b2010-12-15 14:42:30 -0800906 // delete all ids
907 for (long id : ids) {
908 mDownloadManager.remove(id);
909 }
910 // make sure the database is empty
911 cursor = mDownloadManager.query(new Query());
912 try {
913 assertEquals(0, cursor.getCount());
914 } finally {
915 cursor.close();
916 }
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700917 }
918
919 /**
920 * Helper to perform a standard enqueue of data to the mock server.
Vasu Nori82e891b2010-12-15 14:42:30 -0800921 * download is performed to the downloads cache dir (NOT systemcache dir)
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700922 *
923 * @param body The body to return in the response from the server
924 */
925 protected long doStandardEnqueue(byte[] body) throws Exception {
Vasu Nori82e891b2010-12-15 14:42:30 -0800926 return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
927 }
928
929 protected long enqueueDownloadRequest(byte[] body, int location) throws Exception {
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700930 // Prepare the mock server with a standard response
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700931 mServer.enqueue(buildResponse(HTTP_OK, body));
Vasu Nori82e891b2010-12-15 14:42:30 -0800932 return doEnqueue(location);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700933 }
934
935 /**
936 * Helper to perform a standard enqueue of data to the mock server.
937 *
938 * @param body The body to return in the response from the server, contained in the file
939 */
940 protected long doStandardEnqueue(File body) throws Exception {
Vasu Nori82e891b2010-12-15 14:42:30 -0800941 return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
942 }
943
944 protected long enqueueDownloadRequest(File body, int location) throws Exception {
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700945 // Prepare the mock server with a standard response
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -0700946 mServer.enqueue(buildResponse(HTTP_OK, body));
Vasu Nori82e891b2010-12-15 14:42:30 -0800947 return doEnqueue(location);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700948 }
949
950 /**
951 * Helper to do the additional steps (setting title and Uri of default filename) when
952 * doing a standard enqueue request to the server.
953 */
954 protected long doCommonStandardEnqueue() throws Exception {
Vasu Nori82e891b2010-12-15 14:42:30 -0800955 return doEnqueue(DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
956 }
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700957
Vasu Nori82e891b2010-12-15 14:42:30 -0800958 private long doEnqueue(int location) throws Exception {
959 Uri uri = getServerUri(DEFAULT_FILENAME);
960 Request request = new Request(uri).setTitle(DEFAULT_FILENAME);
961 if (location == DOWNLOAD_TO_SYSTEM_CACHE) {
962 request.setDestinationToSystemCache();
963 }
964
965 return mDownloadManager.enqueue(request);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700966 }
967
968 /**
969 * Helper to verify an int value in a Cursor
970 *
971 * @param cursor The cursor containing the query results
972 * @param columnName The name of the column to query
973 * @param expected The expected int value
974 */
975 protected void verifyInt(Cursor cursor, String columnName, int expected) {
976 int index = cursor.getColumnIndex(columnName);
977 int actual = cursor.getInt(index);
Md Haquec6272b92016-03-08 14:51:33 -0800978 assertEquals(String.format("Expected = %d : Actual = %d", expected, actual), expected, actual);
Neal Nguyen40ef0f42010-08-09 14:08:26 -0700979 }
980
981 /**
982 * Helper to verify a String value in a Cursor
983 *
984 * @param cursor The cursor containing the query results
985 * @param columnName The name of the column to query
986 * @param expected The expected String value
987 */
988 protected void verifyString(Cursor cursor, String columnName, String expected) {
989 int index = cursor.getColumnIndex(columnName);
990 String actual = cursor.getString(index);
991 Log.i(LOG_TAG, ": " + actual);
992 assertEquals(expected, actual);
993 }
994
995 /**
996 * Performs a query based on ID and returns a Cursor for the query.
997 *
998 * @param id The id of the download in DL Manager; pass -1 to query all downloads
999 * @return A cursor for the query results
1000 */
1001 protected Cursor getCursor(long id) throws Exception {
1002 Query query = new Query();
1003 if (id != -1) {
1004 query.setFilterById(id);
1005 }
1006
1007 Cursor cursor = mDownloadManager.query(query);
1008 int currentWaitTime = 0;
1009
1010 try {
1011 while (!cursor.moveToFirst()) {
1012 Thread.sleep(DEFAULT_WAIT_POLL_TIME);
1013 currentWaitTime += DEFAULT_WAIT_POLL_TIME;
1014 if (currentWaitTime > DEFAULT_MAX_WAIT_TIME) {
1015 fail("timed out waiting for a non-null query result");
1016 }
1017 cursor.requery();
1018 }
1019 } catch (Exception e) {
1020 cursor.close();
1021 throw e;
1022 }
1023 return cursor;
1024 }
1025
Vasu Nori82e891b2010-12-15 14:42:30 -08001026 /**
1027 * Helper that does the actual basic download verification.
1028 */
1029 protected long doBasicDownload(byte[] blobData, int location) throws Exception {
1030 long dlRequest = enqueueDownloadRequest(blobData, location);
1031
1032 // wait for the download to complete
1033 waitForDownloadOrTimeout(dlRequest);
1034
1035 assertEquals(1, mReceiver.numDownloadsCompleted());
1036 return dlRequest;
1037 }
Jeff Sharkeyb14ad8c2012-03-28 18:59:21 -07001038}