Merge "The HTTP/1.1 Host request header must include port if it's not the default (80)."
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 22befa8..ca5ff24 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1845,7 +1845,7 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_USB_ANLG_HEADSET_PLUG =
-            "android.intent.action.DOCK_HEADSET_PLUG";
+            "android.intent.action.USB_ANLG_HEADSET_PLUG";
 
     /**
      * Broadcast Action: An analog audio speaker/headset plugged in or unplugged.
@@ -1860,7 +1860,7 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_USB_DGTL_HEADSET_PLUG =
-            "android.intent.action.HDMI_AUDIO_PLUG";
+            "android.intent.action.USB_DGTL_HEADSET_PLUG";
 
     /**
      * Broadcast Action: A HMDI cable was plugged or unplugged
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index b1e38ee..f2857fa 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -36,6 +36,9 @@
             android:description="@string/permdesc_testDenied" />
 
     <uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
+    <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
+    <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED" />
+    <uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
diff --git a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
index 8df37ad..1bb2a57 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
@@ -16,8 +16,12 @@
 
 package android.app;
 
+import coretestutils.http.MockResponse;
+import coretestutils.http.MockWebServer;
+
 import android.app.DownloadManager.Query;
 import android.app.DownloadManager.Request;
+import android.app.DownloadManagerBaseTest.DataType;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -27,11 +31,10 @@
 import android.net.NetworkInfo;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
-import android.os.Bundle;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
-import android.os.SystemClock;
 import android.os.ParcelFileDescriptor.AutoCloseInputStream;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.test.InstrumentationTestCase;
 import android.util.Log;
@@ -43,19 +46,12 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.net.URL;
-import java.util.concurrent.TimeoutException;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
 import java.util.Random;
 import java.util.Set;
-import java.util.Vector;
-
-import junit.framework.AssertionFailedError;
-
-import coretestutils.http.MockResponse;
-import coretestutils.http.MockWebServer;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Base class for Instrumented tests for the Download Manager.
@@ -67,7 +63,7 @@
     protected String mFileType = "text/plain";
     protected Context mContext = null;
     protected MultipleDownloadsCompletedReceiver mReceiver = null;
-    protected static final int DEFAULT_FILE_SIZE = 130 * 1024;  // 130kb
+    protected static final int DEFAULT_FILE_SIZE = 10 * 1024;  // 10kb
     protected static final int FILE_BLOCK_READ_SIZE = 1024 * 1024;
 
     protected static final String LOG_TAG = "android.net.DownloadManagerBaseTest";
@@ -85,6 +81,9 @@
     protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 5 * 60 * 1000; // 5 minutes
     protected static final int MAX_WAIT_FOR_LARGE_DOWNLOAD_TIME = 15 * 60 * 1000; // 15 minutes
 
+    protected static final int DOWNLOAD_TO_SYSTEM_CACHE = 1;
+    protected static final int DOWNLOAD_TO_DOWNLOAD_CACHE_DIR = 2;
+
     // Just a few popular file types used to return from a download
     protected enum DownloadFileType {
         PLAINTEXT,
@@ -888,30 +887,46 @@
      */
     protected void removeAllCurrentDownloads() {
         Log.i(LOG_TAG, "Removing all current registered downloads...");
+        ArrayList<Long> ids = new ArrayList<Long>();
         Cursor cursor = mDownloadManager.query(new Query());
         try {
             if (cursor.moveToFirst()) {
                 do {
                     int index = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
                     long downloadId = cursor.getLong(index);
-
-                    mDownloadManager.remove(downloadId);
+                    ids.add(downloadId);
                 } while (cursor.moveToNext());
             }
         } finally {
             cursor.close();
         }
+        // delete all ids
+        for (long id : ids) {
+            mDownloadManager.remove(id);
+        }
+        // make sure the database is empty
+        cursor = mDownloadManager.query(new Query());
+        try {
+            assertEquals(0, cursor.getCount());
+        } finally {
+            cursor.close();
+        }
     }
 
     /**
      * Helper to perform a standard enqueue of data to the mock server.
+     * download is performed to the downloads cache dir (NOT systemcache dir)
      *
      * @param body The body to return in the response from the server
      */
     protected long doStandardEnqueue(byte[] body) throws Exception {
+        return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+    }
+
+    protected long enqueueDownloadRequest(byte[] body, int location) throws Exception {
         // Prepare the mock server with a standard response
         enqueueResponse(HTTP_OK, body);
-        return doCommonStandardEnqueue();
+        return doEnqueue(location);
     }
 
     /**
@@ -920,9 +935,13 @@
      * @param body The body to return in the response from the server, contained in the file
      */
     protected long doStandardEnqueue(File body) throws Exception {
+        return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+    }
+
+    protected long enqueueDownloadRequest(File body, int location) throws Exception {
         // Prepare the mock server with a standard response
         enqueueResponse(HTTP_OK, body);
-        return doCommonStandardEnqueue();
+        return doEnqueue(location);
     }
 
     /**
@@ -930,13 +949,17 @@
      * doing a standard enqueue request to the server.
      */
     protected long doCommonStandardEnqueue() throws Exception {
-        Uri uri = getServerUri(DEFAULT_FILENAME);
-        Request request = new Request(uri);
-        request.setTitle(DEFAULT_FILENAME);
+        return doEnqueue(DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+    }
 
-        long dlRequest = mDownloadManager.enqueue(request);
-        Log.i(LOG_TAG, "request ID: " + dlRequest);
-        return dlRequest;
+    private long doEnqueue(int location) throws Exception {
+        Uri uri = getServerUri(DEFAULT_FILENAME);
+        Request request = new Request(uri).setTitle(DEFAULT_FILENAME);
+        if (location == DOWNLOAD_TO_SYSTEM_CACHE) {
+            request.setDestinationToSystemCache();
+        }
+
+        return mDownloadManager.enqueue(request);
     }
 
     /**
@@ -997,4 +1020,16 @@
         return cursor;
     }
 
+    /**
+     * Helper that does the actual basic download verification.
+     */
+    protected long doBasicDownload(byte[] blobData, int location) throws Exception {
+        long dlRequest = enqueueDownloadRequest(blobData, location);
+
+        // wait for the download to complete
+        waitForDownloadOrTimeout(dlRequest);
+
+        assertEquals(1, mReceiver.numDownloadsCompleted());
+        return dlRequest;
+    }
 }
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
similarity index 80%
rename from core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
rename to core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
index 4f79108..06f3ba4 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
@@ -16,51 +16,35 @@
 
 package android.app;
 
+import coretestutils.http.MockResponse;
+
 import android.app.DownloadManager.Query;
 import android.app.DownloadManager.Request;
 import android.app.DownloadManagerBaseTest.DataType;
-import android.app.DownloadManagerBaseTest.MultipleDownloadsCompletedReceiver;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.database.Cursor;
 import android.net.Uri;
-import android.net.wifi.WifiManager;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.os.StatFs;
-import android.os.SystemClock;
-import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 
 import java.io.File;
 import java.io.FileOutputStream;
-import java.io.FileWriter;
 import java.io.IOException;
-import java.net.URL;
 import java.util.Iterator;
 import java.util.Random;
 import java.util.Set;
 
-import junit.framework.AssertionFailedError;
-
-import coretestutils.http.MockResponse;
-import coretestutils.http.MockWebServer;
-
 /**
  * Integration tests of the DownloadManager API.
  */
-public class DownloadManagerIntegrationTest extends DownloadManagerBaseTest {
-
-    private final static String LOG_TAG = "android.net.DownloadManagerIntegrationTest";
-    private final static String PROHIBITED_DIRECTORY =
-            Environment.getRootDirectory().getAbsolutePath();
+public class DownloadManagerFunctionalTest extends DownloadManagerBaseTest {
+    private static final String TAG = "DownloadManagerFunctionalTest";
     private final static String CACHE_DIR =
             Environment.getDownloadCacheDirectory().getAbsolutePath();
+    private final static String PROHIBITED_DIRECTORY =
+            Environment.getRootDirectory().getAbsolutePath();
 
     /**
      * {@inheritDoc}
@@ -89,26 +73,12 @@
     }
 
     /**
-     * Helper that does the actual basic download verification.
-     */
-    protected long doBasicDownload(byte[] blobData) throws Exception {
-        long dlRequest = doStandardEnqueue(blobData);
-
-        // wait for the download to complete
-        waitForDownloadOrTimeout(dlRequest);
-
-        assertEquals(1, mReceiver.numDownloadsCompleted());
-        return dlRequest;
-    }
-
-    /**
      * Verifies a particular error code was received from a download
      *
      * @param uri The uri to enqueue to the DownloadManager
      * @param error The error code expected
-     * @throws an Exception if the test fails
+     * @throws Exception if the test fails
      */
-    @LargeTest
     public void doErrorTest(Uri uri, int error) throws Exception {
         Request request = new Request(uri);
         request.setTitle(DEFAULT_FILENAME);
@@ -128,66 +98,57 @@
      * Test a basic download of a binary file 500k in size.
      */
     @LargeTest
-    public void testBasicBinaryDownload() throws Exception {
-        int fileSize = 500 * 1024;  // 500k
+    public void testBinaryDownloadToSystemCache() throws Exception {
+        int fileSize = 1024;
         byte[] blobData = generateData(fileSize, DataType.BINARY);
 
-        long dlRequest = doBasicDownload(blobData);
-        verifyAndCleanupSingleFileDownload(dlRequest, blobData);
+        long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
+        verifyDownload(dlRequest, blobData);
+        mDownloadManager.remove(dlRequest);
     }
 
     /**
      * Tests the basic downloading of a text file 300000 bytes in size.
      */
     @LargeTest
-    public void testBasicTextDownload() throws Exception {
-        int fileSize = 300000;
+    public void testTextDownloadToSystemCache() throws Exception {
+        int fileSize = 1024;
         byte[] blobData = generateData(fileSize, DataType.TEXT);
 
-        long dlRequest = doBasicDownload(blobData);
-        verifyAndCleanupSingleFileDownload(dlRequest, blobData);
+        long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
+        verifyDownload(dlRequest, blobData);
+        mDownloadManager.remove(dlRequest);
     }
-
+    
     /**
-     * Tests when the server drops the connection after all headers (but before any data send).
+     * Helper to verify a standard single-file download from the mock server, and clean up after
+     * verification
+     *
+     * Note that this also calls the Download manager's remove, which cleans up the file from cache.
+     *
+     * @param requestId The id of the download to remove
+     * @param fileData The data to verify the file contains
      */
-    @LargeTest
-    public void testDropConnection_headers() throws Exception {
-        byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
-
-        MockResponse response = enqueueResponse(HTTP_OK, blobData);
-        response.setCloseConnectionAfterHeader("content-length");
-        long dlRequest = doCommonStandardEnqueue();
-
-        // Download will never complete when header is dropped
-        boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest, DEFAULT_WAIT_POLL_TIME,
-                DEFAULT_MAX_WAIT_TIME);
-
-        assertFalse(success);
-    }
-
-    /**
-     * Tests that we get an error code when the server drops the connection during a download.
-     */
-    @LargeTest
-    public void testServerDropConnection_body() throws Exception {
-        byte[] blobData = generateData(25000, DataType.TEXT);  // file size = 25000 bytes
-
-        MockResponse response = enqueueResponse(HTTP_OK, blobData);
-        response.setCloseConnectionAfterXBytes(15382);
-        long dlRequest = doCommonStandardEnqueue();
-        waitForDownloadOrTimeout(dlRequest);
-
-        Cursor cursor = getCursor(dlRequest);
+    private void verifyDownload(long requestId, byte[] fileData)
+            throws Exception {
+        int fileSize = fileData.length;
+        ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(requestId);
+        Cursor cursor = mDownloadManager.query(new Query().setFilterById(requestId));
         try {
-            verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
-            verifyInt(cursor, DownloadManager.COLUMN_REASON,
-                    DownloadManager.ERROR_CANNOT_RESUME);
+            assertEquals(1, cursor.getCount());
+            assertTrue(cursor.moveToFirst());
+
+            mServer.checkForExceptions();
+
+            verifyFileSize(pfd, fileSize);
+            verifyFileContents(pfd, fileData);
+            int colIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
+            String fileName = cursor.getString(colIndex);
+            assertTrue(fileName.startsWith(CACHE_DIR));
         } finally {
+            pfd.close();
             cursor.close();
         }
-        // Even tho the server drops the connection, we should still get a completed notification
-        assertEquals(1, mReceiver.numDownloadsCompleted());
     }
 
     /**
@@ -198,7 +159,7 @@
         // need to be sure all current downloads have stopped first
         removeAllCurrentDownloads();
         int NUM_FILES = 10;
-        int MAX_FILE_SIZE = 500 * 1024; // 500 kb
+        int MAX_FILE_SIZE = 10 * 1024; // 10 kb
 
         Random r = new LoggingRng();
         for (int i=0; i<NUM_FILES; ++i) {
@@ -213,7 +174,6 @@
             enqueueResponse(HTTP_OK, blobData);
 
             long requestID = mDownloadManager.enqueue(request);
-            Log.i(LOG_TAG, "request: " + i + " -- requestID: " + requestID);
         }
 
         waitForDownloadsOrTimeout(WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
@@ -236,8 +196,6 @@
 
             assertEquals(NUM_FILES, mReceiver.numDownloadsCompleted());
         } finally {
-            Log.i(LOG_TAG, "All download IDs: " + mReceiver.getDownloadIds().toString());
-            Log.i(LOG_TAG, "Total downloads completed: " + mReceiver.getDownloadIds().size());
             cursor.close();
         }
     }
@@ -258,7 +216,6 @@
             Request request = new Request(uri);
 
             Uri localUri = Uri.fromFile(existentFile);
-            Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
             request.setDestinationUri(localUri);
 
             long dlRequest = mDownloadManager.enqueue(request);
@@ -268,9 +225,7 @@
             Cursor cursor = getCursor(dlRequest);
 
             try {
-                verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
-                verifyInt(cursor, DownloadManager.COLUMN_REASON,
-                        DownloadManager.ERROR_FILE_ALREADY_EXISTS);
+                verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_SUCCESSFUL);
             } finally {
                 cursor.close();
             }
@@ -299,7 +254,6 @@
             Request request = new Request(uri);
 
             Uri localUri = Uri.fromFile(downloadedFile);
-            Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
             request.setDestinationUri(localUri);
 
             long dlRequest = mDownloadManager.enqueue(request);
@@ -331,7 +285,6 @@
             Request request = new Request(uri);
 
             Uri localUri = Uri.fromFile(downloadedFile);
-            Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
             request.setDestinationUri(localUri);
 
             try {
@@ -347,144 +300,6 @@
     }
 
     /**
-     * Tests that a download set for Wifi does not progress while Wifi is disabled, but resumes
-     * once Wifi is re-enabled.
-     */
-    @LargeTest
-    public void testDownloadNoWifi() throws Exception {
-        long timeout = 60 * 1000; // wait only 60 seconds before giving up
-        int fileSize = 140 * 1024;  // 140k
-        byte[] blobData = generateData(fileSize, DataType.TEXT);
-
-        setWiFiStateOn(false);
-        enqueueResponse(HTTP_OK, blobData);
-
-        try {
-            Uri uri = getServerUri(DEFAULT_FILENAME);
-            Request request = new Request(uri);
-            request.setAllowedNetworkTypes(Request.NETWORK_WIFI);
-
-            long dlRequest = mDownloadManager.enqueue(request);
-
-            // wait for the download to complete
-            boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest,
-                    WAIT_FOR_DOWNLOAD_POLL_TIME, timeout);
-            assertFalse("Download proceeded without Wifi connection!", success);
-
-            setWiFiStateOn(true);
-            waitForDownloadOrTimeout(dlRequest);
-
-            assertEquals(1, mReceiver.numDownloadsCompleted());
-        } finally {
-            setWiFiStateOn(true);
-        }
-    }
-
-    /**
-     * Tests downloading a file to cache when there isn't enough space in the cache to hold the
-     * entire file.
-     */
-    @LargeTest
-    public void testDownloadToCache_whenFull() throws Exception {
-        int DOWNLOAD_FILE_SIZE = 500000;
-
-        StatFs fs = new StatFs(CACHE_DIR);
-        Log.i(LOG_TAG, "getAvailableBlocks: " + fs.getAvailableBlocks());
-        Log.i(LOG_TAG, "getBlockSize: " + fs.getBlockSize());
-
-        int blockSize = fs.getBlockSize();
-        int availableBlocks = fs.getAvailableBlocks();
-        int availableBytes = blockSize * availableBlocks;
-        File outFile = null;
-
-        try {
-            // fill cache to ensure we don't have enough space - take half the size of the
-            // download size, and leave that much freespace left on the cache partition
-            if (DOWNLOAD_FILE_SIZE <= availableBytes) {
-                int writeSizeBytes = availableBytes - (DOWNLOAD_FILE_SIZE / 2);
-
-                int writeSizeBlocks = writeSizeBytes / blockSize;
-                int remainderSizeBlocks = availableBlocks - writeSizeBlocks;
-
-                FileOutputStream fo = null;
-                try {
-                    outFile = File.createTempFile("DM_TEST", null, new File(CACHE_DIR));
-                    Log.v(LOG_TAG, "writing " + writeSizeBlocks + " blocks to file "
-                            + outFile.getAbsolutePath());
-
-                    fo = new FileOutputStream(outFile);
-
-                    byte[] buffer = new byte[blockSize];
-                    while (fs.getAvailableBlocks() >= remainderSizeBlocks) {
-                        fo.write(buffer);
-                        fs.restat(CACHE_DIR);
-                    }
-                } catch (IOException e) {
-                    Log.e(LOG_TAG, "error filling file: ", e);
-                    throw e;
-                } finally {
-                    if (fo != null) {
-                        fo.close();
-                    }
-                }
-            }
-
-            Log.i(LOG_TAG, "Done creating filler file.");
-            assertTrue(DOWNLOAD_FILE_SIZE > (fs.getAvailableBlocks() * blockSize));
-            byte[] blobData = generateData(DOWNLOAD_FILE_SIZE, DataType.TEXT);
-            long dlRequest = doBasicDownload(blobData);
-            verifyAndCleanupSingleFileDownload(dlRequest, blobData);
-
-        } finally {
-            if (outFile != null) {
-                outFile.delete();
-            }
-        }
-    }
-
-    /**
-     * Tests that files are not deleted when DOWNLOAD_CACHE_NON_PURGEABLE is set, even if we've
-     * run out of space.
-     */
-    @LargeTest
-    public void testDownloadCacheNonPurgeable() throws Exception {
-        int fileSize = 10000000;
-        byte[] blobData = generateData(fileSize, DataType.BINARY);
-        long dlRequest = -1;
-
-        // Fill up the cache partition until there's not enough room for another download.
-        // Note that we need to initiate a download first, then check for the available space. This
-        // is b/c there could be some files that are still left in the cache that can (and will be)
-        // cleared out, but not until DM gets a request for a download and reclaims that
-        // space first.
-        boolean spaceAvailable = true;
-        while (spaceAvailable) {
-            dlRequest = doStandardEnqueue(blobData);
-            waitForDownloadOrTimeout(dlRequest);
-
-            // Check if we've filled up the cache yet
-            StatFs fs = new StatFs(CACHE_DIR);
-            Log.i(LOG_TAG, "getAvailableBlocks: " + fs.getAvailableBlocks());
-            Log.i(LOG_TAG, "getBlockSize: " + fs.getBlockSize());
-            int availableBytes = fs.getBlockSize() * fs.getAvailableBlocks();
-            spaceAvailable = (availableBytes > fileSize) ? true : false;
-        }
-
-        // Now add one more download (should not fit in the space left over)
-        dlRequest = doStandardEnqueue(blobData);
-        waitForDownloadOrTimeout(dlRequest);
-
-        // For the last download we should have failed b/c there is not enough space left in cache
-        Cursor cursor = getCursor(dlRequest);
-        try {
-            verifyInt(cursor, DownloadManager.COLUMN_REASON,
-                    DownloadManager.ERROR_INSUFFICIENT_SPACE);
-        } finally {
-            cursor.close();
-        }
-    }
-
-    /**
      * Tests that we get the correct download ID from the download notification.
      */
     @LargeTest
@@ -545,10 +360,10 @@
      */
     @LargeTest
     public void testRemoveDownload() throws Exception {
-        int fileSize = 100 * 1024;  // 100k
+        int fileSize = 1024;
         byte[] blobData = generateData(fileSize, DataType.BINARY);
 
-        long dlRequest = doBasicDownload(blobData);
+        long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
         Cursor cursor = mDownloadManager.query(new Query().setFilterById(dlRequest));
         try {
             assertEquals("The count of downloads with this ID is not 1!", 1, cursor.getCount());
@@ -565,7 +380,7 @@
      */
     @LargeTest
     public void testSetTitle() throws Exception {
-        int fileSize = 50 * 1024;  // 50k
+        int fileSize = 1024;
         byte[] blobData = generateData(fileSize, DataType.BINARY);
         MockResponse response = enqueueResponse(HTTP_OK, blobData);
 
@@ -587,4 +402,179 @@
             cursor.close();
         }
     }
+
+    /**
+     * Tests that a download set for Wifi does not progress while Wifi is disabled, but resumes
+     * once Wifi is re-enabled.
+     */
+    @LargeTest
+    public void testDownloadNoWifi() throws Exception {
+        long timeout = 60 * 1000; // wait only 60 seconds before giving up
+        int fileSize = 1024;  // 140k
+        byte[] blobData = generateData(fileSize, DataType.TEXT);
+
+        setWiFiStateOn(false);
+        enqueueResponse(HTTP_OK, blobData);
+
+        try {
+            Uri uri = getServerUri(DEFAULT_FILENAME);
+            Request request = new Request(uri);
+            request.setAllowedNetworkTypes(Request.NETWORK_WIFI);
+
+            long dlRequest = mDownloadManager.enqueue(request);
+
+            // wait for the download to complete
+            boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest,
+                    WAIT_FOR_DOWNLOAD_POLL_TIME, timeout);
+            assertFalse("Download proceeded without Wifi connection!", success);
+
+            setWiFiStateOn(true);
+            waitForDownloadOrTimeout(dlRequest);
+
+            assertEquals(1, mReceiver.numDownloadsCompleted());
+        } finally {
+            setWiFiStateOn(true);
+        }
+    }
+
+    /**
+     * Tests when the server drops the connection after all headers (but before any data send).
+     */
+    @LargeTest
+    public void testDropConnection_headers() throws Exception {
+        byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
+
+        MockResponse response = enqueueResponse(HTTP_OK, blobData);
+        response.setCloseConnectionAfterHeader("content-length");
+        long dlRequest = doCommonStandardEnqueue();
+
+        // Download will never complete when header is dropped
+        boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest, DEFAULT_WAIT_POLL_TIME,
+                DEFAULT_MAX_WAIT_TIME);
+
+        assertFalse(success);
+    }
+
+    /**
+     * Tests that we get an error code when the server drops the connection during a download.
+     */
+    @LargeTest
+    public void testServerDropConnection_body() throws Exception {
+        byte[] blobData = generateData(25000, DataType.TEXT);  // file size = 25000 bytes
+
+        MockResponse response = enqueueResponse(HTTP_OK, blobData);
+        response.setCloseConnectionAfterXBytes(15382);
+        long dlRequest = doCommonStandardEnqueue();
+        waitForDownloadOrTimeout(dlRequest);
+
+        Cursor cursor = getCursor(dlRequest);
+        try {
+            verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
+            verifyInt(cursor, DownloadManager.COLUMN_REASON,
+                    DownloadManager.ERROR_CANNOT_RESUME);
+        } finally {
+            cursor.close();
+        }
+        // Even tho the server drops the connection, we should still get a completed notification
+        assertEquals(1, mReceiver.numDownloadsCompleted());
+    }
+
+    /**
+     * Tests downloading a file to system cache when there isn't enough space in the system cache 
+     * to hold the entire file. DownloadManager deletes enough files to make space for the
+     * new download.
+     */
+    @LargeTest
+    public void testDownloadToCacheWithAlmostFullCache() throws Exception {
+        int DOWNLOAD_FILE_SIZE = 1024 * 1024; // 1MB
+
+        StatFs fs = new StatFs(CACHE_DIR);
+        int blockSize = fs.getBlockSize();
+        int availableBlocks = fs.getAvailableBlocks();
+        int availableBytes = blockSize * availableBlocks;
+        Log.i(TAG, "INITIAL stage, available space in /cache: " + availableBytes);
+        File outFile = File.createTempFile("DM_TEST", null, new File(CACHE_DIR));
+        byte[] buffer = new byte[blockSize];
+
+        try {
+            // fill cache to ensure we don't have enough space - take half the size of the
+            // download size, and leave that much freespace left on the cache partition
+            if (DOWNLOAD_FILE_SIZE <= availableBytes) {
+                int writeSizeBytes = availableBytes - (DOWNLOAD_FILE_SIZE / 2);
+
+                int writeSizeBlocks = writeSizeBytes / blockSize;
+                int remainderSizeBlocks = availableBlocks - writeSizeBlocks;
+
+                FileOutputStream fo = null;
+                try {
+                    fo = new FileOutputStream(outFile);
+                    while (fs.getAvailableBlocks() >= remainderSizeBlocks) {
+                        fo.write(buffer);
+                        fs.restat(CACHE_DIR);
+                    }
+                } catch (IOException e) {
+                    Log.e(LOG_TAG, "error filling file: ", e);
+                    throw e;
+                } finally {
+                    if (fo != null) {
+                        fo.close();
+                    }
+                }
+            }
+
+            // /cache should now be almost full. 
+            long spaceAvailable = fs.getAvailableBlocks() * blockSize;
+            Log.i(TAG, "BEFORE download, available space in /cache: " + spaceAvailable);
+            assertTrue(DOWNLOAD_FILE_SIZE > spaceAvailable);
+
+            // try to download 1MB file into /cache - and it should succeed
+            byte[] blobData = generateData(DOWNLOAD_FILE_SIZE, DataType.TEXT);
+            long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
+            verifyAndCleanupSingleFileDownload(dlRequest, blobData);
+        } finally {
+            if (outFile != null) {
+                outFile.delete();
+            }
+        }
+    }
+
+    /**
+     * Tests that files are not deleted when DOWNLOAD_CACHE_NON_PURGEABLE is set, even if we've
+     * run out of space.
+     */
+    @LargeTest
+    public void testDownloadToCacheNonPurgeableWithFullCache() throws Exception {
+        int fileSize = 1024 * 1024; // 1MB
+        byte[] blobData = generateData(fileSize, DataType.BINARY);
+        long dlRequest = -1;
+
+        // Fill up the cache partition with DOWNLOAD_CACHE_NON_PURGEABLE downloads
+        // until 500KB is left.
+        boolean spaceAvailable = true;
+        while (spaceAvailable) {
+            // since this tests package has android.permission.DOWNLOAD_CACHE_NON_PURGEABLE
+            // permission, downloads are automatically set to be DOWNLOAD_CACHE_NON_PURGEABLE
+            dlRequest = enqueueDownloadRequest(blobData, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+            waitForDownloadOrTimeout(dlRequest);
+
+            // Check if we've filled up the cache yet
+            StatFs fs = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
+            int availableBytes = fs.getBlockSize() * fs.getAvailableBlocks();
+            Log.i(TAG, "available space in /cache: " + availableBytes);
+            spaceAvailable = (availableBytes > fileSize) ? true : false;
+        }
+
+        // Now add one more 1MB download (should not fit in the space left over)
+        dlRequest = enqueueDownloadRequest(blobData, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+        waitForDownloadOrTimeout(dlRequest);
+
+        // For the last download we should have failed b/c there is not enough space left in cache
+        Cursor cursor = getCursor(dlRequest);
+        try {
+            verifyInt(cursor, DownloadManager.COLUMN_REASON,
+                    DownloadManager.ERROR_INSUFFICIENT_SPACE);
+        } finally {
+            cursor.close();
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
index e0b28d0..18b279f 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
@@ -16,19 +16,21 @@
 
 package android.app;
 
-import java.io.File;
-import java.util.Random;
 
 import android.app.DownloadManager.Query;
 import android.app.DownloadManager.Request;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
+import java.io.File;
+import java.util.Random;
+
 
 public class DownloadManagerStressTest extends DownloadManagerBaseTest {
-    private static String LOG_TAG = "android.net.DownloadManagerStressTest";
 
     /**
      * {@inheritDoc}
@@ -42,8 +44,25 @@
     }
 
     /**
-     * Attempts to downloading thousands of files simultaneously
+     * {@inheritDoc}
      */
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        setWiFiStateOn(true);
+        removeAllCurrentDownloads();
+
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+    }
+
+    /**
+     * Attempts to downloading thousands of files simultaneously
+     * don't run this test - downloadmanager needs to allow only a few simultaneous downloads.
+     */
+    @Suppress
     public void testDownloadThousands() throws Exception {
         int NUM_FILES = 1500;
         int MAX_FILE_SIZE = 3000;
@@ -106,6 +125,7 @@
     /**
      * Tests trying to download a large file (50M bytes).
      */
+    @LargeTest
     public void testDownloadLargeFile() throws Exception {
         long fileSize = 50000000L;  // note: kept relatively small to not exceed /cache dir size
         File largeFile = createFileOnSD(null, fileSize, DataType.TEXT, null);
@@ -129,31 +149,4 @@
             largeFile.delete();
         }
     }
-
-    /**
-     * Tests trying to download a large file (~600M bytes) when there's not enough space in cache
-     */
-    public void testInsufficientSpace() throws Exception {
-        // @TODO: Rework this to fill up cache partition with a dynamically calculated size
-        long fileSize = 600000000L;
-        File largeFile = createFileOnSD(null, fileSize, DataType.TEXT, null);
-
-        Cursor cursor = null;
-        try {
-            long dlRequest = doStandardEnqueue(largeFile);
-
-             // wait for the download to complete
-            waitForDownloadOrTimeout(dlRequest);
-
-            cursor = getCursor(dlRequest);
-            verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
-            verifyInt(cursor, DownloadManager.COLUMN_REASON,
-                    DownloadManager.ERROR_INSUFFICIENT_SPACE);
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-            largeFile.delete();
-        }
-    }
 }
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
index 7206f4a..a419068 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
@@ -18,8 +18,7 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-				   ../../../coretests/src/android/app/DownloadManagerBaseTest.java
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib
 LOCAL_SDK_VERSION := current
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerBaseTest.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerBaseTest.java
new file mode 100644
index 0000000..acd2a18
--- /dev/null
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerBaseTest.java
@@ -0,0 +1,816 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.frameworks.downloadmanagertests;
+
+import android.app.DownloadManager;
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.os.ParcelFileDescriptor.AutoCloseInputStream;
+import android.provider.Settings;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.concurrent.TimeoutException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.Vector;
+
+import junit.framework.AssertionFailedError;
+
+import coretestutils.http.MockResponse;
+import coretestutils.http.MockWebServer;
+
+/**
+ * Base class for Instrumented tests for the Download Manager.
+ */
+public class DownloadManagerBaseTest extends InstrumentationTestCase {
+
+    protected DownloadManager mDownloadManager = null;
+    protected MockWebServer mServer = null;
+    protected String mFileType = "text/plain";
+    protected Context mContext = null;
+    protected MultipleDownloadsCompletedReceiver mReceiver = null;
+    protected static final int DEFAULT_FILE_SIZE = 10 * 1024;  // 10kb
+    protected static final int FILE_BLOCK_READ_SIZE = 1024 * 1024;
+
+    protected static final String LOG_TAG = "android.net.DownloadManagerBaseTest";
+    protected static final int HTTP_OK = 200;
+    protected static final int HTTP_REDIRECT = 307;
+    protected static final int HTTP_PARTIAL_CONTENT = 206;
+    protected static final int HTTP_NOT_FOUND = 404;
+    protected static final int HTTP_SERVICE_UNAVAILABLE = 503;
+    protected String DEFAULT_FILENAME = "somefile.txt";
+
+    protected static final int DEFAULT_MAX_WAIT_TIME = 2 * 60 * 1000;  // 2 minutes
+    protected static final int DEFAULT_WAIT_POLL_TIME = 5 * 1000;  // 5 seconds
+
+    protected static final int WAIT_FOR_DOWNLOAD_POLL_TIME = 1 * 1000;  // 1 second
+    protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 5 * 60 * 1000; // 5 minutes
+    protected static final int MAX_WAIT_FOR_LARGE_DOWNLOAD_TIME = 15 * 60 * 1000; // 15 minutes
+
+    protected static final int DOWNLOAD_TO_SYSTEM_CACHE = 1;
+    protected static final int DOWNLOAD_TO_DOWNLOAD_CACHE_DIR = 2;
+
+    // Just a few popular file types used to return from a download
+    protected enum DownloadFileType {
+        PLAINTEXT,
+        APK,
+        GIF,
+        GARBAGE,
+        UNRECOGNIZED,
+        ZIP
+    }
+
+    protected enum DataType {
+        TEXT,
+        BINARY
+    }
+
+    public static class LoggingRng extends Random {
+
+        /**
+         * Constructor
+         *
+         * Creates RNG with self-generated seed value.
+         */
+        public LoggingRng() {
+            this(SystemClock.uptimeMillis());
+        }
+
+        /**
+         * Constructor
+         *
+         * Creats RNG with given initial seed value
+
+         * @param seed The initial seed value
+         */
+        public LoggingRng(long seed) {
+            super(seed);
+            Log.i(LOG_TAG, "Seeding RNG with value: " + seed);
+        }
+    }
+
+    public static class MultipleDownloadsCompletedReceiver extends BroadcastReceiver {
+        private volatile int mNumDownloadsCompleted = 0;
+        private Set<Long> downloadIds = Collections.synchronizedSet(new HashSet<Long>());
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equalsIgnoreCase(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
+                synchronized(this) {
+                    long id = intent.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
+                    Log.i(LOG_TAG, "Received Notification for download: " + id);
+                    if (!downloadIds.contains(id)) {
+                        ++mNumDownloadsCompleted;
+                        Log.i(LOG_TAG, "MultipleDownloadsCompletedReceiver got intent: " +
+                                intent.getAction() + " --> total count: " + mNumDownloadsCompleted);
+                        downloadIds.add(id);
+
+                        DownloadManager dm = (DownloadManager)context.getSystemService(
+                                Context.DOWNLOAD_SERVICE);
+
+                        Cursor cursor = dm.query(new Query().setFilterById(id));
+                        try {
+                            if (cursor.moveToFirst()) {
+                                int status = cursor.getInt(cursor.getColumnIndex(
+                                        DownloadManager.COLUMN_STATUS));
+                                Log.i(LOG_TAG, "Download status is: " + status);
+                            } else {
+                                fail("No status found for completed download!");
+                            }
+                        } finally {
+                            cursor.close();
+                        }
+                    } else {
+                        Log.i(LOG_TAG, "Notification for id: " + id + " has already been made.");
+                    }
+                }
+            }
+        }
+
+        /**
+         * Gets the number of times the {@link #onReceive} callback has been called for the
+         * {@link DownloadManager.ACTION_DOWNLOAD_COMPLETED} action, indicating the number of
+         * downloads completed thus far.
+         *
+         * @return the number of downloads completed so far.
+         */
+        public int numDownloadsCompleted() {
+            return mNumDownloadsCompleted;
+        }
+
+        /**
+         * Gets the list of download IDs.
+         * @return A Set<Long> with the ids of the completed downloads.
+         */
+        public Set<Long> getDownloadIds() {
+            synchronized(this) {
+                Set<Long> returnIds = new HashSet<Long>(downloadIds);
+                return returnIds;
+            }
+        }
+
+    }
+
+    public static class WiFiChangedReceiver extends BroadcastReceiver {
+        private Context mContext = null;
+
+        /**
+         * Constructor
+         *
+         * Sets the current state of WiFi.
+         *
+         * @param context The current app {@link Context}.
+         */
+        public WiFiChangedReceiver(Context context) {
+            mContext = context;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equalsIgnoreCase(ConnectivityManager.CONNECTIVITY_ACTION)) {
+                Log.i(LOG_TAG, "ConnectivityManager state change: " + intent.getAction());
+                synchronized (this) {
+                    this.notify();
+                }
+            }
+        }
+
+        /**
+         * Gets the current state of WiFi.
+         *
+         * @return Returns true if WiFi is on, false otherwise.
+         */
+        public boolean getWiFiIsOn() {
+            ConnectivityManager connManager = (ConnectivityManager)mContext.getSystemService(
+                    Context.CONNECTIVITY_SERVICE);
+            NetworkInfo info = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+            Log.i(LOG_TAG, "WiFi Connection state is currently: " + info.isConnected());
+            return info.isConnected();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        mContext = getInstrumentation().getContext();
+        mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+        mServer = new MockWebServer();
+        mReceiver = registerNewMultipleDownloadsReceiver();
+        // Note: callers overriding this should call mServer.play() with the desired port #
+    }
+
+    /**
+     * Helper to enqueue a response from the MockWebServer.
+     *
+     * @param status The HTTP status code to return for this response
+     * @param body The body to return in this response
+     * @return Returns the mock web server response that was queued (which can be modified)
+     */
+    private MockResponse enqueueResponse(int status, byte[] body) {
+        return doEnqueueResponse(status).setBody(body);
+
+    }
+
+    /**
+     * Helper to enqueue a response from the MockWebServer.
+     *
+     * @param status The HTTP status code to return for this response
+     * @param bodyFile The body to return in this response
+     * @return Returns the mock web server response that was queued (which can be modified)
+     */
+    private MockResponse enqueueResponse(int status, File bodyFile) {
+        return doEnqueueResponse(status).setBody(bodyFile);
+    }
+
+    /**
+     * Helper for enqueue'ing a response from the MockWebServer.
+     *
+     * @param status The HTTP status code to return for this response
+     * @return Returns the mock web server response that was queued (which can be modified)
+     */
+    private MockResponse doEnqueueResponse(int status) {
+        MockResponse response = new MockResponse().setResponseCode(status);
+        response.addHeader("Content-type", mFileType);
+        mServer.enqueue(response);
+        return response;
+    }
+
+    /**
+     * Helper to generate a random blob of bytes using a given RNG.
+     *
+     * @param size The size of the data to generate
+     * @param type The type of data to generate: currently, one of {@link DataType.TEXT} or
+     *         {@link DataType.BINARY}.
+     * @param rng (optional) The RNG to use; pass null to use
+     * @return The random data that is generated.
+     */
+    private byte[] generateData(int size, DataType type, Random rng) {
+        int min = Byte.MIN_VALUE;
+        int max = Byte.MAX_VALUE;
+
+        // Only use chars in the HTTP ASCII printable character range for Text
+        if (type == DataType.TEXT) {
+            min = 32;
+            max = 126;
+        }
+        byte[] result = new byte[size];
+        Log.i(LOG_TAG, "Generating data of size: " + size);
+
+        if (rng == null) {
+            rng = new LoggingRng();
+        }
+
+        for (int i = 0; i < size; ++i) {
+            result[i] = (byte) (min + rng.nextInt(max - min + 1));
+        }
+        return result;
+    }
+
+    /**
+     * Helper to verify the size of a file.
+     *
+     * @param pfd The input file to compare the size of
+     * @param size The expected size of the file
+     */
+    protected void verifyFileSize(ParcelFileDescriptor pfd, long size) {
+        assertEquals(pfd.getStatSize(), size);
+    }
+
+    /**
+     * Helper to verify the contents of a downloaded file versus a byte[].
+     *
+     * @param actual The file of whose contents to verify
+     * @param expected The data we expect to find in the aforementioned file
+     * @throws IOException if there was a problem reading from the file
+     */
+    private void verifyFileContents(ParcelFileDescriptor actual, byte[] expected)
+            throws IOException {
+        AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(actual);
+        long fileSize = actual.getStatSize();
+
+        assertTrue(fileSize <= Integer.MAX_VALUE);
+        assertEquals(expected.length, fileSize);
+
+        byte[] actualData = new byte[expected.length];
+        assertEquals(input.read(actualData), fileSize);
+        compareByteArrays(actualData, expected);
+    }
+
+    /**
+     * Helper to compare 2 byte arrays.
+     *
+     * @param actual The array whose data we want to verify
+     * @param expected The array of data we expect to see
+     */
+    private void compareByteArrays(byte[] actual, byte[] expected) {
+        assertEquals(actual.length, expected.length);
+        int length = actual.length;
+        for (int i = 0; i < length; ++i) {
+            // assert has a bit of overhead, so only do the assert when the values are not the same
+            if (actual[i] != expected[i]) {
+                fail("Byte arrays are not equal.");
+            }
+        }
+    }
+
+    /**
+     * Gets the MIME content string for a given type
+     *
+     * @param type The MIME type to return
+     * @return the String representation of that MIME content type
+     */
+    protected String getMimeMapping(DownloadFileType type) {
+        switch (type) {
+            case APK:
+                return "application/vnd.android.package-archive";
+            case GIF:
+                return "image/gif";
+            case ZIP:
+                return "application/x-zip-compressed";
+            case GARBAGE:
+                return "zip\\pidy/doo/da";
+            case UNRECOGNIZED:
+                return "application/new.undefined.type.of.app";
+        }
+        return "text/plain";
+    }
+
+    /**
+     * Gets the Uri that should be used to access the mock server
+     *
+     * @param filename The name of the file to try to retrieve from the mock server
+     * @return the Uri to use for access the file on the mock server
+     */
+    private Uri getServerUri(String filename) throws Exception {
+        URL url = mServer.getUrl("/" + filename);
+        return Uri.parse(url.toString());
+    }
+
+    /**
+     * Helper to create and register a new MultipleDownloadCompletedReciever
+     *
+     * This is used to track many simultaneous downloads by keeping count of all the downloads
+     * that have completed.
+     *
+     * @return A new receiver that records and can be queried on how many downloads have completed.
+     */
+    protected MultipleDownloadsCompletedReceiver registerNewMultipleDownloadsReceiver() {
+        MultipleDownloadsCompletedReceiver receiver = new MultipleDownloadsCompletedReceiver();
+        mContext.registerReceiver(receiver, new IntentFilter(
+                DownloadManager.ACTION_DOWNLOAD_COMPLETE));
+        return receiver;
+    }
+
+    /**
+     * Enables or disables WiFi.
+     *
+     * Note: Needs the following permissions:
+     *  android.permission.ACCESS_WIFI_STATE
+     *  android.permission.CHANGE_WIFI_STATE
+     * @param enable true if it should be enabled, false if it should be disabled
+     */
+    protected void setWiFiStateOn(boolean enable) throws Exception {
+        Log.i(LOG_TAG, "Setting WiFi State to: " + enable);
+        WifiManager manager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
+
+        manager.setWifiEnabled(enable);
+
+        String timeoutMessage = "Timed out waiting for Wifi to be "
+            + (enable ? "enabled!" : "disabled!");
+
+        WiFiChangedReceiver receiver = new WiFiChangedReceiver(mContext);
+        mContext.registerReceiver(receiver, new IntentFilter(
+                ConnectivityManager.CONNECTIVITY_ACTION));
+
+        synchronized (receiver) {
+            long timeoutTime = SystemClock.elapsedRealtime() + DEFAULT_MAX_WAIT_TIME;
+            boolean timedOut = false;
+
+            while (receiver.getWiFiIsOn() != enable && !timedOut) {
+                try {
+                    receiver.wait(DEFAULT_WAIT_POLL_TIME);
+
+                    if (SystemClock.elapsedRealtime() > timeoutTime) {
+                        timedOut = true;
+                    }
+                }
+                catch (InterruptedException e) {
+                    // ignore InterruptedExceptions
+                }
+            }
+            if (timedOut) {
+                fail(timeoutMessage);
+            }
+        }
+        assertEquals(enable, receiver.getWiFiIsOn());
+    }
+
+    /**
+     * Helper to enables or disables airplane mode. If successful, it also broadcasts an intent
+     * indicating that the mode has changed.
+     *
+     * Note: Needs the following permission:
+     *  android.permission.WRITE_SETTINGS
+     * @param enable true if airplane mode should be ON, false if it should be OFF
+     */
+    protected void setAirplaneModeOn(boolean enable) throws Exception {
+        int state = enable ? 1 : 0;
+
+        // Change the system setting
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
+                state);
+
+        String timeoutMessage = "Timed out waiting for airplane mode to be " +
+                (enable ? "enabled!" : "disabled!");
+
+        // wait for airplane mode to change state
+        int currentWaitTime = 0;
+        while (Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.AIRPLANE_MODE_ON, -1) != state) {
+            timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME, DEFAULT_MAX_WAIT_TIME,
+                    timeoutMessage);
+        }
+
+        // Post the intent
+        Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        intent.putExtra("state", true);
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Helper to wait for a particular download to finish, or else a timeout to occur
+     *
+     * Does not wait for a receiver notification of the download.
+     *
+     * @param id The download id to query on (wait for)
+     */
+    private void waitForDownloadOrTimeout_skipNotification(long id) throws TimeoutException,
+            InterruptedException {
+        waitForDownloadOrTimeout(id, WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
+    }
+
+    /**
+     * Helper to wait for a particular download to finish, or else a timeout to occur
+     *
+     * Also guarantees a notification has been posted for the download.
+     *
+     * @param id The download id to query on (wait for)
+     */
+    protected void waitForDownloadOrTimeout(long id) throws TimeoutException,
+            InterruptedException {
+        waitForDownloadOrTimeout_skipNotification(id);
+        waitForReceiverNotifications(1);
+    }
+
+    /**
+     * Helper to wait for a particular download to finish, or else a timeout to occur
+     *
+     * Also guarantees a notification has been posted for the download.
+     *
+     * @param id The download id to query on (wait for)
+     * @param poll The amount of time to wait
+     * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
+     */
+    protected void waitForDownloadOrTimeout(long id, long poll, long timeoutMillis)
+            throws TimeoutException, InterruptedException {
+        doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
+        waitForReceiverNotifications(1);
+    }
+
+    /**
+     * Helper to wait for all downloads to finish, or else a specified timeout to occur
+     *
+     * Makes no guaranee that notifications have been posted for all downloads.
+     *
+     * @param poll The amount of time to wait
+     * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
+     */
+    protected void waitForDownloadsOrTimeout(long poll, long timeoutMillis) throws TimeoutException,
+            InterruptedException {
+        doWaitForDownloadsOrTimeout(new Query(), poll, timeoutMillis);
+    }
+
+    /**
+     * Helper to wait for all downloads to finish, or else a timeout to occur, but does not throw
+     *
+     * Also guarantees a notification has been posted for the download.
+     *
+     * @param id The id of the download to query against
+     * @param poll The amount of time to wait
+     * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
+     * @return true if download completed successfully (didn't timeout), false otherwise
+     */
+    private boolean waitForDownloadOrTimeoutNoThrow(long id, long poll, long timeoutMillis) {
+        try {
+            doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
+            waitForReceiverNotifications(1);
+        } catch (TimeoutException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Helper function to synchronously wait, or timeout if the maximum threshold has been exceeded.
+     *
+     * @param currentTotalWaitTime The total time waited so far
+     * @param poll The amount of time to wait
+     * @param maxTimeoutMillis The total wait time threshold; if we've waited more than this long,
+     *          we timeout and fail
+     * @param timedOutMessage The message to display in the failure message if we timeout
+     * @return The new total amount of time we've waited so far
+     * @throws TimeoutException if timed out waiting for SD card to mount
+     */
+    private int timeoutWait(int currentTotalWaitTime, long poll, long maxTimeoutMillis,
+            String timedOutMessage) throws TimeoutException {
+        long now = SystemClock.elapsedRealtime();
+        long end = now + poll;
+
+        // if we get InterruptedException's, ignore them and just keep sleeping
+        while (now < end) {
+            try {
+                Thread.sleep(end - now);
+            } catch (InterruptedException e) {
+                // ignore interrupted exceptions
+            }
+            now = SystemClock.elapsedRealtime();
+        }
+
+        currentTotalWaitTime += poll;
+        if (currentTotalWaitTime > maxTimeoutMillis) {
+            throw new TimeoutException(timedOutMessage);
+        }
+        return currentTotalWaitTime;
+    }
+
+    /**
+     * Helper to wait for all downloads to finish, or else a timeout to occur
+     *
+     * @param query The query to pass to the download manager
+     * @param poll The poll time to wait between checks
+     * @param timeoutMillis The max amount of time (in ms) to wait for the download(s) to complete
+     */
+    private void doWaitForDownloadsOrTimeout(Query query, long poll, long timeoutMillis)
+            throws TimeoutException {
+        int currentWaitTime = 0;
+        while (true) {
+            query.setFilterByStatus(DownloadManager.STATUS_PENDING | DownloadManager.STATUS_PAUSED
+                    | DownloadManager.STATUS_RUNNING);
+            Cursor cursor = mDownloadManager.query(query);
+
+            try {
+                if (cursor.getCount() == 0) {
+                    Log.i(LOG_TAG, "All downloads should be done...");
+                    break;
+                }
+                currentWaitTime = timeoutWait(currentWaitTime, poll, timeoutMillis,
+                        "Timed out waiting for all downloads to finish");
+            } finally {
+                cursor.close();
+            }
+        }
+    }
+
+    /**
+     * Synchronously waits for external store to be mounted (eg: SD Card).
+     *
+     * @throws InterruptedException if interrupted
+     * @throws Exception if timed out waiting for SD card to mount
+     */
+    protected void waitForExternalStoreMount() throws Exception {
+        String extStorageState = Environment.getExternalStorageState();
+        int currentWaitTime = 0;
+        while (!extStorageState.equals(Environment.MEDIA_MOUNTED)) {
+            Log.i(LOG_TAG, "Waiting for SD card...");
+            currentWaitTime = timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME,
+                    DEFAULT_MAX_WAIT_TIME, "Timed out waiting for SD Card to be ready!");
+            extStorageState = Environment.getExternalStorageState();
+        }
+    }
+
+    /**
+     * Synchronously waits for a download to start.
+     *
+     * @param dlRequest the download request id used by Download Manager to track the download.
+     * @throws Exception if timed out while waiting for SD card to mount
+     */
+    protected void waitForDownloadToStart(long dlRequest) throws Exception {
+        Cursor cursor = getCursor(dlRequest);
+        try {
+            int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
+            int value = cursor.getInt(columnIndex);
+            int currentWaitTime = 0;
+
+            while (value != DownloadManager.STATUS_RUNNING &&
+                    (value != DownloadManager.STATUS_FAILED) &&
+                    (value != DownloadManager.STATUS_SUCCESSFUL)) {
+                Log.i(LOG_TAG, "Waiting for download to start...");
+                currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
+                        MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download to start!");
+                cursor.requery();
+                assertTrue(cursor.moveToFirst());
+                columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
+                value = cursor.getInt(columnIndex);
+            }
+            assertFalse("Download failed immediately after start",
+                    value == DownloadManager.STATUS_FAILED);
+        } finally {
+            cursor.close();
+        }
+    }
+
+    /**
+     * Synchronously waits for our receiver to receive notification for a given number of
+     * downloads.
+     *
+     * @param targetNumber The number of notifications for unique downloads to wait for; pass in
+     *         -1 to not wait for notification.
+     * @throws Exception if timed out while waiting
+     */
+    private void waitForReceiverNotifications(int targetNumber) throws TimeoutException {
+        int count = mReceiver.numDownloadsCompleted();
+        int currentWaitTime = 0;
+
+        while (count < targetNumber) {
+            Log.i(LOG_TAG, "Waiting for notification of downloads...");
+            currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
+                    MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download notifications!"
+                    + " Received " + count + "notifications.");
+            count = mReceiver.numDownloadsCompleted();
+        }
+    }
+
+    /**
+     * Synchronously waits for a file to increase in size (such as to monitor that a download is
+     * progressing).
+     *
+     * @param file The file whose size to track.
+     * @throws Exception if timed out while waiting for the file to grow in size.
+     */
+    protected void waitForFileToGrow(File file) throws Exception {
+        int currentWaitTime = 0;
+
+        // File may not even exist yet, so wait until it does (or we timeout)
+        while (!file.exists()) {
+            Log.i(LOG_TAG, "Waiting for file to exist...");
+            currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
+                    MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be created.");
+        }
+
+        // Get original file size...
+        long originalSize = file.length();
+
+        while (file.length() <= originalSize) {
+            Log.i(LOG_TAG, "Waiting for file to be written to...");
+            currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
+                    MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be written to.");
+        }
+    }
+
+    /**
+     * Helper to remove all downloads that are registered with the DL Manager.
+     *
+     * Note: This gives us a clean slate b/c it includes downloads that are pending, running,
+     * paused, or have completed.
+     */
+    protected void removeAllCurrentDownloads() {
+        Log.i(LOG_TAG, "Removing all current registered downloads...");
+        Cursor cursor = mDownloadManager.query(new Query());
+        try {
+            if (cursor.moveToFirst()) {
+                do {
+                    int index = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
+                    long downloadId = cursor.getLong(index);
+
+                    mDownloadManager.remove(downloadId);
+                } while (cursor.moveToNext());
+            }
+        } finally {
+            cursor.close();
+        }
+    }
+
+    /**
+     * Helper to perform a standard enqueue of data to the mock server.
+     * download is performed to the downloads cache dir (NOT systemcache dir)
+     *
+     * @param body The body to return in the response from the server
+     */
+    private long doStandardEnqueue(byte[] body) throws Exception {
+        // Prepare the mock server with a standard response
+        enqueueResponse(HTTP_OK, body);
+        return doCommonStandardEnqueue();
+    }
+
+    /**
+     * Helper to perform a standard enqueue of data to the mock server.
+     *
+     * @param body The body to return in the response from the server, contained in the file
+     */
+    private long doStandardEnqueue(File body) throws Exception {
+        // Prepare the mock server with a standard response
+        enqueueResponse(HTTP_OK, body);
+        return doCommonStandardEnqueue();
+    }
+
+    /**
+     * Helper to do the additional steps (setting title and Uri of default filename) when
+     * doing a standard enqueue request to the server.
+     */
+    private long doCommonStandardEnqueue() throws Exception {
+        Uri uri = getServerUri(DEFAULT_FILENAME);
+        Request request = new Request(uri).setTitle(DEFAULT_FILENAME);
+        return mDownloadManager.enqueue(request);
+    }
+
+    /**
+     * Helper to verify an int value in a Cursor
+     *
+     * @param cursor The cursor containing the query results
+     * @param columnName The name of the column to query
+     * @param expected The expected int value
+     */
+    private void verifyInt(Cursor cursor, String columnName, int expected) {
+        int index = cursor.getColumnIndex(columnName);
+        int actual = cursor.getInt(index);
+        assertEquals(expected, actual);
+    }
+
+    /**
+     * Performs a query based on ID and returns a Cursor for the query.
+     *
+     * @param id The id of the download in DL Manager; pass -1 to query all downloads
+     * @return A cursor for the query results
+     */
+    protected Cursor getCursor(long id) throws Exception {
+        Query query = new Query();
+        if (id != -1) {
+            query.setFilterById(id);
+        }
+
+        Cursor cursor = mDownloadManager.query(query);
+        int currentWaitTime = 0;
+
+        try {
+            while (!cursor.moveToFirst()) {
+                Thread.sleep(DEFAULT_WAIT_POLL_TIME);
+                currentWaitTime += DEFAULT_WAIT_POLL_TIME;
+                if (currentWaitTime > DEFAULT_MAX_WAIT_TIME) {
+                    fail("timed out waiting for a non-null query result");
+                }
+                cursor.requery();
+            }
+        } catch (Exception e) {
+            cursor.close();
+            throw e;
+        }
+        return cursor;
+    }
+}
\ No newline at end of file
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java
index e1d7b4c..ba5ee2c 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java
@@ -18,7 +18,6 @@
 import android.app.DownloadManager;
 import android.app.DownloadManager.Query;
 import android.app.DownloadManager.Request;
-import android.app.DownloadManagerBaseTest;
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 940470d..b3815c4 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -14,11 +14,12 @@
 
 struct ACodec : public AHierarchicalStateMachine {
     enum {
-        kWhatFillThisBuffer    = 'fill',
-        kWhatDrainThisBuffer   = 'drai',
-        kWhatEOS               = 'eos ',
-        kWhatShutdownCompleted = 'scom',
-        kWhatFlushCompleted    = 'fcom',
+        kWhatFillThisBuffer      = 'fill',
+        kWhatDrainThisBuffer     = 'drai',
+        kWhatEOS                 = 'eos ',
+        kWhatShutdownCompleted   = 'scom',
+        kWhatFlushCompleted      = 'fcom',
+        kWhatOutputFormatChanged = 'outC',
     };
 
     ACodec();
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 941f6b9..91ec60c 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -64,6 +64,9 @@
 
     void post(int64_t delayUs = 0);
 
+    // Performs a deep-copy of "this", contained messages are in turn "dup'ed".
+    // Warning: RefBase items, i.e. "objects" are _not_ copied but only have
+    // their refcount incremented.
     sp<AMessage> dup() const;
 
     AString debugString(int32_t indent = 0) const;
diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
index 89a5e69..9738e33 100644
--- a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
+++ b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
@@ -166,6 +166,9 @@
     sp<MediaSource> mDecoder;
     sp<AMessage> mNotify;
     bool mEOS;
+    bool mSentFormat;
+
+    void sendFormatChange();
 
     DISALLOW_EVIL_CONSTRUCTORS(WrapperReader);
 };
@@ -174,7 +177,8 @@
         const sp<MediaSource> &decoder, const sp<AMessage> &notify)
     : mDecoder(decoder),
       mNotify(notify),
-      mEOS(false) {
+      mEOS(false),
+      mSentFormat(false) {
 }
 
 DecoderWrapper::WrapperReader::~WrapperReader() {
@@ -215,12 +219,17 @@
             MediaBuffer *src;
             status_t err = mDecoder->read(&src, &options);
 
-            sp<AMessage> notify = mNotify->dup();
-
-            sp<AMessage> realNotify;
-            CHECK(notify->findMessage("real-notify", &realNotify));
-
             if (err == OK) {
+                if (!mSentFormat) {
+                    sendFormatChange();
+                    mSentFormat = true;
+                }
+
+                sp<AMessage> notify = mNotify->dup();
+
+                sp<AMessage> realNotify;
+                CHECK(notify->findMessage("real-notify", &realNotify));
+
                 realNotify->setInt32("what", ACodec::kWhatDrainThisBuffer);
 
                 sp<ABuffer> dst = new ABuffer(src->range_length());
@@ -236,12 +245,23 @@
                 dst->meta()->setInt64("timeUs", timeUs);
 
                 realNotify->setObject("buffer", dst);
+
+                notify->post();
+            } else if (err == INFO_FORMAT_CHANGED) {
+                sendFormatChange();
+
+                readMore(false /* flush */);
             } else {
+                sp<AMessage> notify = mNotify->dup();
+
+                sp<AMessage> realNotify;
+                CHECK(notify->findMessage("real-notify", &realNotify));
+
                 realNotify->setInt32("what", ACodec::kWhatEOS);
                 mEOS = true;
-            }
 
-            notify->post();
+                notify->post();
+            }
             break;
         }
 
@@ -251,6 +271,46 @@
     }
 }
 
+void DecoderWrapper::WrapperReader::sendFormatChange() {
+    sp<AMessage> notify = mNotify->dup();
+
+    sp<AMessage> realNotify;
+    CHECK(notify->findMessage("real-notify", &realNotify));
+
+    realNotify->setInt32("what", ACodec::kWhatOutputFormatChanged);
+
+    sp<MetaData> meta = mDecoder->getFormat();
+
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    realNotify->setString("mime", mime);
+
+    if (!strncasecmp("audio/", mime, 6)) {
+        int32_t numChannels;
+        CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+
+        int32_t sampleRate;
+        CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+        realNotify->setInt32("channel-count", numChannels);
+        realNotify->setInt32("sample-rate", sampleRate);
+    } else {
+        CHECK(!strncasecmp("video/", mime, 6));
+
+        int32_t width, height;
+        CHECK(meta->findInt32(kKeyWidth, &width));
+        CHECK(meta->findInt32(kKeyHeight, &height));
+
+        realNotify->setInt32("width", width);
+        realNotify->setInt32("height", height);
+    }
+
+    notify->post();
+
+    mSentFormat = true;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 DecoderWrapper::DecoderWrapper()
@@ -327,24 +387,33 @@
 
         case kWhatFillBufferDone:
         {
-            CHECK_GT(mNumPendingDecodes, 0);
-            --mNumPendingDecodes;
-
-            if (mFlushing) {
-                completeFlushIfPossible();
-                break;
-            }
-
             sp<AMessage> notify;
             CHECK(msg->findMessage("real-notify", &notify));
 
-            sp<AMessage> reply =
-                new AMessage(kWhatOutputBufferDrained, id());
+            int32_t what;
+            CHECK(notify->findInt32("what", &what));
 
-            notify->setMessage("reply", reply);
+            if (what == ACodec::kWhatDrainThisBuffer) {
+                CHECK_GT(mNumPendingDecodes, 0);
+                --mNumPendingDecodes;
+
+                sp<AMessage> reply =
+                    new AMessage(kWhatOutputBufferDrained, id());
+
+                notify->setMessage("reply", reply);
+
+                ++mNumOutstandingOutputBuffers;
+            } else if (what == ACodec::kWhatEOS) {
+                CHECK_GT(mNumPendingDecodes, 0);
+                --mNumPendingDecodes;
+
+                if (mFlushing) {
+                    completeFlushIfPossible();
+                    break;
+                }
+            }
+
             notify->post();
-
-            ++mNumOutstandingOutputBuffers;
             break;
         }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 403029a..e99c24a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -198,6 +198,21 @@
                     mFlushingAudio = NONE;
                     mFlushingVideo = NONE;
                 }
+            } else if (what == ACodec::kWhatOutputFormatChanged) {
+                CHECK(audio);
+
+                int32_t numChannels;
+                CHECK(codecRequest->findInt32("channel-count", &numChannels));
+
+                int32_t sampleRate;
+                CHECK(codecRequest->findInt32("sample-rate", &sampleRate));
+
+                LOGI("Audio output format changed to %d Hz, %d channels",
+                     sampleRate, numChannels);
+
+                mAudioSink->close();
+                CHECK_EQ(mAudioSink->open(sampleRate, numChannels), (status_t)OK);
+                mAudioSink->start();
             } else {
                 CHECK_EQ((int)what, (int)ACodec::kWhatDrainThisBuffer);
 
@@ -365,18 +380,6 @@
     const sp<MetaData> &meta = source->getFormat();
     (*decoder)->configure(meta);
 
-    if (audio) {
-        int32_t sampleRate;
-        int32_t channelCount;
-        CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
-        CHECK(meta->findInt32(kKeyChannelCount, &channelCount));
-
-        channelCount = 2;  // XXX
-
-        CHECK_EQ(mAudioSink->open(sampleRate, channelCount), (status_t)OK);
-        mAudioSink->start();
-    }
-
     return OK;
 }
 
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 7613d04..d1525cf 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -33,8 +33,6 @@
 #include "UDPPusher.h"
 
 #include <binder/IPCThreadState.h>
-#include <binder/MemoryDealer.h>
-#include <media/IStreamSource.h>
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/AudioPlayer.h>
@@ -161,245 +159,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-struct QueueDataSource;
-
-struct QueueListener : public BnStreamListener {
-    QueueListener(QueueDataSource *owner)
-        : mOwner(owner) {
-    }
-
-    void clearOwner();
-
-    virtual void queueBuffer(size_t index, size_t size);
-
-    virtual void issueCommand(
-            Command cmd, bool synchronous, const sp<AMessage> &msg);
-
-private:
-    Mutex mLock;
-
-    QueueDataSource *mOwner;
-
-    DISALLOW_EVIL_CONSTRUCTORS(QueueListener);
-};
-
-struct QueueDataSource : public DataSource {
-    QueueDataSource(const sp<IStreamSource> &source);
-
-    virtual status_t initCheck() const;
-
-    virtual ssize_t readAt(off64_t offset, void *data, size_t size);
-
-    virtual void queueBuffer(size_t index, size_t size);
-
-    virtual void issueCommand(
-            IStreamListener::Command cmd,
-            bool synchronous,
-            const sp<AMessage> &msg);
-
-protected:
-    virtual ~QueueDataSource();
-
-private:
-    enum {
-        kNumBuffers = 16
-    };
-
-    struct QueueEntry {
-        bool mIsCommand;
-
-        IStreamListener::Command mCommand;
-        sp<AMessage> mCommandMessage;
-
-        size_t mIndex;
-        size_t mOffset;
-        size_t mSize;
-    };
-
-    Mutex mLock;
-    Condition mCondition;
-
-    sp<IStreamSource> mSource;
-    sp<QueueListener> mListener;
-    sp<MemoryDealer> mDealer;
-    Vector<sp<IMemory> > mBuffers;
-
-    List<QueueEntry> mQueue;
-
-    off64_t mPosition;
-    bool mEOS;
-
-    DISALLOW_EVIL_CONSTRUCTORS(QueueDataSource);
-};
-
-QueueDataSource::QueueDataSource(const sp<IStreamSource> &source)
-    : mSource(source),
-      mPosition(0),
-      mEOS(false) {
-    mListener = new QueueListener(this);
-    mSource->setListener(mListener);
-
-    static const size_t kBufferSize = (8192 / 188) * 188;
-
-    mDealer = new MemoryDealer(kNumBuffers * kBufferSize);
-    for (size_t i = 0; i < kNumBuffers; ++i) {
-        sp<IMemory> mem = mDealer->allocate(kBufferSize);
-        CHECK(mem != NULL);
-
-        mBuffers.push(mem);
-    }
-    mSource->setBuffers(mBuffers);
-
-    for (size_t i = 0; i < kNumBuffers; ++i) {
-        mSource->onBufferAvailable(i);
-    }
-}
-
-QueueDataSource::~QueueDataSource() {
-    Mutex::Autolock autoLock(mLock);
-
-    mListener->clearOwner();
-}
-
-status_t QueueDataSource::initCheck() const {
-    return OK;
-}
-
-ssize_t QueueDataSource::readAt(off64_t offset, void *data, size_t size) {
-    if (offset != mPosition) {
-        return -EPIPE;
-    }
-
-    Mutex::Autolock autoLock(mLock);
-
-    if (mEOS) {
-        return ERROR_END_OF_STREAM;
-    }
-
-    size_t sizeDone = 0;
-
-    while (sizeDone < size) {
-        while (mQueue.empty()) {
-            mCondition.wait(mLock);
-        }
-
-        QueueEntry &entry = *mQueue.begin();
-
-        if (entry.mIsCommand) {
-            switch (entry.mCommand) {
-                case IStreamListener::EOS:
-                {
-                    mEOS = true;
-
-                    if (sizeDone > 0) {
-                        offset += sizeDone;
-                        return sizeDone;
-                    } else {
-                        return ERROR_END_OF_STREAM;
-                    }
-                    break;
-                }
-
-                case IStreamListener::DISCONTINUITY:
-                {
-                    CHECK_EQ(size, 188u);
-                    CHECK_EQ(sizeDone, 0u);
-
-                    memset(data, 0, size);
-                    sizeDone = size;
-                    break;
-                }
-
-                default:
-                    break;
-            }
-
-            mQueue.erase(mQueue.begin());
-            continue;
-        }
-
-        size_t copy = size - sizeDone;
-        if (copy > entry.mSize) {
-            copy = entry.mSize;
-        }
-
-        memcpy((uint8_t *)data + sizeDone,
-               (const uint8_t *)mBuffers.itemAt(entry.mIndex)->pointer()
-                    + entry.mOffset,
-               copy);
-
-        entry.mSize -= copy;
-        entry.mOffset += copy;
-        sizeDone += copy;
-
-        if (entry.mSize == 0) {
-            mSource->onBufferAvailable(entry.mIndex);
-            mQueue.erase(mQueue.begin());
-        }
-    }
-
-    mPosition += sizeDone;
-
-    return sizeDone;
-}
-
-void QueueDataSource::queueBuffer(size_t index, size_t size) {
-    Mutex::Autolock autoLock(mLock);
-
-    CHECK_LT(index, mBuffers.size());
-    CHECK_LE(size, mBuffers.itemAt(index)->size());
-
-    QueueEntry entry;
-    entry.mIsCommand = false;
-    entry.mIndex = index;
-    entry.mSize = size;
-    entry.mOffset = 0;
-
-    mQueue.push_back(entry);
-    mCondition.signal();
-}
-
-void QueueDataSource::issueCommand(
-        IStreamListener::Command cmd,
-        bool synchronous,
-        const sp<AMessage> &msg) {
-    Mutex::Autolock autoLock(mLock);
-
-    CHECK(!synchronous);
-
-    QueueEntry entry;
-    entry.mIsCommand = true;
-    entry.mCommand = cmd;
-    entry.mCommandMessage = msg;
-    mQueue.push_back(entry);
-
-    mCondition.signal();
-}
-
-void QueueListener::clearOwner() {
-    Mutex::Autolock autoLock(mLock);
-    mOwner = NULL;
-}
-
-void QueueListener::queueBuffer(size_t index, size_t size) {
-    Mutex::Autolock autoLock(mLock);
-    if (mOwner == NULL) {
-        return;
-    }
-    mOwner->queueBuffer(index, size);
-}
-
-void QueueListener::issueCommand(
-        Command cmd, bool synchronous, const sp<AMessage> &msg) {
-    Mutex::Autolock autoLock(mLock);
-    if (mOwner == NULL) {
-        return;
-    }
-    mOwner->issueCommand(cmd, synchronous, msg);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
 AwesomePlayer::AwesomePlayer()
     : mQueueStarted(false),
       mTimeSource(NULL),
@@ -511,14 +270,7 @@
 }
 
 status_t AwesomePlayer::setDataSource(const sp<IStreamSource> &source) {
-    Mutex::Autolock autoLock(mLock);
-
-    reset_l();
-
-    sp<DataSource> dataSource = new QueueDataSource(source);
-    sp<MediaExtractor> extractor = new MPEG2TSExtractor(dataSource);
-
-    return setDataSource_l(extractor);
+    return INVALID_OPERATION;
 }
 
 status_t AwesomePlayer::setDataSource_l(
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index d3c7445..dfc9b5a 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -959,6 +959,13 @@
             uint16_t width = U16_AT(&buffer[6 + 18]);
             uint16_t height = U16_AT(&buffer[6 + 20]);
 
+            // The video sample is not stand-compliant if it has invalid dimension.
+            // Use some default width and height value, and
+            // let the decoder figure out the actual width and height (and thus
+            // be prepared for INFO_FOMRAT_CHANGED event).
+            if (width == 0)  width  = 352;
+            if (height == 0) height = 288;
+
             // printf("*** coding='%s' width=%d height=%d\n",
             //        chunk, width, height);
 
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 7da9cb8..0e40acc 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -224,13 +224,22 @@
             }
 
             case kTypeObject:
-            case kTypeMessage:
             {
                 to->u.refValue = from->u.refValue;
                 to->u.refValue->incStrong(msg.get());
                 break;
             }
 
+            case kTypeMessage:
+            {
+                sp<AMessage> copy =
+                    static_cast<AMessage *>(from->u.refValue)->dup();
+
+                to->u.refValue = copy.get();
+                to->u.refValue->incStrong(msg.get());
+                break;
+            }
+
             default:
             {
                 to->u = from->u;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index b8c068e..25662492 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -393,7 +393,6 @@
 
         // set the initial view visibility
         setAreThereNotifications();
-        refreshNotificationTrigger();
 
         // Add the windows
         addPanelWindows();
@@ -505,23 +504,6 @@
         }
     }
 
-    public void refreshNotificationTrigger() {
-        /*
-        if (mNotificationTrigger == null) return;
-
-        int resId;
-        boolean panel = (mNotificationPanel != null && mNotificationPanel.isShowing();
-        if (!mNotificationsOn) {
-            resId = R.drawable.ic_sysbar_noti_dnd;
-        } else if (mNotns.size() > 0) {
-            resId = panel ? R.drawable.ic_sysbar_noti_avail_open : R.drawable.ic_sysbar_noti_avail;
-        } else {
-            resId = panel ? R.drawable.ic_sysbar_noti_none_open : R.drawable.ic_sysbar_noti_none;
-        }
-        //mNotificationTrigger.setImageResource(resId);
-        */
-    }
-
     public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
         if (DEBUG) Slog.d(TAG, "addIcon(" + slot + ") -> " + icon);
     }
@@ -669,6 +651,11 @@
             Slog.d(TAG, "DISABLE_CLOCK: " + (show ? "no" : "yes"));
             showClock(show);
         }
+        if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
+            boolean show = (state & StatusBarManager.DISABLE_SYSTEM_INFO) == 0;
+            Slog.d(TAG, "DISABLE_SYSTEM_INFO: " + (show ? "no" : "yes"));
+            mNotificationTrigger.setVisibility(show ? View.VISIBLE : View.GONE);
+        }
         if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
             if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
                 Slog.d(TAG, "DISABLE_EXPAND: yes");
@@ -820,7 +807,6 @@
             if (!mNotificationsOn) {
                 mNotificationsOn = true;
                 mIconLayout.setVisibility(View.VISIBLE); // TODO: animation
-                refreshNotificationTrigger();
             } else {
                 int msg = !mNotificationPanel.isShowing()
                     ? MSG_OPEN_NOTIFICATION_PANEL
@@ -1068,7 +1054,6 @@
         }
 
         loadNotificationPanel();
-        refreshNotificationTrigger();
     }
 
     private void loadNotificationPanel() {
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index 5b9273d..855af9f 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -1605,10 +1605,8 @@
             break;
 
         case AudioSystem::FORCE_SPEAKER:
-            if (!isInCall() || strategy != STRATEGY_DTMF) {
-                device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
-                if (device) break;
-            }
+            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
+            if (device) break;
 #ifdef WITH_A2DP
             // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
             // A2DP speaker when forcing to speaker output
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index 44c7d72..563f28c 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -178,6 +178,11 @@
                 mStatusBarManager.disable(StatusBarManager.DISABLE_CLOCK);
             }
         },
+        new Test("Disable System Info") {
+            public void run() {
+                mStatusBarManager.disable(StatusBarManager.DISABLE_SYSTEM_INFO);
+            }
+        },
         new Test("Disable everything in 3 sec") {
             public void run() {
                 mHandler.postDelayed(new Runnable() {