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> ¬ify)
: 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", ¬ify));
- 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() {