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;
3040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.os.Environment;
3140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.os.ParcelFileDescriptor;
32c09cdce1b05075da808ae080b9905a14a3e1e627Christopher Tateimport android.os.UserHandle;
3340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.os.ParcelFileDescriptor.AutoCloseInputStream;
3482e891b3259350a92b55969a6380ca1240ee0829Vasu Noriimport android.os.SystemClock;
3540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.provider.Settings;
3640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.test.InstrumentationTestCase;
3740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport android.util.Log;
3840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
39b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkeyimport com.google.mockwebserver.MockResponse;
40b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkeyimport com.google.mockwebserver.MockWebServer;
41b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey
4240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.io.DataInputStream;
4340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.io.DataOutputStream;
4440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.io.File;
4540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.io.FileInputStream;
46b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkeyimport java.io.FileNotFoundException;
4740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.io.FileOutputStream;
4840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.io.IOException;
4940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.net.URL;
5082e891b3259350a92b55969a6380ca1240ee0829Vasu Noriimport java.util.ArrayList;
51df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyenimport java.util.Collections;
52df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyenimport java.util.HashSet;
5340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenimport java.util.Random;
54df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyenimport java.util.Set;
5582e891b3259350a92b55969a6380ca1240ee0829Vasu Noriimport java.util.concurrent.TimeoutException;
5640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
57b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkeyimport libcore.io.Streams;
58b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey
5940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen/**
6040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen * Base class for Instrumented tests for the Download Manager.
6140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen */
6240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyenpublic class DownloadManagerBaseTest extends InstrumentationTestCase {
636f35c0ecba6cb1ce5d7563a9962acf9557dbacedVasu Nori    private static final String TAG = "DownloadManagerBaseTest";
6440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected DownloadManager mDownloadManager = null;
65b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey    private MockWebServer mServer = null;
6640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected String mFileType = "text/plain";
6740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected Context mContext = null;
6863e5d79a163461651cd8bd89d3de691ef7649109Neal Nguyen    protected MultipleDownloadsCompletedReceiver mReceiver = null;
6982e891b3259350a92b55969a6380ca1240ee0829Vasu Nori    protected static final int DEFAULT_FILE_SIZE = 10 * 1024;  // 10kb
7040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int FILE_BLOCK_READ_SIZE = 1024 * 1024;
7140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
7240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final String LOG_TAG = "android.net.DownloadManagerBaseTest";
7340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int HTTP_OK = 200;
74df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen    protected static final int HTTP_REDIRECT = 307;
7540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int HTTP_PARTIAL_CONTENT = 206;
7640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int HTTP_NOT_FOUND = 404;
7740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int HTTP_SERVICE_UNAVAILABLE = 503;
7840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected String DEFAULT_FILENAME = "somefile.txt";
7940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
8040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int DEFAULT_MAX_WAIT_TIME = 2 * 60 * 1000;  // 2 minutes
8140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int DEFAULT_WAIT_POLL_TIME = 5 * 1000;  // 5 seconds
8240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
8340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int WAIT_FOR_DOWNLOAD_POLL_TIME = 1 * 1000;  // 1 second
8440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 5 * 60 * 1000; // 5 minutes
85bd06f02d02e07ca15e420ee9e50e35253646ba64Neal Nguyen    protected static final int MAX_WAIT_FOR_LARGE_DOWNLOAD_TIME = 15 * 60 * 1000; // 15 minutes
8640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
8782e891b3259350a92b55969a6380ca1240ee0829Vasu Nori    protected static final int DOWNLOAD_TO_SYSTEM_CACHE = 1;
8882e891b3259350a92b55969a6380ca1240ee0829Vasu Nori    protected static final int DOWNLOAD_TO_DOWNLOAD_CACHE_DIR = 2;
8982e891b3259350a92b55969a6380ca1240ee0829Vasu Nori
9040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    // Just a few popular file types used to return from a download
9140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected enum DownloadFileType {
9240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        PLAINTEXT,
9340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        APK,
9440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        GIF,
9540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        GARBAGE,
9640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        UNRECOGNIZED,
9740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        ZIP
9840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
9940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
10040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected enum DataType {
10140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        TEXT,
10240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        BINARY
10340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
10440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
10540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    public static class LoggingRng extends Random {
10640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
10740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        /**
10840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Constructor
10940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         *
11040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Creates RNG with self-generated seed value.
11140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         */
11240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        public LoggingRng() {
11340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            this(SystemClock.uptimeMillis());
11440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
11540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
11640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        /**
11740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Constructor
11840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         *
11940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Creats RNG with given initial seed value
12040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
12140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * @param seed The initial seed value
12240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         */
12340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        public LoggingRng(long seed) {
12440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            super(seed);
12540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            Log.i(LOG_TAG, "Seeding RNG with value: " + seed);
12640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
12740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
12840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
12940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    public static class MultipleDownloadsCompletedReceiver extends BroadcastReceiver {
13040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        private volatile int mNumDownloadsCompleted = 0;
131df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen        private Set<Long> downloadIds = Collections.synchronizedSet(new HashSet<Long>());
13240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
13340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        /**
13440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * {@inheritDoc}
13540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         */
13640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        @Override
13740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        public void onReceive(Context context, Intent intent) {
13840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            if (intent.getAction().equalsIgnoreCase(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
13963e5d79a163461651cd8bd89d3de691ef7649109Neal Nguyen                synchronized(this) {
14065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    long id = intent.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
14165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    Log.i(LOG_TAG, "Received Notification for download: " + id);
14265c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    if (!downloadIds.contains(id)) {
14365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        ++mNumDownloadsCompleted;
14465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        Log.i(LOG_TAG, "MultipleDownloadsCompletedReceiver got intent: " +
14565c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                                intent.getAction() + " --> total count: " + mNumDownloadsCompleted);
14665c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        downloadIds.add(id);
14765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen
14865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        DownloadManager dm = (DownloadManager)context.getSystemService(
14965c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                                Context.DOWNLOAD_SERVICE);
15065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen
15165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        Cursor cursor = dm.query(new Query().setFilterById(id));
15265c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        try {
15365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                            if (cursor.moveToFirst()) {
15465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                                int status = cursor.getInt(cursor.getColumnIndex(
15565c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                                        DownloadManager.COLUMN_STATUS));
15665c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                                Log.i(LOG_TAG, "Download status is: " + status);
15765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                            } else {
15865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                                fail("No status found for completed download!");
15965c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                            }
16065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        } finally {
16165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                            cursor.close();
16265c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        }
16365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    } else {
16465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                        Log.i(LOG_TAG, "Notification for id: " + id + " has already been made.");
16565c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    }
16663e5d79a163461651cd8bd89d3de691ef7649109Neal Nguyen                }
16740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
16840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
16940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
17040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        /**
17140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Gets the number of times the {@link #onReceive} callback has been called for the
17240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * {@link DownloadManager.ACTION_DOWNLOAD_COMPLETED} action, indicating the number of
17340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * downloads completed thus far.
17440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         *
17540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * @return the number of downloads completed so far.
17640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         */
17740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        public int numDownloadsCompleted() {
17840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            return mNumDownloadsCompleted;
17940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
180df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen
181df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen        /**
182df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen         * Gets the list of download IDs.
183df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen         * @return A Set<Long> with the ids of the completed downloads.
184df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen         */
185df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen        public Set<Long> getDownloadIds() {
186bd06f02d02e07ca15e420ee9e50e35253646ba64Neal Nguyen            synchronized(this) {
187df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen                Set<Long> returnIds = new HashSet<Long>(downloadIds);
188df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen                return returnIds;
189df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen            }
190df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen        }
191df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen
19240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
19340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
19440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    public static class WiFiChangedReceiver extends BroadcastReceiver {
19540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        private Context mContext = null;
19640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
19740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        /**
19840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Constructor
19940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         *
20040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Sets the current state of WiFi.
20140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         *
20240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * @param context The current app {@link Context}.
20340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         */
20440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        public WiFiChangedReceiver(Context context) {
20540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            mContext = context;
20640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
20740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
20840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        /**
20940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * {@inheritDoc}
21040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         */
21140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        @Override
21240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        public void onReceive(Context context, Intent intent) {
21340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            if (intent.getAction().equalsIgnoreCase(ConnectivityManager.CONNECTIVITY_ACTION)) {
21440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                Log.i(LOG_TAG, "ConnectivityManager state change: " + intent.getAction());
21540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                synchronized (this) {
21640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    this.notify();
21740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                }
21840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
21940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
22040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
22140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        /**
22240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * Gets the current state of WiFi.
22340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         *
22440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         * @return Returns true if WiFi is on, false otherwise.
22540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen         */
22640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        public boolean getWiFiIsOn() {
22740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            ConnectivityManager connManager = (ConnectivityManager)mContext.getSystemService(
22840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    Context.CONNECTIVITY_SERVICE);
22940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            NetworkInfo info = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
230bd06f02d02e07ca15e420ee9e50e35253646ba64Neal Nguyen            Log.i(LOG_TAG, "WiFi Connection state is currently: " + info.isConnected());
23140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            return info.isConnected();
23240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
23340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
23440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
23540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
23640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * {@inheritDoc}
23740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
23840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    @Override
23940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    public void setUp() throws Exception {
24040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        mContext = getInstrumentation().getContext();
24140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE);
24240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        mServer = new MockWebServer();
243b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey        mServer.play();
24463e5d79a163461651cd8bd89d3de691ef7649109Neal Nguyen        mReceiver = registerNewMultipleDownloadsReceiver();
24540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Note: callers overriding this should call mServer.play() with the desired port #
24640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
24740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
24840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
249b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey     * Helper to build a response from the MockWebServer with no body.
250df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen     *
251df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen     * @param status The HTTP status code to return for this response
252df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen     * @return Returns the mock web server response that was queued (which can be modified)
253df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen     */
254b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey    protected MockResponse buildResponse(int status) {
255b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey        MockResponse response = new MockResponse().setResponseCode(status);
256b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey        response.setHeader("Content-type", mFileType);
257b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey        return response;
258df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen    }
259df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen
260df7a865bbf45b57c60d294d5ac721e67e69a2dd6Neal Nguyen    /**
261b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey     * Helper to build a response from the MockWebServer.
26240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
26340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param status The HTTP status code to return for this response
26440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param body The body to return in this response
26540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return Returns the mock web server response that was queued (which can be modified)
26640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
267b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey    protected MockResponse buildResponse(int status, byte[] body) {
268b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey        return buildResponse(status).setBody(body);
26940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
27040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
27140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
272b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey     * Helper to build a response from the MockWebServer.
27340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
27440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param status The HTTP status code to return for this response
27540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param bodyFile The body to return in this response
27640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return Returns the mock web server response that was queued (which can be modified)
27740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
278b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey    protected MockResponse buildResponse(int status, File bodyFile)
279b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey            throws FileNotFoundException, IOException {
280b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey        final byte[] body = Streams.readFully(new FileInputStream(bodyFile));
281b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey        return buildResponse(status).setBody(body);
28240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
28340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
284b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey    protected void enqueueResponse(MockResponse resp) {
285b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey        mServer.enqueue(resp);
28640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
28740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
28840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
28940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to generate a random blob of bytes.
29040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
29140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param size The size of the data to generate
292b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey     * @param type The type of data to generate: currently, one of {@link DataType#TEXT} or
293b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey     *         {@link DataType#BINARY}.
29440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return The random data that is generated.
29540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
29640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected byte[] generateData(int size, DataType type) {
29740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return generateData(size, type, null);
29840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
29940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
30040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
30140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to generate a random blob of bytes using a given RNG.
30240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
30340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param size The size of the data to generate
304b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey     * @param type The type of data to generate: currently, one of {@link DataType#TEXT} or
305b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey     *         {@link DataType#BINARY}.
30640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param rng (optional) The RNG to use; pass null to use
30740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return The random data that is generated.
30840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
30940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected byte[] generateData(int size, DataType type, Random rng) {
31040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int min = Byte.MIN_VALUE;
31140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int max = Byte.MAX_VALUE;
31240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
31340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Only use chars in the HTTP ASCII printable character range for Text
31440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        if (type == DataType.TEXT) {
31540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            min = 32;
31640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            max = 126;
31740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
31840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        byte[] result = new byte[size];
31940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Log.i(LOG_TAG, "Generating data of size: " + size);
32040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
32140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        if (rng == null) {
32240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            rng = new LoggingRng();
32340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
32440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
32540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        for (int i = 0; i < size; ++i) {
32640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            result[i] = (byte) (min + rng.nextInt(max - min + 1));
32740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
32840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return result;
32940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
33040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
33140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
33240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to verify the size of a file.
33340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
33440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param pfd The input file to compare the size of
33540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param size The expected size of the file
33640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
33740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void verifyFileSize(ParcelFileDescriptor pfd, long size) {
33840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(pfd.getStatSize(), size);
33940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
34040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
34140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
34240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to verify the contents of a downloaded file versus a byte[].
34340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
34440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param actual The file of whose contents to verify
34540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param expected The data we expect to find in the aforementioned file
34640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws IOException if there was a problem reading from the file
34740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
34840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void verifyFileContents(ParcelFileDescriptor actual, byte[] expected)
34940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            throws IOException {
35040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(actual);
35140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        long fileSize = actual.getStatSize();
35240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
35340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertTrue(fileSize <= Integer.MAX_VALUE);
35440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(expected.length, fileSize);
35540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
35640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        byte[] actualData = new byte[expected.length];
35740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(input.read(actualData), fileSize);
35840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        compareByteArrays(actualData, expected);
35940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
36040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
36140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
36240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to compare 2 byte arrays.
36340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
36440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param actual The array whose data we want to verify
36540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param expected The array of data we expect to see
36640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
36740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void compareByteArrays(byte[] actual, byte[] expected) {
36840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(actual.length, expected.length);
36940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int length = actual.length;
37040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        for (int i = 0; i < length; ++i) {
37140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            // assert has a bit of overhead, so only do the assert when the values are not the same
37240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            if (actual[i] != expected[i]) {
37340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                fail("Byte arrays are not equal.");
37440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
37540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
37640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
37740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
37840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
37940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Verifies the contents of a downloaded file versus the contents of a File.
38040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
38140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param pfd The file whose data we want to verify
38240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param file The file containing the data we expect to see in the aforementioned file
38340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws IOException If there was a problem reading either of the two files
38440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
38540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void verifyFileContents(ParcelFileDescriptor pfd, File file) throws IOException {
38640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        byte[] actual = new byte[FILE_BLOCK_READ_SIZE];
38740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        byte[] expected = new byte[FILE_BLOCK_READ_SIZE];
38840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
38940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
39040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
39140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(file.length(), pfd.getStatSize());
39240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
39340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        DataInputStream inFile = new DataInputStream(new FileInputStream(file));
39440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int actualRead = 0;
39540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int expectedRead = 0;
39640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
39740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        while (((actualRead = input.read(actual)) != -1) &&
39840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                ((expectedRead = inFile.read(expected)) != -1)) {
39940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            assertEquals(actualRead, expectedRead);
40040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            compareByteArrays(actual, expected);
40140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
40240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
40340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
40440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
40540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Sets the MIME type of file that will be served from the mock server
40640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
40740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param type The MIME type to return from the server
40840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
40940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void setServerMimeType(DownloadFileType type) {
41040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        mFileType = getMimeMapping(type);
41140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
41240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
41340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
41440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Gets the MIME content string for a given type
41540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
41640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param type The MIME type to return
41740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return the String representation of that MIME content type
41840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
41940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected String getMimeMapping(DownloadFileType type) {
42040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        switch (type) {
42140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            case APK:
42240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                return "application/vnd.android.package-archive";
42340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            case GIF:
42440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                return "image/gif";
42540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            case ZIP:
42640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                return "application/x-zip-compressed";
42740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            case GARBAGE:
42840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                return "zip\\pidy/doo/da";
42940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            case UNRECOGNIZED:
43040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                return "application/new.undefined.type.of.app";
43140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
43240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return "text/plain";
43340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
43440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
43540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
43640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Gets the Uri that should be used to access the mock server
43740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
43840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param filename The name of the file to try to retrieve from the mock server
43940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return the Uri to use for access the file on the mock server
44040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
44140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected Uri getServerUri(String filename) throws Exception {
44240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        URL url = mServer.getUrl("/" + filename);
44340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return Uri.parse(url.toString());
44440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
44540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
44640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen   /**
44740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    * Gets the Uri that should be used to access the mock server
44840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    *
44940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    * @param filename The name of the file to try to retrieve from the mock server
45040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    * @return the Uri to use for access the file on the mock server
45140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    */
45240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void logDBColumnData(Cursor cursor, String column) {
45340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int index = cursor.getColumnIndex(column);
45440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Log.i(LOG_TAG, "columnName: " + column);
45540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Log.i(LOG_TAG, "columnValue: " + cursor.getString(index));
45640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
45740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
45840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
45940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to create and register a new MultipleDownloadCompletedReciever
46040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
46140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * This is used to track many simultaneous downloads by keeping count of all the downloads
46240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * that have completed.
46340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
46440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return A new receiver that records and can be queried on how many downloads have completed.
46540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
46640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected MultipleDownloadsCompletedReceiver registerNewMultipleDownloadsReceiver() {
46740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        MultipleDownloadsCompletedReceiver receiver = new MultipleDownloadsCompletedReceiver();
46840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        mContext.registerReceiver(receiver, new IntentFilter(
46940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                DownloadManager.ACTION_DOWNLOAD_COMPLETE));
47040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return receiver;
47140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
47240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
47340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
47440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to verify a standard single-file download from the mock server, and clean up after
47540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * verification
47640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
47740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Note that this also calls the Download manager's remove, which cleans up the file from cache.
47840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
47940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param requestId The id of the download to remove
48040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param fileData The data to verify the file contains
48140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
48240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void verifyAndCleanupSingleFileDownload(long requestId, byte[] fileData)
48340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            throws Exception {
48440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int fileSize = fileData.length;
48540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(requestId);
48640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Cursor cursor = mDownloadManager.query(new Query().setFilterById(requestId));
48740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
48840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        try {
48940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            assertEquals(1, cursor.getCount());
49040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            assertTrue(cursor.moveToFirst());
49140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
49240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            verifyFileSize(pfd, fileSize);
49340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            verifyFileContents(pfd, fileData);
49440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        } finally {
49540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            pfd.close();
49640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            cursor.close();
49740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            mDownloadManager.remove(requestId);
49840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
49940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
50040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
50140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
50240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Enables or disables WiFi.
50340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
50440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Note: Needs the following permissions:
50540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *  android.permission.ACCESS_WIFI_STATE
50640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *  android.permission.CHANGE_WIFI_STATE
50740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param enable true if it should be enabled, false if it should be disabled
50840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
50940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void setWiFiStateOn(boolean enable) throws Exception {
510bd06f02d02e07ca15e420ee9e50e35253646ba64Neal Nguyen        Log.i(LOG_TAG, "Setting WiFi State to: " + enable);
51140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        WifiManager manager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
51240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
51340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        manager.setWifiEnabled(enable);
51440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
51540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        String timeoutMessage = "Timed out waiting for Wifi to be "
51640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            + (enable ? "enabled!" : "disabled!");
51740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
51840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        WiFiChangedReceiver receiver = new WiFiChangedReceiver(mContext);
51940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        mContext.registerReceiver(receiver, new IntentFilter(
52040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                ConnectivityManager.CONNECTIVITY_ACTION));
52140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
52240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        synchronized (receiver) {
52340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            long timeoutTime = SystemClock.elapsedRealtime() + DEFAULT_MAX_WAIT_TIME;
52440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            boolean timedOut = false;
52540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
52640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            while (receiver.getWiFiIsOn() != enable && !timedOut) {
52740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                try {
528bd06f02d02e07ca15e420ee9e50e35253646ba64Neal Nguyen                    receiver.wait(DEFAULT_WAIT_POLL_TIME);
52940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
53040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    if (SystemClock.elapsedRealtime() > timeoutTime) {
53140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                        timedOut = true;
53240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    }
53340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                }
53440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                catch (InterruptedException e) {
53540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    // ignore InterruptedExceptions
53640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                }
53740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
53840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            if (timedOut) {
53940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                fail(timeoutMessage);
54040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
54140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
54240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(enable, receiver.getWiFiIsOn());
54340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
54440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
54540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
54640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to enables or disables airplane mode. If successful, it also broadcasts an intent
54740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * indicating that the mode has changed.
54840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
54940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Note: Needs the following permission:
55040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *  android.permission.WRITE_SETTINGS
55140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param enable true if airplane mode should be ON, false if it should be OFF
55240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
55340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void setAirplaneModeOn(boolean enable) throws Exception {
55440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int state = enable ? 1 : 0;
55540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
55640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Change the system setting
557c09cdce1b05075da808ae080b9905a14a3e1e627Christopher Tate        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON,
55840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                state);
55940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
56040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        String timeoutMessage = "Timed out waiting for airplane mode to be " +
56140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                (enable ? "enabled!" : "disabled!");
56240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
56340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // wait for airplane mode to change state
56440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int currentWaitTime = 0;
565c09cdce1b05075da808ae080b9905a14a3e1e627Christopher Tate        while (Settings.Global.getInt(mContext.getContentResolver(),
566c09cdce1b05075da808ae080b9905a14a3e1e627Christopher Tate                Settings.Global.AIRPLANE_MODE_ON, -1) != state) {
56740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME, DEFAULT_MAX_WAIT_TIME,
56840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    timeoutMessage);
56940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
57040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
57140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Post the intent
57240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
57340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        intent.putExtra("state", true);
574c09cdce1b05075da808ae080b9905a14a3e1e627Christopher Tate        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
57540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
57640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
57740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
57840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to create a large file of random data on the SD card.
57940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
58040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param filename (optional) The name of the file to create on the SD card; pass in null to
58140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *          use a default temp filename.
58240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param type The type of file to create
58340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param subdirectory If not null, the subdirectory under the SD card where the file should go
58440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return The File that was created
58540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws IOException if there was an error while creating the file.
58640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
58740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected File createFileOnSD(String filename, long fileSize, DataType type,
58840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            String subdirectory) throws IOException {
58940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
59040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Build up the file path and name
59140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        String sdPath = Environment.getExternalStorageDirectory().getPath();
59240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        StringBuilder fullPath = new StringBuilder(sdPath);
59340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        if (subdirectory != null) {
59440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            fullPath.append(File.separatorChar).append(subdirectory);
59540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
59640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
59740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        File file = null;
59840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        if (filename == null) {
59940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            file = File.createTempFile("DMTEST_", null, new File(fullPath.toString()));
60040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
60140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        else {
60240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            fullPath.append(File.separatorChar).append(filename);
60340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            file = new File(fullPath.toString());
60440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            file.createNewFile();
60540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
60640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
60740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Fill the file with random data
60840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        DataOutputStream output = new DataOutputStream(new FileOutputStream(file));
60940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        final int CHUNK_SIZE = 1000000;  // copy random data in 1000000-char chunks
61040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        long remaining = fileSize;
61140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int nextChunkSize = CHUNK_SIZE;
61240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        byte[] randomData = null;
61340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Random rng = new LoggingRng();
6146f35c0ecba6cb1ce5d7563a9962acf9557dbacedVasu Nori        byte[] chunkSizeData = generateData(nextChunkSize, type, rng);
61540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
61640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        try {
61740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            while (remaining > 0) {
61840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                if (remaining < CHUNK_SIZE) {
61940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    nextChunkSize = (int)remaining;
62040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    remaining = 0;
6216f35c0ecba6cb1ce5d7563a9962acf9557dbacedVasu Nori                    randomData = generateData(nextChunkSize, type, rng);
62240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                }
62340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                else {
62440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    remaining -= CHUNK_SIZE;
6256f35c0ecba6cb1ce5d7563a9962acf9557dbacedVasu Nori                    randomData = chunkSizeData;
62640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                }
62740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                output.write(randomData);
6286f35c0ecba6cb1ce5d7563a9962acf9557dbacedVasu Nori                Log.i(TAG, "while creating " + fileSize + " file, " +
6296f35c0ecba6cb1ce5d7563a9962acf9557dbacedVasu Nori                        "remaining bytes to be written: " + remaining);
63040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
63140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        } catch (IOException e) {
63240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            Log.e(LOG_TAG, "Error writing to file " + file.getAbsolutePath());
63340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            file.delete();
63440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            throw e;
63540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        } finally {
63640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            output.close();
63740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
63840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return file;
63940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
64040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
64140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
64240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to wait for a particular download to finish, or else a timeout to occur
64340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
64465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Does not wait for a receiver notification of the download.
64565c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
64640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param id The download id to query on (wait for)
64740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
64865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    protected void waitForDownloadOrTimeout_skipNotification(long id) throws TimeoutException,
64940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            InterruptedException {
65040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        waitForDownloadOrTimeout(id, WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
65140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
65240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
65340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
65440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to wait for a particular download to finish, or else a timeout to occur
65540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
65665c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Also guarantees a notification has been posted for the download.
65765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
65865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * @param id The download id to query on (wait for)
65965c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     */
66065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    protected void waitForDownloadOrTimeout(long id) throws TimeoutException,
66165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen            InterruptedException {
66265c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        waitForDownloadOrTimeout_skipNotification(id);
66365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        waitForReceiverNotifications(1);
66465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    }
66565c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen
66665c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    /**
66765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Helper to wait for a particular download to finish, or else a timeout to occur
66865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
66965c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Also guarantees a notification has been posted for the download.
67065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
67140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param id The download id to query on (wait for)
67240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param poll The amount of time to wait
67340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
67440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
67540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void waitForDownloadOrTimeout(long id, long poll, long timeoutMillis)
67640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            throws TimeoutException, InterruptedException {
67740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
67865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        waitForReceiverNotifications(1);
67940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
68040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
68140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
68240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to wait for all downloads to finish, or else a specified timeout to occur
68340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
68465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Makes no guaranee that notifications have been posted for all downloads.
68565c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
68640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param poll The amount of time to wait
68740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
68840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
68940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void waitForDownloadsOrTimeout(long poll, long timeoutMillis) throws TimeoutException,
69040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            InterruptedException {
69140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        doWaitForDownloadsOrTimeout(new Query(), poll, timeoutMillis);
69240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
69340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
69440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
69540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to wait for all downloads to finish, or else a timeout to occur, but does not throw
69640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
69765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Also guarantees a notification has been posted for the download.
69865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
69940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param id The id of the download to query against
70040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param poll The amount of time to wait
70140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
70240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return true if download completed successfully (didn't timeout), false otherwise
70340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
70440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected boolean waitForDownloadOrTimeoutNoThrow(long id, long poll, long timeoutMillis) {
70540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        try {
70640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
70765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen            waitForReceiverNotifications(1);
70840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        } catch (TimeoutException e) {
70940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            return false;
71040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
71140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return true;
71240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
71340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
71440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
71540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper function to synchronously wait, or timeout if the maximum threshold has been exceeded.
71640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
71740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param currentTotalWaitTime The total time waited so far
71840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param poll The amount of time to wait
71940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param maxTimeoutMillis The total wait time threshold; if we've waited more than this long,
72040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *          we timeout and fail
72140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param timedOutMessage The message to display in the failure message if we timeout
72240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return The new total amount of time we've waited so far
72340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws TimeoutException if timed out waiting for SD card to mount
72440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
72540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected int timeoutWait(int currentTotalWaitTime, long poll, long maxTimeoutMillis,
72640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            String timedOutMessage) throws TimeoutException {
72740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        long now = SystemClock.elapsedRealtime();
72840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        long end = now + poll;
72940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
73040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // if we get InterruptedException's, ignore them and just keep sleeping
73140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        while (now < end) {
73240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            try {
73340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                Thread.sleep(end - now);
73440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            } catch (InterruptedException e) {
73540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                // ignore interrupted exceptions
73640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
73740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            now = SystemClock.elapsedRealtime();
73840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
73940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
74040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        currentTotalWaitTime += poll;
74140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        if (currentTotalWaitTime > maxTimeoutMillis) {
74240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            throw new TimeoutException(timedOutMessage);
74340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
74440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return currentTotalWaitTime;
74540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
74640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
74740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
74840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to wait for all downloads to finish, or else a timeout to occur
74940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
75040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param query The query to pass to the download manager
75140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param poll The poll time to wait between checks
75240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param timeoutMillis The max amount of time (in ms) to wait for the download(s) to complete
75340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
75440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void doWaitForDownloadsOrTimeout(Query query, long poll, long timeoutMillis)
75540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            throws TimeoutException {
75640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int currentWaitTime = 0;
75740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        while (true) {
75840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            query.setFilterByStatus(DownloadManager.STATUS_PENDING | DownloadManager.STATUS_PAUSED
75940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    | DownloadManager.STATUS_RUNNING);
76040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            Cursor cursor = mDownloadManager.query(query);
76140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
76240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            try {
76365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                if (cursor.getCount() == 0) {
76465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    Log.i(LOG_TAG, "All downloads should be done...");
76540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    break;
76640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                }
76740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                currentWaitTime = timeoutWait(currentWaitTime, poll, timeoutMillis,
76840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                        "Timed out waiting for all downloads to finish");
76940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            } finally {
77040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                cursor.close();
77140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
77240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
77340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
77440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
77540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
77640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Synchronously waits for external store to be mounted (eg: SD Card).
77740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
77840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws InterruptedException if interrupted
77940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws Exception if timed out waiting for SD card to mount
78040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
78140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void waitForExternalStoreMount() throws Exception {
78240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        String extStorageState = Environment.getExternalStorageState();
78340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int currentWaitTime = 0;
78440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        while (!extStorageState.equals(Environment.MEDIA_MOUNTED)) {
78540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            Log.i(LOG_TAG, "Waiting for SD card...");
78640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            currentWaitTime = timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME,
78740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    DEFAULT_MAX_WAIT_TIME, "Timed out waiting for SD Card to be ready!");
78840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            extStorageState = Environment.getExternalStorageState();
78940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
79040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
79140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
79240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
79340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Synchronously waits for a download to start.
79440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
79540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param dlRequest the download request id used by Download Manager to track the download.
79640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws Exception if timed out while waiting for SD card to mount
79740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
79840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void waitForDownloadToStart(long dlRequest) throws Exception {
79940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Cursor cursor = getCursor(dlRequest);
80040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        try {
80140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
80240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            int value = cursor.getInt(columnIndex);
80340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            int currentWaitTime = 0;
80440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
80540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            while (value != DownloadManager.STATUS_RUNNING &&
80640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    (value != DownloadManager.STATUS_FAILED) &&
80740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    (value != DownloadManager.STATUS_SUCCESSFUL)) {
80840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                Log.i(LOG_TAG, "Waiting for download to start...");
80940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
81040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                        MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download to start!");
81140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                cursor.requery();
81240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                assertTrue(cursor.moveToFirst());
81340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
81440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                value = cursor.getInt(columnIndex);
81540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
81640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            assertFalse("Download failed immediately after start",
81740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    value == DownloadManager.STATUS_FAILED);
81840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        } finally {
81940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            cursor.close();
82040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
82140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
82240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
82340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
82465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Convenience function to wait for just 1 notification of a download.
82565c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
82665c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * @throws Exception if timed out while waiting
82765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     */
82865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    protected void waitForReceiverNotification() throws Exception {
82965c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        waitForReceiverNotifications(1);
83065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    }
83165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen
83265c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    /**
83365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * Synchronously waits for our receiver to receive notification for a given number of
83465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * downloads.
83565c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *
83665c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * @param targetNumber The number of notifications for unique downloads to wait for; pass in
83765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     *         -1 to not wait for notification.
83865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     * @throws Exception if timed out while waiting
83965c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen     */
84065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    protected void waitForReceiverNotifications(int targetNumber) throws TimeoutException {
84165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        int count = mReceiver.numDownloadsCompleted();
84265c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        int currentWaitTime = 0;
84365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen
84465c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        while (count < targetNumber) {
84565c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen            Log.i(LOG_TAG, "Waiting for notification of downloads...");
84665c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen            currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
84765c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download notifications!"
84865c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen                    + " Received " + count + "notifications.");
84965c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen            count = mReceiver.numDownloadsCompleted();
85065c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen        }
85165c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    }
85265c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen
85365c36e6133be04e008bc164b62d42884ff06a13aNeal Nguyen    /**
85440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Synchronously waits for a file to increase in size (such as to monitor that a download is
85540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * progressing).
85640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
85740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param file The file whose size to track.
85840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @throws Exception if timed out while waiting for the file to grow in size.
85940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
86040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void waitForFileToGrow(File file) throws Exception {
86140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int currentWaitTime = 0;
86240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
86340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // File may not even exist yet, so wait until it does (or we timeout)
86440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        while (!file.exists()) {
86540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            Log.i(LOG_TAG, "Waiting for file to exist...");
86640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
86740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be created.");
86840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
86940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
87040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Get original file size...
87140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        long originalSize = file.length();
87240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
87340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        while (file.length() <= originalSize) {
87440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            Log.i(LOG_TAG, "Waiting for file to be written to...");
87540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
87640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be written to.");
87740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
87840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
87940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
88040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
88140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to remove all downloads that are registered with the DL Manager.
88240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
88340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Note: This gives us a clean slate b/c it includes downloads that are pending, running,
88440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * paused, or have completed.
88540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
88640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void removeAllCurrentDownloads() {
88740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Log.i(LOG_TAG, "Removing all current registered downloads...");
88882e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        ArrayList<Long> ids = new ArrayList<Long>();
88940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Cursor cursor = mDownloadManager.query(new Query());
89040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        try {
89140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            if (cursor.moveToFirst()) {
89240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                do {
89340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    int index = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
89440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    long downloadId = cursor.getLong(index);
89582e891b3259350a92b55969a6380ca1240ee0829Vasu Nori                    ids.add(downloadId);
89640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                } while (cursor.moveToNext());
89740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
89840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        } finally {
89940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            cursor.close();
90040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
90182e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        // delete all ids
90282e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        for (long id : ids) {
90382e891b3259350a92b55969a6380ca1240ee0829Vasu Nori            mDownloadManager.remove(id);
90482e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        }
90582e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        // make sure the database is empty
90682e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        cursor = mDownloadManager.query(new Query());
90782e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        try {
90882e891b3259350a92b55969a6380ca1240ee0829Vasu Nori            assertEquals(0, cursor.getCount());
90982e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        } finally {
91082e891b3259350a92b55969a6380ca1240ee0829Vasu Nori            cursor.close();
91182e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        }
91240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
91340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
91440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
91540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to perform a standard enqueue of data to the mock server.
91682e891b3259350a92b55969a6380ca1240ee0829Vasu Nori     * download is performed to the downloads cache dir (NOT systemcache dir)
91740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
91840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param body The body to return in the response from the server
91940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
92040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected long doStandardEnqueue(byte[] body) throws Exception {
92182e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
92282e891b3259350a92b55969a6380ca1240ee0829Vasu Nori    }
92382e891b3259350a92b55969a6380ca1240ee0829Vasu Nori
92482e891b3259350a92b55969a6380ca1240ee0829Vasu Nori    protected long enqueueDownloadRequest(byte[] body, int location) throws Exception {
92540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Prepare the mock server with a standard response
926b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey        mServer.enqueue(buildResponse(HTTP_OK, body));
92782e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        return doEnqueue(location);
92840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
92940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
93040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
93140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to perform a standard enqueue of data to the mock server.
93240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
93340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param body The body to return in the response from the server, contained in the file
93440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
93540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected long doStandardEnqueue(File body) throws Exception {
93682e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
93782e891b3259350a92b55969a6380ca1240ee0829Vasu Nori    }
93882e891b3259350a92b55969a6380ca1240ee0829Vasu Nori
93982e891b3259350a92b55969a6380ca1240ee0829Vasu Nori    protected long enqueueDownloadRequest(File body, int location) throws Exception {
94040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        // Prepare the mock server with a standard response
941b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey        mServer.enqueue(buildResponse(HTTP_OK, body));
94282e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        return doEnqueue(location);
94340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
94440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
94540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
94640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to do the additional steps (setting title and Uri of default filename) when
94740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * doing a standard enqueue request to the server.
94840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
94940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected long doCommonStandardEnqueue() throws Exception {
95082e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        return doEnqueue(DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
95182e891b3259350a92b55969a6380ca1240ee0829Vasu Nori    }
95282e891b3259350a92b55969a6380ca1240ee0829Vasu Nori
95382e891b3259350a92b55969a6380ca1240ee0829Vasu Nori    private long doEnqueue(int location) throws Exception {
95440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Uri uri = getServerUri(DEFAULT_FILENAME);
95582e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        Request request = new Request(uri).setTitle(DEFAULT_FILENAME);
95682e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        if (location == DOWNLOAD_TO_SYSTEM_CACHE) {
95782e891b3259350a92b55969a6380ca1240ee0829Vasu Nori            request.setDestinationToSystemCache();
95882e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        }
95940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
96082e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        return mDownloadManager.enqueue(request);
96140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
96240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
96340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
96440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to verify an int value in a Cursor
96540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
96640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param cursor The cursor containing the query results
96740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param columnName The name of the column to query
96840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param expected The expected int value
96940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
97040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void verifyInt(Cursor cursor, String columnName, int expected) {
97140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int index = cursor.getColumnIndex(columnName);
97240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int actual = cursor.getInt(index);
97340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(expected, actual);
97440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
97540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
97640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
97740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Helper to verify a String value in a Cursor
97840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
97940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param cursor The cursor containing the query results
98040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param columnName The name of the column to query
98140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param expected The expected String value
98240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
98340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected void verifyString(Cursor cursor, String columnName, String expected) {
98440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int index = cursor.getColumnIndex(columnName);
98540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        String actual = cursor.getString(index);
98640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Log.i(LOG_TAG, ": " + actual);
98740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        assertEquals(expected, actual);
98840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
98940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
99040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    /**
99140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * Performs a query based on ID and returns a Cursor for the query.
99240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     *
99340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @param id The id of the download in DL Manager; pass -1 to query all downloads
99440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     * @return A cursor for the query results
99540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen     */
99640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    protected Cursor getCursor(long id) throws Exception {
99740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Query query = new Query();
99840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        if (id != -1) {
99940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            query.setFilterById(id);
100040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
100140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
100240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        Cursor cursor = mDownloadManager.query(query);
100340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        int currentWaitTime = 0;
100440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
100540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        try {
100640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            while (!cursor.moveToFirst()) {
100740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                Thread.sleep(DEFAULT_WAIT_POLL_TIME);
100840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                currentWaitTime += DEFAULT_WAIT_POLL_TIME;
100940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                if (currentWaitTime > DEFAULT_MAX_WAIT_TIME) {
101040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                    fail("timed out waiting for a non-null query result");
101140ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                }
101240ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen                cursor.requery();
101340ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            }
101440ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        } catch (Exception e) {
101540ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            cursor.close();
101640ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen            throw e;
101740ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        }
101840ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen        return cursor;
101940ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen    }
102040ef0f49ea9fa7c39eb0018fdb4df4b73a11a77dNeal Nguyen
102182e891b3259350a92b55969a6380ca1240ee0829Vasu Nori    /**
102282e891b3259350a92b55969a6380ca1240ee0829Vasu Nori     * Helper that does the actual basic download verification.
102382e891b3259350a92b55969a6380ca1240ee0829Vasu Nori     */
102482e891b3259350a92b55969a6380ca1240ee0829Vasu Nori    protected long doBasicDownload(byte[] blobData, int location) throws Exception {
102582e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        long dlRequest = enqueueDownloadRequest(blobData, location);
102682e891b3259350a92b55969a6380ca1240ee0829Vasu Nori
102782e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        // wait for the download to complete
102882e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        waitForDownloadOrTimeout(dlRequest);
102982e891b3259350a92b55969a6380ca1240ee0829Vasu Nori
103082e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        assertEquals(1, mReceiver.numDownloadsCompleted());
103182e891b3259350a92b55969a6380ca1240ee0829Vasu Nori        return dlRequest;
103282e891b3259350a92b55969a6380ca1240ee0829Vasu Nori    }
1033b14ad8cc8cb0ed774072b077694b21fd0a6f33beJeff Sharkey}
1034