140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen/*
240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen * Copyright (C) 2010 The Android Open Source Project
340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen *
440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen * Licensed under the Apache License, Version 2.0 (the "License");
540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen * you may not use this file except in compliance with the License.
640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen * You may obtain a copy of the License at
740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen *
840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen *      http://www.apache.org/licenses/LICENSE-2.0
940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen *
1040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen * Unless required by applicable law or agreed to in writing, software
1140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen * distributed under the License is distributed on an "AS IS" BASIS,
1240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen * See the License for the specific language governing permissions and
1440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen * limitations under the License.
1540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen */
1640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
1731fd85f39b554e09b2e6c1c2ccf5c186859880faSteve Howardpackage android.app;
1840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
1931fd85f39b554e09b2e6c1c2ccf5c186859880faSteve Howardimport android.app.DownloadManager.Query;
2031fd85f39b554e09b2e6c1c2ccf5c186859880faSteve Howardimport android.app.DownloadManager.Request;
2140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.content.BroadcastReceiver;
2240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.content.Context;
2340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.content.Intent;
2440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.content.IntentFilter;
2540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.database.Cursor;
2640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.net.ConnectivityManager;
2740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.net.NetworkInfo;
2831fd85f39b554e09b2e6c1c2ccf5c186859880faSteve Howardimport android.net.Uri;
2940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.net.wifi.WifiManager;
30df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyenimport android.os.Bundle;
3140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.os.Environment;
3240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.os.ParcelFileDescriptor;
3340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.os.SystemClock;
3440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.os.ParcelFileDescriptor.AutoCloseInputStream;
3540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.provider.Settings;
3640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.test.InstrumentationTestCase;
3740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.util.Log;
3840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
3940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.io.DataInputStream;
4040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.io.DataOutputStream;
4140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.io.File;
4240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.io.FileInputStream;
4340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.io.FileOutputStream;
4440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.io.IOException;
4540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.net.URL;
4640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.util.concurrent.TimeoutException;
47df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyenimport java.util.Collections;
48df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyenimport java.util.HashSet;
4940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.util.Iterator;
5040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.util.List;
5140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.util.Random;
52df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyenimport java.util.Set;
5340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.util.Vector;
5440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
5540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport junit.framework.AssertionFailedError;
5640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
5740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport coretestutils.http.MockResponse;
5840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport coretestutils.http.MockWebServer;
5940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
6040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen/**
6140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen * Base class for Instrumented tests for the Download Manager.
6240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen */
6340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenpublic class DownloadManagerBaseTest extends InstrumentationTestCase {
6440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
6540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected DownloadManager mDownloadManager = null;
6640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected MockWebServer mServer = null;
6740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected String mFileType = "text/plain";
6840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected Context mContext = null;
6963e5d79a163461651cd8bd89d3de691ef7649109Neal Nguyen    protected MultipleDownloadsCompletedReceiver mReceiver = null;
7040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int DEFAULT_FILE_SIZE = 130 * 1024;  // 130kb
7140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int FILE_BLOCK_READ_SIZE = 1024 * 1024;
7240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
7340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final String LOG_TAG = "android.net.DownloadManagerBaseTest";
7440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int HTTP_OK = 200;
75df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen    protected static final int HTTP_REDIRECT = 307;
7640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int HTTP_PARTIAL_CONTENT = 206;
7740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int HTTP_NOT_FOUND = 404;
7840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int HTTP_SERVICE_UNAVAILABLE = 503;
7940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected String DEFAULT_FILENAME = "somefile.txt";
8040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
8140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int DEFAULT_MAX_WAIT_TIME = 2 * 60 * 1000;  // 2 minutes
8240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int DEFAULT_WAIT_POLL_TIME = 5 * 1000;  // 5 seconds
8340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
8440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int WAIT_FOR_DOWNLOAD_POLL_TIME = 1 * 1000;  // 1 second
8540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 5 * 60 * 1000; // 5 minutes
86bd06f02d02e07ca15e420ee9e50e35253646ba64Neal Nguyen    protected static final int MAX_WAIT_FOR_LARGE_DOWNLOAD_TIME = 15 * 60 * 1000; // 15 minutes
8740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
8840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    // Just a few popular file types used to return from a download
8940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected enum DownloadFileType {
9040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        PLAINTEXT,
9140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        APK,
9240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        GIF,
9340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        GARBAGE,
9440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        UNRECOGNIZED,
9540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        ZIP
9640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
9740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
9840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected enum DataType {
9940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        TEXT,
10040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        BINARY
10140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
10240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
10340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    public static class LoggingRng extends Random {
10440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
10540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        /**
10640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Constructor
10740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         *
10840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Creates RNG with self-generated seed value.
10940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         */
11040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        public LoggingRng() {
11140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            this(SystemClock.uptimeMillis());
11240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
11340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
11440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        /**
11540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Constructor
11640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         *
11740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Creats RNG with given initial seed value
11840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
11940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * @param seed The initial seed value
12040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         */
12140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        public LoggingRng(long seed) {
12240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            super(seed);
12340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            Log.i(LOG_TAG, "Seeding RNG with value: " + seed);
12440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
12540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
12640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
12740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    public static class MultipleDownloadsCompletedReceiver extends BroadcastReceiver {
12840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        private volatile int mNumDownloadsCompleted = 0;
129df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen        private Set<Long> downloadIds = Collections.synchronizedSet(new HashSet<Long>());
13040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
13140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        /**
13240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * {@inheritDoc}
13340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         */
13440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        @Override
13540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        public void onReceive(Context context, Intent intent) {
13640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            if (intent.getAction().equalsIgnoreCase(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
13763e5d79a163461651cd8bd89d3de691ef7649109Neal Nguyen                synchronized(this) {
13865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    long id = intent.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
13965c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    Log.i(LOG_TAG, "Received Notification for download: " + id);
14065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    if (!downloadIds.contains(id)) {
14165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        ++mNumDownloadsCompleted;
14265c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        Log.i(LOG_TAG, "MultipleDownloadsCompletedReceiver got intent: " +
14365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                                intent.getAction() + " --> total count: " + mNumDownloadsCompleted);
14465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        downloadIds.add(id);
14565c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen
14665c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        DownloadManager dm = (DownloadManager)context.getSystemService(
14765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                                Context.DOWNLOAD_SERVICE);
14865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen
14965c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        Cursor cursor = dm.query(new Query().setFilterById(id));
15065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        try {
15165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                            if (cursor.moveToFirst()) {
15265c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                                int status = cursor.getInt(cursor.getColumnIndex(
15365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                                        DownloadManager.COLUMN_STATUS));
15465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                                Log.i(LOG_TAG, "Download status is: " + status);
15565c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                            } else {
15665c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                                fail("No status found for completed download!");
15765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                            }
15865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        } finally {
15965c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                            cursor.close();
16065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        }
16165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    } else {
16265c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        Log.i(LOG_TAG, "Notification for id: " + id + " has already been made.");
16365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    }
16463e5d79a163461651cd8bd89d3de691ef7649109Neal Nguyen                }
16540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
16640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
16740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
16840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        /**
16940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Gets the number of times the {@link #onReceive} callback has been called for the
17040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * {@link DownloadManager.ACTION_DOWNLOAD_COMPLETED} action, indicating the number of
17140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * downloads completed thus far.
17240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         *
17340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * @return the number of downloads completed so far.
17440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         */
17540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        public int numDownloadsCompleted() {
17640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            return mNumDownloadsCompleted;
17740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
178df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen
179df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen        /**
180df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen         * Gets the list of download IDs.
181df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen         * @return A Set<Long> with the ids of the completed downloads.
182df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen         */
183df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen        public Set<Long> getDownloadIds() {
184bd06f02d02e07ca15e420ee9e50e35253646ba64Neal Nguyen            synchronized(this) {
185df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen                Set<Long> returnIds = new HashSet<Long>(downloadIds);
186df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen                return returnIds;
187df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen            }
188df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen        }
189df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen
19040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
19140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
19240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    public static class WiFiChangedReceiver extends BroadcastReceiver {
19340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        private Context mContext = null;
19440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
19540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        /**
19640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Constructor
19740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         *
19840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Sets the current state of WiFi.
19940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         *
20040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * @param context The current app {@link Context}.
20140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         */
20240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        public WiFiChangedReceiver(Context context) {
20340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            mContext = context;
20440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
20540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
20640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        /**
20740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * {@inheritDoc}
20840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         */
20940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        @Override
21040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        public void onReceive(Context context, Intent intent) {
21140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            if (intent.getAction().equalsIgnoreCase(ConnectivityManager.CONNECTIVITY_ACTION)) {
21240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                Log.i(LOG_TAG, "ConnectivityManager state change: " + intent.getAction());
21340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                synchronized (this) {
21440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    this.notify();
21540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                }
21640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
21740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
21840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
21940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        /**
22040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Gets the current state of WiFi.
22140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         *
22240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * @return Returns true if WiFi is on, false otherwise.
22340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         */
22440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        public boolean getWiFiIsOn() {
22540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            ConnectivityManager connManager = (ConnectivityManager)mContext.getSystemService(
22640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    Context.CONNECTIVITY_SERVICE);
22740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            NetworkInfo info = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
228bd06f02d02e07ca15e420ee9e50e35253646ba64Neal Nguyen            Log.i(LOG_TAG, "WiFi Connection state is currently: " + info.isConnected());
22940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            return info.isConnected();
23040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
23140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
23240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
23340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
23440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * {@inheritDoc}
23540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
23640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    @Override
23740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    public void setUp() throws Exception {
23840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        mContext = getInstrumentation().getContext();
23940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE);
24040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        mServer = new MockWebServer();
24163e5d79a163461651cd8bd89d3de691ef7649109Neal Nguyen        mReceiver = registerNewMultipleDownloadsReceiver();
24240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Note: callers overriding this should call mServer.play() with the desired port #
24340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
24440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
24540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
246df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen     * Helper to enqueue a response from the MockWebServer with no body.
247df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen     *
248df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen     * @param status The HTTP status code to return for this response
249df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen     * @return Returns the mock web server response that was queued (which can be modified)
250df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen     */
251df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen    protected MockResponse enqueueResponse(int status) {
252df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen        return doEnqueueResponse(status);
253df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen
254df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen    }
255df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen
256df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen    /**
25740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to enqueue a response from the MockWebServer.
25840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
25940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param status The HTTP status code to return for this response
26040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param body The body to return in this response
26140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return Returns the mock web server response that was queued (which can be modified)
26240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
26340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected MockResponse enqueueResponse(int status, byte[] body) {
26440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return doEnqueueResponse(status).setBody(body);
26540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
26640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
26740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
26840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
26940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to enqueue a response from the MockWebServer.
27040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
27140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param status The HTTP status code to return for this response
27240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param bodyFile The body to return in this response
27340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return Returns the mock web server response that was queued (which can be modified)
27440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
27540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected MockResponse enqueueResponse(int status, File bodyFile) {
27640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return doEnqueueResponse(status).setBody(bodyFile);
27740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
27840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
27940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
28040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper for enqueue'ing a response from the MockWebServer.
28140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
28240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param status The HTTP status code to return for this response
28340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return Returns the mock web server response that was queued (which can be modified)
28440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
28540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected MockResponse doEnqueueResponse(int status) {
28640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        MockResponse response = new MockResponse().setResponseCode(status);
28740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        response.addHeader("Content-type", mFileType);
28840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        mServer.enqueue(response);
28940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return response;
29040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
29140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
29240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
29340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to generate a random blob of bytes.
29440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
29540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param size The size of the data to generate
29640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param type The type of data to generate: currently, one of {@link DataType.TEXT} or
29740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *         {@link DataType.BINARY}.
29840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return The random data that is generated.
29940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
30040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected byte[] generateData(int size, DataType type) {
30140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return generateData(size, type, null);
30240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
30340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
30440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
30540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to generate a random blob of bytes using a given RNG.
30640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
30740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param size The size of the data to generate
30840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param type The type of data to generate: currently, one of {@link DataType.TEXT} or
30940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *         {@link DataType.BINARY}.
31040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param rng (optional) The RNG to use; pass null to use
31140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return The random data that is generated.
31240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
31340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected byte[] generateData(int size, DataType type, Random rng) {
31440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int min = Byte.MIN_VALUE;
31540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int max = Byte.MAX_VALUE;
31640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
31740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Only use chars in the HTTP ASCII printable character range for Text
31840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        if (type == DataType.TEXT) {
31940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            min = 32;
32040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            max = 126;
32140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
32240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        byte[] result = new byte[size];
32340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Log.i(LOG_TAG, "Generating data of size: " + size);
32440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
32540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        if (rng == null) {
32640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            rng = new LoggingRng();
32740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
32840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
32940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        for (int i = 0; i < size; ++i) {
33040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            result[i] = (byte) (min + rng.nextInt(max - min + 1));
33140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
33240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return result;
33340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
33440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
33540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
33640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to verify the size of a file.
33740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
33840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param pfd The input file to compare the size of
33940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param size The expected size of the file
34040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
34140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void verifyFileSize(ParcelFileDescriptor pfd, long size) {
34240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(pfd.getStatSize(), size);
34340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
34440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
34540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
34640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to verify the contents of a downloaded file versus a byte[].
34740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
34840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param actual The file of whose contents to verify
34940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param expected The data we expect to find in the aforementioned file
35040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws IOException if there was a problem reading from the file
35140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
35240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void verifyFileContents(ParcelFileDescriptor actual, byte[] expected)
35340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            throws IOException {
35440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(actual);
35540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        long fileSize = actual.getStatSize();
35640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
35740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertTrue(fileSize <= Integer.MAX_VALUE);
35840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(expected.length, fileSize);
35940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
36040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        byte[] actualData = new byte[expected.length];
36140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(input.read(actualData), fileSize);
36240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        compareByteArrays(actualData, expected);
36340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
36440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
36540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
36640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to compare 2 byte arrays.
36740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
36840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param actual The array whose data we want to verify
36940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param expected The array of data we expect to see
37040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
37140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void compareByteArrays(byte[] actual, byte[] expected) {
37240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(actual.length, expected.length);
37340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int length = actual.length;
37440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        for (int i = 0; i < length; ++i) {
37540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            // assert has a bit of overhead, so only do the assert when the values are not the same
37640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            if (actual[i] != expected[i]) {
37740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                fail("Byte arrays are not equal.");
37840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
37940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
38040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
38140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
38240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
38340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Verifies the contents of a downloaded file versus the contents of a File.
38440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
38540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param pfd The file whose data we want to verify
38640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param file The file containing the data we expect to see in the aforementioned file
38740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws IOException If there was a problem reading either of the two files
38840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
38940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void verifyFileContents(ParcelFileDescriptor pfd, File file) throws IOException {
39040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        byte[] actual = new byte[FILE_BLOCK_READ_SIZE];
39140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        byte[] expected = new byte[FILE_BLOCK_READ_SIZE];
39240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
39340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
39440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
39540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(file.length(), pfd.getStatSize());
39640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
39740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        DataInputStream inFile = new DataInputStream(new FileInputStream(file));
39840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int actualRead = 0;
39940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int expectedRead = 0;
40040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
40140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        while (((actualRead = input.read(actual)) != -1) &&
40240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                ((expectedRead = inFile.read(expected)) != -1)) {
40340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            assertEquals(actualRead, expectedRead);
40440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            compareByteArrays(actual, expected);
40540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
40640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
40740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
40840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
40940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Sets the MIME type of file that will be served from the mock server
41040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
41140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param type The MIME type to return from the server
41240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
41340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void setServerMimeType(DownloadFileType type) {
41440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        mFileType = getMimeMapping(type);
41540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
41640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
41740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
41840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Gets the MIME content string for a given type
41940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
42040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param type The MIME type to return
42140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return the String representation of that MIME content type
42240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
42340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected String getMimeMapping(DownloadFileType type) {
42440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        switch (type) {
42540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            case APK:
42640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                return "application/vnd.android.package-archive";
42740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            case GIF:
42840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                return "image/gif";
42940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            case ZIP:
43040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                return "application/x-zip-compressed";
43140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            case GARBAGE:
43240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                return "zip\\pidy/doo/da";
43340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            case UNRECOGNIZED:
43440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                return "application/new.undefined.type.of.app";
43540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
43640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return "text/plain";
43740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
43840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
43940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
44040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Gets the Uri that should be used to access the mock server
44140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
44240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param filename The name of the file to try to retrieve from the mock server
44340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return the Uri to use for access the file on the mock server
44440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
44540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected Uri getServerUri(String filename) throws Exception {
44640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        URL url = mServer.getUrl("/" + filename);
44740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return Uri.parse(url.toString());
44840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
44940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
45040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen   /**
45140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    * Gets the Uri that should be used to access the mock server
45240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    *
45340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    * @param filename The name of the file to try to retrieve from the mock server
45440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    * @return the Uri to use for access the file on the mock server
45540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    */
45640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void logDBColumnData(Cursor cursor, String column) {
45740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int index = cursor.getColumnIndex(column);
45840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Log.i(LOG_TAG, "columnName: " + column);
45940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Log.i(LOG_TAG, "columnValue: " + cursor.getString(index));
46040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
46140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
46240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
46340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to create and register a new MultipleDownloadCompletedReciever
46440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
46540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * This is used to track many simultaneous downloads by keeping count of all the downloads
46640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * that have completed.
46740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
46840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return A new receiver that records and can be queried on how many downloads have completed.
46940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
47040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected MultipleDownloadsCompletedReceiver registerNewMultipleDownloadsReceiver() {
47140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        MultipleDownloadsCompletedReceiver receiver = new MultipleDownloadsCompletedReceiver();
47240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        mContext.registerReceiver(receiver, new IntentFilter(
47340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                DownloadManager.ACTION_DOWNLOAD_COMPLETE));
47440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return receiver;
47540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
47640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
47740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
47840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to verify a standard single-file download from the mock server, and clean up after
47940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * verification
48040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
48140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Note that this also calls the Download manager's remove, which cleans up the file from cache.
48240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
48340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param requestId The id of the download to remove
48440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param fileData The data to verify the file contains
48540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
48640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void verifyAndCleanupSingleFileDownload(long requestId, byte[] fileData)
48740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            throws Exception {
48840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int fileSize = fileData.length;
48940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(requestId);
49040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Cursor cursor = mDownloadManager.query(new Query().setFilterById(requestId));
49140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
49240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        try {
49340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            assertEquals(1, cursor.getCount());
49440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            assertTrue(cursor.moveToFirst());
49540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
49640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            mServer.checkForExceptions();
49740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
49840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            verifyFileSize(pfd, fileSize);
49940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            verifyFileContents(pfd, fileData);
50040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        } finally {
50140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            pfd.close();
50240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            cursor.close();
50340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            mDownloadManager.remove(requestId);
50440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
50540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
50640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
50740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
50840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Enables or disables WiFi.
50940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
51040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Note: Needs the following permissions:
51140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *  android.permission.ACCESS_WIFI_STATE
51240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *  android.permission.CHANGE_WIFI_STATE
51340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param enable true if it should be enabled, false if it should be disabled
51440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
51540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void setWiFiStateOn(boolean enable) throws Exception {
516bd06f02d02e07ca15e420ee9e50e35253646ba64Neal Nguyen        Log.i(LOG_TAG, "Setting WiFi State to: " + enable);
51740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        WifiManager manager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
51840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
51940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        manager.setWifiEnabled(enable);
52040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
52140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        String timeoutMessage = "Timed out waiting for Wifi to be "
52240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            + (enable ? "enabled!" : "disabled!");
52340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
52440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        WiFiChangedReceiver receiver = new WiFiChangedReceiver(mContext);
52540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        mContext.registerReceiver(receiver, new IntentFilter(
52640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                ConnectivityManager.CONNECTIVITY_ACTION));
52740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
52840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        synchronized (receiver) {
52940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            long timeoutTime = SystemClock.elapsedRealtime() + DEFAULT_MAX_WAIT_TIME;
53040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            boolean timedOut = false;
53140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
53240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            while (receiver.getWiFiIsOn() != enable && !timedOut) {
53340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                try {
534bd06f02d02e07ca15e420ee9e50e35253646ba64Neal Nguyen                    receiver.wait(DEFAULT_WAIT_POLL_TIME);
53540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
53640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    if (SystemClock.elapsedRealtime() > timeoutTime) {
53740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                        timedOut = true;
53840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    }
53940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                }
54040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                catch (InterruptedException e) {
54140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    // ignore InterruptedExceptions
54240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                }
54340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
54440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            if (timedOut) {
54540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                fail(timeoutMessage);
54640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
54740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
54840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(enable, receiver.getWiFiIsOn());
54940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
55040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
55140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
55240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to enables or disables airplane mode. If successful, it also broadcasts an intent
55340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * indicating that the mode has changed.
55440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
55540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Note: Needs the following permission:
55640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *  android.permission.WRITE_SETTINGS
55740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param enable true if airplane mode should be ON, false if it should be OFF
55840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
55940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void setAirplaneModeOn(boolean enable) throws Exception {
56040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int state = enable ? 1 : 0;
56140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
56240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Change the system setting
56340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Settings.System.putInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
56440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                state);
56540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
56640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        String timeoutMessage = "Timed out waiting for airplane mode to be " +
56740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                (enable ? "enabled!" : "disabled!");
56840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
56940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // wait for airplane mode to change state
57040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int currentWaitTime = 0;
57140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        while (Settings.System.getInt(mContext.getContentResolver(),
57240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                Settings.System.AIRPLANE_MODE_ON, -1) != state) {
57340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME, DEFAULT_MAX_WAIT_TIME,
57440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    timeoutMessage);
57540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
57640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
57740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Post the intent
57840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
57940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        intent.putExtra("state", true);
58040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        mContext.sendBroadcast(intent);
58140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
58240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
58340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
58440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to create a large file of random data on the SD card.
58540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
58640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param filename (optional) The name of the file to create on the SD card; pass in null to
58740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *          use a default temp filename.
58840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param type The type of file to create
58940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param subdirectory If not null, the subdirectory under the SD card where the file should go
59040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return The File that was created
59140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws IOException if there was an error while creating the file.
59240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
59340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected File createFileOnSD(String filename, long fileSize, DataType type,
59440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            String subdirectory) throws IOException {
59540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
59640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Build up the file path and name
59740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        String sdPath = Environment.getExternalStorageDirectory().getPath();
59840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        StringBuilder fullPath = new StringBuilder(sdPath);
59940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        if (subdirectory != null) {
60040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            fullPath.append(File.separatorChar).append(subdirectory);
60140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
60240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
60340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        File file = null;
60440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        if (filename == null) {
60540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            file = File.createTempFile("DMTEST_", null, new File(fullPath.toString()));
60640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
60740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        else {
60840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            fullPath.append(File.separatorChar).append(filename);
60940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            file = new File(fullPath.toString());
61040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            file.createNewFile();
61140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
61240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
61340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Fill the file with random data
61440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        DataOutputStream output = new DataOutputStream(new FileOutputStream(file));
61540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        final int CHUNK_SIZE = 1000000;  // copy random data in 1000000-char chunks
61640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        long remaining = fileSize;
61740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int nextChunkSize = CHUNK_SIZE;
61840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        byte[] randomData = null;
61940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Random rng = new LoggingRng();
62040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
62140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        try {
62240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            while (remaining > 0) {
62340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                if (remaining < CHUNK_SIZE) {
62440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    nextChunkSize = (int)remaining;
62540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    remaining = 0;
62640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                }
62740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                else {
62840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    remaining -= CHUNK_SIZE;
62940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                }
63040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
63140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                randomData = generateData(nextChunkSize, type, rng);
63240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                output.write(randomData);
63340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
63440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        } catch (IOException e) {
63540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            Log.e(LOG_TAG, "Error writing to file " + file.getAbsolutePath());
63640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            file.delete();
63740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            throw e;
63840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        } finally {
63940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            output.close();
64040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
64140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return file;
64240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
64340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
64440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
64540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to wait for a particular download to finish, or else a timeout to occur
64640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
64765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Does not wait for a receiver notification of the download.
64865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
64940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param id The download id to query on (wait for)
65040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
65165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    protected void waitForDownloadOrTimeout_skipNotification(long id) throws TimeoutException,
65240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            InterruptedException {
65340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        waitForDownloadOrTimeout(id, WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
65440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
65540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
65640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
65740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to wait for a particular download to finish, or else a timeout to occur
65840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
65965c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Also guarantees a notification has been posted for the download.
66065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
66165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * @param id The download id to query on (wait for)
66265c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     */
66365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    protected void waitForDownloadOrTimeout(long id) throws TimeoutException,
66465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen            InterruptedException {
66565c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        waitForDownloadOrTimeout_skipNotification(id);
66665c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        waitForReceiverNotifications(1);
66765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    }
66865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen
66965c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    /**
67065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Helper to wait for a particular download to finish, or else a timeout to occur
67165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
67265c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Also guarantees a notification has been posted for the download.
67365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
67440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param id The download id to query on (wait for)
67540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param poll The amount of time to wait
67640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
67740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
67840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void waitForDownloadOrTimeout(long id, long poll, long timeoutMillis)
67940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            throws TimeoutException, InterruptedException {
68040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
68165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        waitForReceiverNotifications(1);
68240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
68340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
68440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
68540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to wait for all downloads to finish, or else a specified timeout to occur
68640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
68765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Makes no guaranee that notifications have been posted for all downloads.
68865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
68940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param poll The amount of time to wait
69040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
69140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
69240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void waitForDownloadsOrTimeout(long poll, long timeoutMillis) throws TimeoutException,
69340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            InterruptedException {
69440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        doWaitForDownloadsOrTimeout(new Query(), poll, timeoutMillis);
69540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
69640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
69740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
69840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to wait for all downloads to finish, or else a timeout to occur, but does not throw
69940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
70065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Also guarantees a notification has been posted for the download.
70165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
70240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param id The id of the download to query against
70340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param poll The amount of time to wait
70440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
70540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return true if download completed successfully (didn't timeout), false otherwise
70640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
70740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected boolean waitForDownloadOrTimeoutNoThrow(long id, long poll, long timeoutMillis) {
70840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        try {
70940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
71065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen            waitForReceiverNotifications(1);
71140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        } catch (TimeoutException e) {
71240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            return false;
71340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
71440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return true;
71540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
71640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
71740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
71840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper function to synchronously wait, or timeout if the maximum threshold has been exceeded.
71940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
72040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param currentTotalWaitTime The total time waited so far
72140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param poll The amount of time to wait
72240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param maxTimeoutMillis The total wait time threshold; if we've waited more than this long,
72340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *          we timeout and fail
72440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param timedOutMessage The message to display in the failure message if we timeout
72540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return The new total amount of time we've waited so far
72640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws TimeoutException if timed out waiting for SD card to mount
72740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
72840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected int timeoutWait(int currentTotalWaitTime, long poll, long maxTimeoutMillis,
72940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            String timedOutMessage) throws TimeoutException {
73040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        long now = SystemClock.elapsedRealtime();
73140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        long end = now + poll;
73240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
73340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // if we get InterruptedException's, ignore them and just keep sleeping
73440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        while (now < end) {
73540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            try {
73640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                Thread.sleep(end - now);
73740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            } catch (InterruptedException e) {
73840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                // ignore interrupted exceptions
73940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
74040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            now = SystemClock.elapsedRealtime();
74140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
74240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
74340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        currentTotalWaitTime += poll;
74440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        if (currentTotalWaitTime > maxTimeoutMillis) {
74540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            throw new TimeoutException(timedOutMessage);
74640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
74740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return currentTotalWaitTime;
74840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
74940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
75040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
75140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to wait for all downloads to finish, or else a timeout to occur
75240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
75340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param query The query to pass to the download manager
75440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param poll The poll time to wait between checks
75540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param timeoutMillis The max amount of time (in ms) to wait for the download(s) to complete
75640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
75740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void doWaitForDownloadsOrTimeout(Query query, long poll, long timeoutMillis)
75840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            throws TimeoutException {
75940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int currentWaitTime = 0;
76040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        while (true) {
76140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            query.setFilterByStatus(DownloadManager.STATUS_PENDING | DownloadManager.STATUS_PAUSED
76240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    | DownloadManager.STATUS_RUNNING);
76340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            Cursor cursor = mDownloadManager.query(query);
76440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
76540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            try {
76665c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                if (cursor.getCount() == 0) {
76765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    Log.i(LOG_TAG, "All downloads should be done...");
76840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    break;
76940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                }
77040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                currentWaitTime = timeoutWait(currentWaitTime, poll, timeoutMillis,
77140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                        "Timed out waiting for all downloads to finish");
77240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            } finally {
77340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                cursor.close();
77440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
77540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
77640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
77740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
77840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
77940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Synchronously waits for external store to be mounted (eg: SD Card).
78040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
78140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws InterruptedException if interrupted
78240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws Exception if timed out waiting for SD card to mount
78340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
78440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void waitForExternalStoreMount() throws Exception {
78540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        String extStorageState = Environment.getExternalStorageState();
78640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int currentWaitTime = 0;
78740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        while (!extStorageState.equals(Environment.MEDIA_MOUNTED)) {
78840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            Log.i(LOG_TAG, "Waiting for SD card...");
78940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            currentWaitTime = timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME,
79040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    DEFAULT_MAX_WAIT_TIME, "Timed out waiting for SD Card to be ready!");
79140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            extStorageState = Environment.getExternalStorageState();
79240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
79340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
79440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
79540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
79640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Synchronously waits for a download to start.
79740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
79840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param dlRequest the download request id used by Download Manager to track the download.
79940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws Exception if timed out while waiting for SD card to mount
80040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
80140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void waitForDownloadToStart(long dlRequest) throws Exception {
80240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Cursor cursor = getCursor(dlRequest);
80340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        try {
80440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
80540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            int value = cursor.getInt(columnIndex);
80640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            int currentWaitTime = 0;
80740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
80840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            while (value != DownloadManager.STATUS_RUNNING &&
80940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    (value != DownloadManager.STATUS_FAILED) &&
81040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    (value != DownloadManager.STATUS_SUCCESSFUL)) {
81140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                Log.i(LOG_TAG, "Waiting for download to start...");
81240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
81340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                        MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download to start!");
81440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                cursor.requery();
81540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                assertTrue(cursor.moveToFirst());
81640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
81740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                value = cursor.getInt(columnIndex);
81840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
81940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            assertFalse("Download failed immediately after start",
82040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    value == DownloadManager.STATUS_FAILED);
82140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        } finally {
82240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            cursor.close();
82340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
82440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
82540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
82640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
82765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Convenience function to wait for just 1 notification of a download.
82865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
82965c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * @throws Exception if timed out while waiting
83065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     */
83165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    protected void waitForReceiverNotification() throws Exception {
83265c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        waitForReceiverNotifications(1);
83365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    }
83465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen
83565c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    /**
83665c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Synchronously waits for our receiver to receive notification for a given number of
83765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * downloads.
83865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
83965c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * @param targetNumber The number of notifications for unique downloads to wait for; pass in
84065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *         -1 to not wait for notification.
84165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * @throws Exception if timed out while waiting
84265c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     */
84365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    protected void waitForReceiverNotifications(int targetNumber) throws TimeoutException {
84465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        int count = mReceiver.numDownloadsCompleted();
84565c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        int currentWaitTime = 0;
84665c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen
84765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        while (count < targetNumber) {
84865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen            Log.i(LOG_TAG, "Waiting for notification of downloads...");
84965c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen            currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
85065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download notifications!"
85165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    + " Received " + count + "notifications.");
85265c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen            count = mReceiver.numDownloadsCompleted();
85365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        }
85465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    }
85565c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen
85665c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    /**
85740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Synchronously waits for a file to increase in size (such as to monitor that a download is
85840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * progressing).
85940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
86040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param file The file whose size to track.
86140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws Exception if timed out while waiting for the file to grow in size.
86240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
86340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void waitForFileToGrow(File file) throws Exception {
86440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int currentWaitTime = 0;
86540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
86640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // File may not even exist yet, so wait until it does (or we timeout)
86740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        while (!file.exists()) {
86840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            Log.i(LOG_TAG, "Waiting for file to exist...");
86940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
87040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be created.");
87140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
87240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
87340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Get original file size...
87440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        long originalSize = file.length();
87540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
87640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        while (file.length() <= originalSize) {
87740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            Log.i(LOG_TAG, "Waiting for file to be written to...");
87840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
87940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be written to.");
88040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
88140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
88240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
88340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
88440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to remove all downloads that are registered with the DL Manager.
88540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
88640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Note: This gives us a clean slate b/c it includes downloads that are pending, running,
88740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * paused, or have completed.
88840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
88940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void removeAllCurrentDownloads() {
89040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Log.i(LOG_TAG, "Removing all current registered downloads...");
89140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Cursor cursor = mDownloadManager.query(new Query());
89240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        try {
89340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            if (cursor.moveToFirst()) {
89440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                do {
89540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    int index = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
89640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    long downloadId = cursor.getLong(index);
89740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
89840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    mDownloadManager.remove(downloadId);
89940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                } while (cursor.moveToNext());
90040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
90140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        } finally {
90240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            cursor.close();
90340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
90440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
90540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
90640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
90740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to perform a standard enqueue of data to the mock server.
90840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
90940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param body The body to return in the response from the server
91040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
91140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected long doStandardEnqueue(byte[] body) throws Exception {
91240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Prepare the mock server with a standard response
91340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        enqueueResponse(HTTP_OK, body);
91440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return doCommonStandardEnqueue();
91540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
91640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
91740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
91840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to perform a standard enqueue of data to the mock server.
91940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
92040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param body The body to return in the response from the server, contained in the file
92140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
92240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected long doStandardEnqueue(File body) throws Exception {
92340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Prepare the mock server with a standard response
92440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        enqueueResponse(HTTP_OK, body);
92540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return doCommonStandardEnqueue();
92640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
92740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
92840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
92940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to do the additional steps (setting title and Uri of default filename) when
93040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * doing a standard enqueue request to the server.
93140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
93240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected long doCommonStandardEnqueue() throws Exception {
93340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Uri uri = getServerUri(DEFAULT_FILENAME);
93440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Request request = new Request(uri);
93540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        request.setTitle(DEFAULT_FILENAME);
93640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
93740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        long dlRequest = mDownloadManager.enqueue(request);
93840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Log.i(LOG_TAG, "request ID: " + dlRequest);
93940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return dlRequest;
94040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
94140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
94240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
94340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to verify an int value in a Cursor
94440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
94540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param cursor The cursor containing the query results
94640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param columnName The name of the column to query
94740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param expected The expected int value
94840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
94940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void verifyInt(Cursor cursor, String columnName, int expected) {
95040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int index = cursor.getColumnIndex(columnName);
95140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int actual = cursor.getInt(index);
95240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(expected, actual);
95340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
95440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
95540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
95640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to verify a String value in a Cursor
95740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
95840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param cursor The cursor containing the query results
95940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param columnName The name of the column to query
96040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param expected The expected String value
96140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
96240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void verifyString(Cursor cursor, String columnName, String expected) {
96340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int index = cursor.getColumnIndex(columnName);
96440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        String actual = cursor.getString(index);
96540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Log.i(LOG_TAG, ": " + actual);
96640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(expected, actual);
96740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
96840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
96940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
97040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Performs a query based on ID and returns a Cursor for the query.
97140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
97240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param id The id of the download in DL Manager; pass -1 to query all downloads
97340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return A cursor for the query results
97440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
97540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected Cursor getCursor(long id) throws Exception {
97640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Query query = new Query();
97740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        if (id != -1) {
97840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            query.setFilterById(id);
97940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
98040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
98140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Cursor cursor = mDownloadManager.query(query);
98240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int currentWaitTime = 0;
98340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
98440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        try {
98540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            while (!cursor.moveToFirst()) {
98640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                Thread.sleep(DEFAULT_WAIT_POLL_TIME);
98740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                currentWaitTime += DEFAULT_WAIT_POLL_TIME;
98840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                if (currentWaitTime > DEFAULT_MAX_WAIT_TIME) {
98940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    fail("timed out waiting for a non-null query result");
99040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                }
99140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                cursor.requery();
99240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
99340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        } catch (Exception e) {
99440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            cursor.close();
99540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            throw e;
99640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
99740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return cursor;
99840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
99940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
100040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen}