Merge "do not merge" into gingerbread
diff --git a/api/current.xml b/api/current.xml
index d7fc03d..1d6feb5 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -76944,11 +76944,11 @@
visibility="public"
>
</field>
-<field name="FOCUS_MODE_CONTINUOUS"
+<field name="FOCUS_MODE_CONTINUOUS_VIDEO"
type="java.lang.String"
transient="false"
volatile="false"
- value=""continuous""
+ value=""continuous-video""
static="true"
final="true"
deprecated="not deprecated"
@@ -84729,6 +84729,39 @@
<parameter name="value" type="short">
</parameter>
</method>
+<field name="ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ALREADY_EXISTS"
type="int"
transient="false"
@@ -84740,6 +84773,50 @@
visibility="public"
>
</field>
+<field name="CONTENT_TYPE_GAME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONTENT_TYPE_MOVIE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONTENT_TYPE_MUSIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONTENT_TYPE_VOICE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="EFFECT_AUXILIARY"
type="java.lang.String"
transient="false"
@@ -84888,6 +84965,39 @@
visibility="public"
>
</field>
+<field name="EXTRA_AUDIO_SESSION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.media.extra.AUDIO_SESSION""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_CONTENT_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.media.extra.CONTENT_TYPE""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_PACKAGE_NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.media.extra.PACKAGE_NAME""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="NATIVE_EVENT_CONTROL_STATUS"
type="int"
transient="false"
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index c424281..b718299 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -36,24 +36,30 @@
static const int32_t kIFramesIntervalSec = 1;
static const int32_t kVideoBitRate = 512 * 1024;
static const int32_t kAudioBitRate = 12200;
-static const int32_t kColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
static const int64_t kDurationUs = 10000000LL; // 10 seconds
#if 1
class DummySource : public MediaSource {
public:
- DummySource(int width, int height)
+ DummySource(int width, int height, int colorFormat)
: mWidth(width),
mHeight(height),
+ mColorFormat(colorFormat),
mSize((width * height * 3) / 2) {
mGroup.add_buffer(new MediaBuffer(mSize));
+
+ // Check the color format to make sure
+ // that the buffer size mSize it set correctly above.
+ CHECK(colorFormat == OMX_COLOR_FormatYUV420SemiPlanar ||
+ colorFormat == OMX_COLOR_FormatYUV420Planar);
}
virtual sp<MetaData> getFormat() {
sp<MetaData> meta = new MetaData;
meta->setInt32(kKeyWidth, mWidth);
meta->setInt32(kKeyHeight, mHeight);
+ meta->setInt32(kKeyColorFormat, mColorFormat);
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
return meta;
@@ -100,6 +106,7 @@
private:
MediaBufferGroup mGroup;
int mWidth, mHeight;
+ int mColorFormat;
size_t mSize;
int64_t mNumFramesOutput;;
@@ -139,20 +146,47 @@
return source;
}
+enum {
+ kYUV420SP = 0,
+ kYUV420P = 1,
+};
+
+// returns -1 if mapping of the given color is unsuccessful
+// returns an omx color enum value otherwise
+static int translateColorToOmxEnumValue(int color) {
+ switch (color) {
+ case kYUV420SP:
+ return OMX_COLOR_FormatYUV420SemiPlanar;
+ case kYUV420P:
+ return OMX_COLOR_FormatYUV420Planar;
+ default:
+ fprintf(stderr, "Unsupported color: %d\n", color);
+ return -1;
+ }
+}
+
int main(int argc, char **argv) {
android::ProcessState::self()->startThreadPool();
DataSource::RegisterDefaultSniffers();
#if 1
- if (argc != 2) {
- fprintf(stderr, "usage: %s filename\n", argv[0]);
+ if (argc != 3) {
+ fprintf(stderr, "usage: %s <filename> <input_color_format>\n", argv[0]);
+ fprintf(stderr, " <input_color_format>: 0 (YUV420SP) or 1 (YUV420P)\n");
return 1;
}
+ int colorFormat = translateColorToOmxEnumValue(atoi(argv[2]));
+ if (colorFormat == -1) {
+ fprintf(stderr, "input color format must be 0 (YUV420SP) or 1 (YUV420P)\n");
+ return 1;
+ }
OMXClient client;
CHECK_EQ(client.connect(), OK);
+ status_t err = OK;
+
#if 0
sp<MediaSource> source = createSource(argv[1]);
@@ -173,7 +207,7 @@
#else
int width = 720;
int height = 480;
- sp<MediaSource> decoder = new DummySource(width, height);
+ sp<MediaSource> decoder = new DummySource(width, height, colorFormat);
#endif
sp<MetaData> enc_meta = new MetaData;
@@ -187,7 +221,7 @@
enc_meta->setInt32(kKeyStride, width);
enc_meta->setInt32(kKeySliceHeight, height);
enc_meta->setInt32(kKeyIFramesInterval, kIFramesIntervalSec);
- enc_meta->setInt32(kKeyColorFormat, kColorFormat);
+ enc_meta->setInt32(kKeyColorFormat, colorFormat);
sp<MediaSource> encoder =
OMXCodec::Create(
@@ -197,14 +231,14 @@
sp<MPEG4Writer> writer = new MPEG4Writer("/sdcard/output.mp4");
writer->addSource(encoder);
writer->setMaxFileDuration(kDurationUs);
- writer->start();
+ CHECK_EQ(OK, writer->start());
while (!writer->reachedEOS()) {
fprintf(stderr, ".");
usleep(100000);
}
- writer->stop();
+ err = writer->stop();
#else
- encoder->start();
+ CHECK_EQ(OK, encoder->start());
MediaBuffer *buffer;
while (encoder->read(&buffer) == OK) {
@@ -222,7 +256,7 @@
buffer = NULL;
}
- encoder->stop();
+ err = encoder->stop();
#endif
printf("$\n");
@@ -247,12 +281,16 @@
buffer = NULL;
}
- source->stop();
+ err = source->stop();
delete source;
source = NULL;
#endif
+ if (err != OK && err != ERROR_END_OF_STREAM) {
+ fprintf(stderr, "record failed: %d\n", err);
+ return 1;
+ }
return 0;
}
#else
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d8b5253..3c7bebf 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -363,7 +363,8 @@
private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
private static final String ONE_COUNT_COLUMN = "%17s %8d";
private static final String TWO_COUNT_COLUMNS = "%17s %8d %17s %8d";
- private static final String DB_INFO_FORMAT = " %8d %8d %10d %s";
+ private static final String TWO_COUNT_COLUMNS_DB = "%20s %8d %20s %8d";
+ private static final String DB_INFO_FORMAT = " %8d %8d %14d %s";
// Formatting for checkin service - update version if row format changes
private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 1;
@@ -806,15 +807,15 @@
// SQLite mem info
pw.println(" ");
pw.println(" SQL");
- printRow(pw, TWO_COUNT_COLUMNS, "heap:", sqliteAllocated, "memoryUsed:",
+ printRow(pw, TWO_COUNT_COLUMNS_DB, "heap:", sqliteAllocated, "MEMORY_USED:",
stats.memoryUsed / 1024);
- printRow(pw, TWO_COUNT_COLUMNS, "pageCacheOverflo:", stats.pageCacheOverflo / 1024,
- "largestMemAlloc:", stats.largestMemAlloc / 1024);
+ printRow(pw, TWO_COUNT_COLUMNS_DB, "PAGECACHE_OVERFLOW:",
+ stats.pageCacheOverflo / 1024, "MALLOC_SIZE:", stats.largestMemAlloc / 1024);
pw.println(" ");
int N = stats.dbStats.size();
if (N > 0) {
pw.println(" DATABASES");
- printRow(pw, " %8s %8s %10s %s", "Pagesize", "Dbsize", "Lookaside", "Dbname");
+ printRow(pw, " %8s %8s %14s %s", "pgsz", "dbsz", "Lookaside(b)", "Dbname");
for (int i = 0; i < N; i++) {
DbStats dbStats = stats.dbStats.get(i);
printRow(pw, DB_INFO_FORMAT, dbStats.pageSize, dbStats.dbSize,
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index 89c3f96..b2a166b 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -134,7 +134,7 @@
public DbStats(String dbName, long pageCount, long pageSize, int lookaside) {
this.dbName = dbName;
- this.pageSize = pageSize;
+ this.pageSize = pageSize / 1024;
dbSize = (pageCount * pageSize) / 1024;
this.lookaside = lookaside;
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 26600f3..0d8228c 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1182,14 +1182,17 @@
public static final String FOCUS_MODE_EDOF = "edof";
/**
- * Continuous auto focus mode. The camera continuously tries to focus.
- * This is ideal for shooting video or shooting photo of moving object.
- * Auto focus starts when the parameter is set. Applications should not
- * call {@link #autoFocus(AutoFocusCallback)} in this mode. To stop
- * continuous focus, applications should change the focus mode to other
- * modes.
+ * Continuous auto focus mode intended for video recording. The camera
+ * continuously tries to focus. This is ideal for shooting video.
+ * Applications still can call {@link
+ * #takePicture(Camera.ShutterCallback, Camera.PictureCallback,
+ * Camera.PictureCallback)} in this mode but the subject may not be in
+ * focus. Auto focus starts when the parameter is set. Applications
+ * should not call {@link #autoFocus(AutoFocusCallback)} in this mode.
+ * To stop continuous focus, applications should change the focus mode
+ * to other modes.
*/
- public static final String FOCUS_MODE_CONTINUOUS = "continuous";
+ public static final String FOCUS_MODE_CONTINUOUS_VIDEO = "continuous-video";
// Indices for focus distance array.
/**
@@ -2023,7 +2026,7 @@
* @see #FOCUS_MODE_MACRO
* @see #FOCUS_MODE_FIXED
* @see #FOCUS_MODE_EDOF
- * @see #FOCUS_MODE_CONTINUOUS
+ * @see #FOCUS_MODE_CONTINUOUS_VIDEO
*/
public String getFocusMode() {
return get(KEY_FOCUS_MODE);
@@ -2225,8 +2228,8 @@
* #autoFocus(AutoFocusCallback)}, {@link #cancelAutoFocus}, or {@link
* #startPreview()}. Applications can call {@link #getParameters()}
* and this method anytime to get the latest focus distances. If the
- * focus mode is FOCUS_MODE_CONTINUOUS, focus distances may change from
- * time to time.
+ * focus mode is FOCUS_MODE_CONTINUOUS_VIDEO, focus distances may change
+ * from time to time.
*
* This method is intended to estimate the distance between the camera
* and the subject. After autofocus, the subject distance may be within
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index 4039a69..3ee8a80 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -70,8 +70,11 @@
if (!implCreated) {
synchronized (this) {
if (!implCreated) {
- implCreated = true;
- impl.create(true);
+ try {
+ impl.create(true);
+ } finally {
+ implCreated = true;
+ }
}
}
}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 5fea6fe..e56e257 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -307,8 +307,10 @@
* Requires the {@link android.Manifest.permission#REBOOT} permission.
*
* @param context the Context to use
- * @param packageFile the update package to install. Currently
- * must be on the /cache or /data partitions.
+ * @param packageFile the update package to install. Must be on
+ * a partition mountable by recovery. (The set of partitions
+ * known to recovery may vary from device to device. Generally,
+ * /cache and /data are safe.)
*
* @throws IOException if writing the recovery command file
* fails, or if the reboot itself fails.
@@ -316,15 +318,6 @@
public static void installPackage(Context context, File packageFile)
throws IOException {
String filename = packageFile.getCanonicalPath();
-
- if (filename.startsWith("/cache/")) {
- filename = "CACHE:" + filename.substring(7);
- } else if (filename.startsWith("/data/")) {
- filename = "DATA:" + filename.substring(6);
- } else {
- throw new IllegalArgumentException(
- "Must start with /cache or /data: " + filename);
- }
Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
String arg = "--update_package=" + filename;
bootCommand(context, arg);
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 871a0441..1e358c9 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -895,6 +895,14 @@
*/
public static final String COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI = "is_visible_in_downloads_ui";
+ /**
+ * If true, the user has confirmed that this download can proceed over the mobile network
+ * even though it exceeds the recommended maximum size.
+ * <P>Type: BOOLEAN</P>
+ */
+ public static final String COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT =
+ "bypass_recommended_size_limit";
+
/*
* Lists the destinations that an application can specify for a download.
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fd60115..69151df 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3428,6 +3428,15 @@
"download_manager_max_bytes_over_mobile";
/**
+ * The recommended maximum size, in bytes, of a download that the download manager should
+ * transfer over a non-wifi connection. Over this size, the use will be warned, but will
+ * have the option to start the download over the mobile connection anyway.
+ * @hide
+ */
+ public static final String DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE =
+ "download_manager_recommended_max_bytes_over_mobile";
+
+ /**
* ms during which to consume extra events related to Inet connection condition
* after a transtion to fully-connected
* @hide
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 456e0d9d..c9835a0 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1284,13 +1284,18 @@
return mDatabase.getHttpAuthUsernamePassword(host, realm);
}
+ private void clearHelpers() {
+ clearTextEntry(false);
+ selectionDone();
+ }
+
/**
* Destroy the internal state of the WebView. This method should be called
* after the WebView has been removed from the view system. No other
* methods may be called on a WebView after destroy.
*/
public void destroy() {
- clearTextEntry(false);
+ clearHelpers();
if (mWebViewCore != null) {
// Set the handlers to null before destroying WebViewCore so no
// more messages will be posted.
@@ -1608,7 +1613,7 @@
arg.mUrl = url;
arg.mExtraHeaders = extraHeaders;
mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);
- clearTextEntry(false);
+ clearHelpers();
}
/**
@@ -1637,7 +1642,7 @@
arg.mUrl = url;
arg.mPostData = postData;
mWebViewCore.sendMessage(EventHub.POST_URL, arg);
- clearTextEntry(false);
+ clearHelpers();
} else {
loadUrl(url);
}
@@ -1693,7 +1698,7 @@
arg.mEncoding = encoding;
arg.mHistoryUrl = historyUrl;
mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
- clearTextEntry(false);
+ clearHelpers();
}
/**
@@ -1710,7 +1715,7 @@
* Reload the current url.
*/
public void reload() {
- clearTextEntry(false);
+ clearHelpers();
switchOutDrawHistory();
mWebViewCore.sendMessage(EventHub.RELOAD);
}
@@ -1790,7 +1795,7 @@
private void goBackOrForward(int steps, boolean ignoreSnapshot) {
if (steps != 0) {
- clearTextEntry(false);
+ clearHelpers();
mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
ignoreSnapshot ? 1 : 0);
}
@@ -4421,7 +4426,7 @@
@Override
protected void onDetachedFromWindow() {
- clearTextEntry(false);
+ clearHelpers();
dismissZoomControl();
if (hasWindowFocus()) setActive(false);
super.onDetachedFromWindow();
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index f09421b..487a00d 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -35,6 +35,7 @@
android:label="@string/permlab_testDenied"
android:description="@string/permdesc_testDenied" />
+ <uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
<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/net/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/net/DownloadManagerBaseTest.java
index ee0f5f1..a7ec7d5 100644
--- a/core/tests/coretests/src/android/net/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/net/DownloadManagerBaseTest.java
@@ -27,6 +27,7 @@
import android.net.DownloadManager.Query;
import android.net.DownloadManager.Request;
import android.net.wifi.WifiManager;
+import android.os.Bundle;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
@@ -43,9 +44,12 @@
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;
@@ -67,6 +71,7 @@
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;
@@ -119,6 +124,7 @@
public static class MultipleDownloadsCompletedReceiver extends BroadcastReceiver {
private volatile int mNumDownloadsCompleted = 0;
+ private Set<Long> downloadIds = Collections.synchronizedSet(new HashSet<Long>());
/**
* {@inheritDoc}
@@ -129,6 +135,8 @@
++mNumDownloadsCompleted;
Log.i(LOG_TAG, "MultipleDownloadsCompletedReceiver got intent: " +
intent.getAction() + " --> total count: " + mNumDownloadsCompleted);
+ Bundle extras = intent.getExtras();
+ downloadIds.add(new Long(extras.getLong(DownloadManager.EXTRA_DOWNLOAD_ID)));
}
}
@@ -142,6 +150,18 @@
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(downloadIds) {
+ Set<Long> returnIds = new HashSet<Long>(downloadIds);
+ return returnIds;
+ }
+ }
+
}
public static class WiFiChangedReceiver extends BroadcastReceiver {
@@ -196,6 +216,17 @@
}
/**
+ * Helper to enqueue a response from the MockWebServer with no body.
+ *
+ * @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)
+ */
+ protected MockResponse enqueueResponse(int status) {
+ return doEnqueueResponse(status);
+
+ }
+
+ /**
* Helper to enqueue a response from the MockWebServer.
*
* @param status The HTTP status code to return for this response
diff --git a/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java b/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java
index be3cbf7..a61f02d 100644
--- a/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java
@@ -21,6 +21,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
+import android.net.DownloadManager;
import android.net.DownloadManager.Query;
import android.net.DownloadManager.Request;
import android.net.DownloadManagerBaseTest.DataType;
@@ -28,6 +29,7 @@
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;
@@ -36,10 +38,13 @@
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;
@@ -51,8 +56,11 @@
*/
public class DownloadManagerIntegrationTest extends DownloadManagerBaseTest {
- private static String LOG_TAG = "android.net.DownloadManagerIntegrationTest";
- private static String PROHIBITED_DIRECTORY = "/system";
+ private final static String LOG_TAG = "android.net.DownloadManagerIntegrationTest";
+ private final static String PROHIBITED_DIRECTORY =
+ Environment.getRootDirectory().getAbsolutePath();
+ private final static String CACHE_DIR =
+ Environment.getDownloadCacheDirectory().getAbsolutePath();
protected MultipleDownloadsCompletedReceiver mReceiver = null;
/**
@@ -74,25 +82,48 @@
public void tearDown() throws Exception {
super.tearDown();
setWiFiStateOn(true);
+ removeAllCurrentDownloads();
if (mReceiver != null) {
mContext.unregisterReceiver(mReceiver);
mReceiver = null;
- removeAllCurrentDownloads();
}
}
/**
* Helper that does the actual basic download verification.
*/
- protected void doBasicDownload(byte[] blobData) throws Exception {
+ protected long doBasicDownload(byte[] blobData) throws Exception {
long dlRequest = doStandardEnqueue(blobData);
// wait for the download to complete
waitForDownloadOrTimeout(dlRequest);
- verifyAndCleanupSingleFileDownload(dlRequest, blobData);
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
+ */
+ @LargeTest
+ public void doErrorTest(Uri uri, int error) throws Exception {
+ Request request = new Request(uri);
+ request.setTitle(DEFAULT_FILENAME);
+
+ long dlRequest = mDownloadManager.enqueue(request);
+ waitForDownloadOrTimeout(dlRequest);
+
+ Cursor cursor = getCursor(dlRequest);
+ try {
+ verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE, error);
+ } finally {
+ cursor.close();
+ }
}
/**
@@ -103,7 +134,8 @@
int fileSize = 500 * 1024; // 500k
byte[] blobData = generateData(fileSize, DataType.BINARY);
- doBasicDownload(blobData);
+ long dlRequest = doBasicDownload(blobData);
+ verifyAndCleanupSingleFileDownload(dlRequest, blobData);
}
/**
@@ -114,7 +146,8 @@
int fileSize = 300000;
byte[] blobData = generateData(fileSize, DataType.TEXT);
- doBasicDownload(blobData);
+ long dlRequest = doBasicDownload(blobData);
+ verifyAndCleanupSingleFileDownload(dlRequest, blobData);
}
/**
@@ -348,34 +381,209 @@
}
/**
- * Tests trying to download two large files (50M bytes, followed by 60M bytes)
+ * Tests downloading a file to cache when there isn't enough space in the cache to hold the
+ * entire file.
*/
@LargeTest
- public void testInsufficientSpaceSingleFiles() throws Exception {
- long fileSize1 = 50000000L;
- long fileSize2 = 60000000L;
- File largeFile1 = createFileOnSD(null, fileSize1, DataType.TEXT, null);
- File largeFile2 = createFileOnSD(null, fileSize2, DataType.TEXT, null);
+ 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 {
- long dlRequest = doStandardEnqueue(largeFile1);
- waitForDownloadOrTimeout(dlRequest);
- ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest);
- verifyFileContents(pfd, largeFile1);
- verifyFileSize(pfd, largeFile1.length());
+ // 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);
- dlRequest = doStandardEnqueue(largeFile2);
- waitForDownloadOrTimeout(dlRequest);
- Cursor cursor = getCursor(dlRequest);
- try {
- verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE,
- DownloadManager.ERROR_INSUFFICIENT_SPACE);
- } finally {
- cursor.close();
+ 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();
+ }
+ }
}
+
+ assertTrue(DOWNLOAD_FILE_SIZE > (fs.getAvailableBlocks() * blockSize));
+ byte[] blobData = generateData(DOWNLOAD_FILE_SIZE, DataType.TEXT);
+ long dlRequest = doBasicDownload(blobData);
+ verifyAndCleanupSingleFileDownload(dlRequest, blobData);
+
} finally {
- largeFile1.delete();
- largeFile2.delete();
+ 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_ERROR_CODE,
+ DownloadManager.ERROR_INSUFFICIENT_SPACE);
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Tests that we get the correct download ID from the download notification.
+ */
+ @LargeTest
+ public void testGetDownloadIdOnNotification() throws Exception {
+ byte[] blobData = generateData(3000, DataType.TEXT); // file size = 3000 bytes
+
+ MockResponse response = enqueueResponse(HTTP_OK, blobData);
+ long dlRequest = doCommonStandardEnqueue();
+ waitForDownloadOrTimeout(dlRequest);
+
+ Set<Long> ids = mReceiver.getDownloadIds();
+ assertEquals(1, ids.size());
+ Iterator<Long> it = ids.iterator();
+ assertEquals("Download ID received from notification does not match initial id!",
+ dlRequest, it.next().longValue());
+ }
+
+ /**
+ * Tests the download failure error after too many redirects (>5).
+ */
+ @LargeTest
+ public void testErrorTooManyRedirects() throws Exception {
+ Uri uri = getServerUri(DEFAULT_FILENAME);
+
+ // force 6 redirects
+ for (int i = 0; i < 6; ++i) {
+ MockResponse response = enqueueResponse(HTTP_REDIRECT);
+ response.addHeader("Location", uri.toString());
+ }
+ doErrorTest(uri, DownloadManager.ERROR_TOO_MANY_REDIRECTS);
+ }
+
+ /**
+ * Tests the download failure error from an unhandled HTTP status code
+ */
+ @LargeTest
+ public void testErrorUnhandledHttpCode() throws Exception {
+ Uri uri = getServerUri(DEFAULT_FILENAME);
+ MockResponse response = enqueueResponse(HTTP_PARTIAL_CONTENT);
+
+ doErrorTest(uri, DownloadManager.ERROR_UNHANDLED_HTTP_CODE);
+ }
+
+ /**
+ * Tests the download failure error from an unhandled HTTP status code
+ */
+ @LargeTest
+ public void testErrorHttpDataError_invalidRedirect() throws Exception {
+ Uri uri = getServerUri(DEFAULT_FILENAME);
+ MockResponse response = enqueueResponse(HTTP_REDIRECT);
+ response.addHeader("Location", "://blah.blah.blah.com");
+
+ doErrorTest(uri, DownloadManager.ERROR_HTTP_DATA_ERROR);
+ }
+
+ /**
+ * Tests that we can remove a download from the download manager.
+ */
+ @LargeTest
+ public void testRemoveDownload() throws Exception {
+ int fileSize = 100 * 1024; // 100k
+ byte[] blobData = generateData(fileSize, DataType.BINARY);
+
+ long dlRequest = doBasicDownload(blobData);
+ Cursor cursor = mDownloadManager.query(new Query().setFilterById(dlRequest));
+ try {
+ assertEquals("The count of downloads with this ID is not 1!", 1, cursor.getCount());
+ mDownloadManager.remove(dlRequest);
+ cursor.requery();
+ assertEquals("The count of downloads with this ID is not 0!", 0, cursor.getCount());
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Tests that we can set the title of a download.
+ */
+ @LargeTest
+ public void testSetTitle() throws Exception {
+ int fileSize = 50 * 1024; // 50k
+ byte[] blobData = generateData(fileSize, DataType.BINARY);
+ MockResponse response = enqueueResponse(HTTP_OK, blobData);
+
+ // An arbitrary unicode string title
+ final String title = "\u00a5123;\"\u0152\u017d \u054b \u0a07 \ucce0 \u6820\u03a8\u5c34" +
+ "\uf4ad\u0da9\uc0c5\uc1a8 \uf4c5 \uf4aa\u0023\'";
+
+ Uri uri = getServerUri(DEFAULT_FILENAME);
+ Request request = new Request(uri);
+ request.setTitle(title);
+
+ long dlRequest = mDownloadManager.enqueue(request);
+ waitForDownloadOrTimeout(dlRequest);
+
+ Cursor cursor = getCursor(dlRequest);
+ try {
+ verifyString(cursor, DownloadManager.COLUMN_TITLE, title);
+ } finally {
+ cursor.close();
}
}
}
diff --git a/core/tests/hosttests/src/android/net/DownloadManagerHostTests.java b/core/tests/hosttests/src/android/net/DownloadManagerHostTests.java
index ed280c9..df781bf 100644
--- a/core/tests/hosttests/src/android/net/DownloadManagerHostTests.java
+++ b/core/tests/hosttests/src/android/net/DownloadManagerHostTests.java
@@ -54,8 +54,10 @@
private static final String EXTERNAL_DOWNLOAD_URI_KEY = "external_download_uri";
// Note: External environment variable ANDROID_TEST_EXTERNAL_URI must be set to point to the
// external URI under which the files downloaded by the tests can be found. Note that the Uri
- // must be accessible by the device during a test run.
- private static String EXTERNAL_DOWNLOAD_URI_VALUE = null;
+ // must be accessible by the device during a test run. Correspondingly,
+ // ANDROID_TEST_EXTERNAL_LARGE_URI should point to the external URI of the folder containing
+ // large files.
+ private static String externalDownloadUriValue = null;
Hashtable<String, String> mExtraParams = null;
@@ -69,8 +71,8 @@
// ensure apk path has been set before test is run
assertNotNull(getTestAppPath());
mPMUtils = new PackageManagerHostTestUtils(getDevice());
- EXTERNAL_DOWNLOAD_URI_VALUE = System.getenv("ANDROID_TEST_EXTERNAL_URI");
- assertNotNull(EXTERNAL_DOWNLOAD_URI_VALUE);
+ externalDownloadUriValue = System.getenv("ANDROID_TEST_EXTERNAL_URI");
+ assertNotNull(externalDownloadUriValue);
mExtraParams = getExtraParams();
}
@@ -79,7 +81,7 @@
*/
protected Hashtable<String, String> getExtraParams() {
Hashtable<String, String> extraParams = new Hashtable<String, String>();
- extraParams.put(EXTERNAL_DOWNLOAD_URI_KEY, EXTERNAL_DOWNLOAD_URI_VALUE);
+ extraParams.put(EXTERNAL_DOWNLOAD_URI_KEY, externalDownloadUriValue);
return extraParams;
}
@@ -198,4 +200,19 @@
DOWNLOAD_TEST_RUNNER_NAME, mExtraParams);
assertTrue(testPassed);
}
+
+ /**
+ * Spawns a device-based function to test 15 concurrent downloads of 5,000,000-byte files
+ *
+ * @throws Exception if the test failed at any point
+ */
+ public void testDownloadMultipleSimultaneously() throws Exception {
+ mPMUtils.installAppAndVerifyExistsOnDevice(String.format("%s%s%s", getTestAppPath(),
+ File.separator, FILE_DOWNLOAD_APK), FILE_DOWNLOAD_PKG, true);
+
+ boolean testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG,
+ FILE_DOWNLOAD_CLASS, "runDownloadMultipleSimultaneously",
+ DOWNLOAD_TEST_RUNNER_NAME, mExtraParams);
+ assertTrue(testPassed);
+ }
}
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 ef81353..0293ded 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
@@ -35,6 +35,7 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
+import java.util.HashSet;
import coretestutils.http.MockResponse;
import coretestutils.http.MockWebServer;
@@ -55,8 +56,13 @@
protected static String DOWNLOAD_10MB_FILENAME = "External10mb.apk";
protected static long DOWNLOAD_10MB_FILESIZE = 10258741;
+ private static final String FILE_CONCURRENT_DOWNLOAD_FILE_PREFIX = "file";
+ private static final String FILE_CONCURRENT_DOWNLOAD_FILE_EXTENSION = ".bin";
+ protected static long CONCURRENT_DOWNLOAD_FILESIZE = 1000000;
+
// Values to be obtained from TestRunner
private String externalDownloadUriValue = null;
+ private String externalLargeDownloadUriValue = null;
/**
* {@inheritDoc }
@@ -65,12 +71,24 @@
public void setUp() throws Exception {
super.setUp();
DownloadManagerTestRunner mRunner = (DownloadManagerTestRunner)getInstrumentation();
- externalDownloadUriValue = mRunner.externalDownloadUriValue;
+ externalDownloadUriValue = normalizeUri(mRunner.externalDownloadUriValue);
assertNotNull(externalDownloadUriValue);
- if (!externalDownloadUriValue.endsWith("/")) {
- externalDownloadUriValue += "/";
+ externalLargeDownloadUriValue = normalizeUri(mRunner.externalDownloadUriValue);
+ assertNotNull(externalLargeDownloadUriValue);
+ }
+
+ /**
+ * Normalizes a uri to ensure it ends with a "/"
+ *
+ * @param uri The uri to normalize (or null)
+ * @return The normalized uri, or null if null was passed in
+ */
+ public String normalizeUri(String uri) {
+ if (uri != null && !uri.endsWith("/")) {
+ uri += "/";
}
+ return uri;
}
/**
@@ -460,4 +478,37 @@
downloadedFile.delete();
}
}
+
+ /**
+ * Tests 15 concurrent downloads of 1,000,000-byte files.
+ *
+ * @throws Exception if test failed
+ */
+ public void runDownloadMultipleSimultaneously() throws Exception {
+ final int TOTAL_DOWNLOADS = 15;
+ HashSet<Long> downloadIds = new HashSet<Long>(TOTAL_DOWNLOADS);
+ MultipleDownloadsCompletedReceiver receiver = registerNewMultipleDownloadsReceiver();
+
+ // Make sure there are no pending downloads currently going on
+ removeAllCurrentDownloads();
+
+ try {
+ for (int i = 0; i < TOTAL_DOWNLOADS; ++i) {
+ long dlRequest = -1;
+ String filename = FILE_CONCURRENT_DOWNLOAD_FILE_PREFIX + i
+ + FILE_CONCURRENT_DOWNLOAD_FILE_EXTENSION;
+ Uri remoteUri = getExternalFileUri(filename);
+ Request request = new Request(remoteUri);
+ request.setTitle(filename);
+ dlRequest = mDownloadManager.enqueue(request);
+ assertTrue(dlRequest != -1);
+ downloadIds.add(dlRequest);
+ }
+
+ waitForDownloadsOrTimeout(DEFAULT_WAIT_POLL_TIME, 15 * 60 * 2000); // wait 15 mins max
+ assertEquals(TOTAL_DOWNLOADS, receiver.numDownloadsCompleted());
+ } finally {
+ removeAllCurrentDownloads();
+ }
+ }
}
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestRunner.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestRunner.java
index 0f16619..27bf7e1 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestRunner.java
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestRunner.java
@@ -30,7 +30,7 @@
*
* To run the download manager tests:
*
- * adb shell am instrument -e external_download_1mb_uri <uri> external_download_500k_uri <uri> \
+ * adb shell am instrument -e external_download_uri <uri> external_large_download_uri <uri> \
* -w com.android.frameworks.downloadmanagertests/.DownloadManagerTestRunner
*/
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 7c5371a..53039a0 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -380,12 +380,14 @@
// continuously. Applications should not call
// CameraHardwareInterface.autoFocus in this mode.
static const char FOCUS_MODE_EDOF[];
- // Continuous auto focus mode. The camera continuously tries to focus. This
- // is ideal for shooting video or shooting photo of moving object. Auto
- // focus starts when the parameter is set. Applications should not call
- // CameraHardwareInterface.autoFocus in this mode. To stop continuous
- // focus, applications should change the focus mode to other modes.
- static const char FOCUS_MODE_CONTINUOUS[];
+ // Continuous auto focus mode intended for video recording. The camera
+ // continuously tries to focus. This is ideal for shooting video.
+ // Applications still can call CameraHardwareInterface.takePicture in this
+ // mode but the subject may not be in focus. Auto focus starts when the
+ // parameter is set. Applications should not call
+ // CameraHardwareInterface.autoFocus in this mode. To stop continuous focus,
+ // applications should change the focus mode to other modes.
+ static const char FOCUS_MODE_CONTINUOUS_VIDEO[];
private:
DefaultKeyedVector<String8,String8> mMap;
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index 362d9ee..83e5e57f 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -142,7 +142,7 @@
const char CameraParameters::FOCUS_MODE_MACRO[] = "macro";
const char CameraParameters::FOCUS_MODE_FIXED[] = "fixed";
const char CameraParameters::FOCUS_MODE_EDOF[] = "edof";
-const char CameraParameters::FOCUS_MODE_CONTINUOUS[] = "continuous";
+const char CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO[] = "continuous-video";
CameraParameters::CameraParameters()
: mMap()
diff --git a/media/java/android/media/AudioEffect.java b/media/java/android/media/AudioEffect.java
index ae67114..ed7601e 100644
--- a/media/java/android/media/AudioEffect.java
+++ b/media/java/android/media/AudioEffect.java
@@ -847,39 +847,40 @@
// -------------------------------------------------------------------------
/**
- * This intent launches an audio effect control panel UI. The goal of this intent is to enable
- * separate implementations of music/media player applications and audio effect control
- * application or services. This will allow platform vendors to offer more advanced control
- * options for standard effects or control for platform specific effects.
+ * Intent to launch an audio effect control panel UI.
+ * <p>The goal of this intent is to enable separate implementations of music/media player
+ * applications and audio effect control application or services.
+ * This will allow platform vendors to offer more advanced control options for standard effects
+ * or control for platform specific effects.
* <p>The intent carries a number of extras used by the player application to communicate
* necessary pieces of information to the control panel application.
* <p>The calling application must use the
* {@link android.app.Activity#startActivityForResult(Intent, int)} method to launch the
* control panel so that its package name is indicated and used by the control panel
* application to keep track of changes for this particular application.
- * <p>The android.media.EXTRA_AUDIO_SESSION extra will indicate an audio session to which the
+ * <p>The {@link #EXTRA_AUDIO_SESSION} extra will indicate an audio session to which the
* audio effects should be applied. If no audio session is specified, either one of the
* follownig will happen:
- * - If an audio session was previously opened by the calling application with
+ * <p>- If an audio session was previously opened by the calling application with
* {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intent, the effect changes will
* be applied to that session.
- * - If no audio session is opened, the changes will be stored in the package specific storage
- * area and applied whenever a new audio session is opened by this application.
- * <p>The android.media.EXTRA_CONTENT_TYPE extra will help the control panel application
+ * <p>- If no audio session is opened, the changes will be stored in the package specific
+ * storage area and applied whenever a new audio session is opened by this application.
+ * <p>The {@link #EXTRA_CONTENT_TYPE} extra will help the control panel application
* customize both the UI layout and the default audio effect settings if none are already
* stored for the calling application.
- * {@hide} pending API council approval
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL =
"android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL";
/**
- * This intent indicates to the effect control application or service that a new audio session
- * is opened and requires audio effects to be applied. This is different from
- * {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} in that no UI should be displayed in
- * this case. Music player applications can broadcast this intent before starting playback
- * to make sure that any audio effect settings previously selected by the user are applied.
+ * Intent to signal to the effect control application or service that a new audio session
+ * is opened and requires audio effects to be applied.
+ * <p>This is different from {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} in that no
+ * UI should be displayed in this case. Music player applications can broadcast this intent
+ * before starting playback to make sure that any audio effect settings previously selected
+ * by the user are applied.
* <p>The effect control application receiving this intent will look for previously stored
* settings for the calling application, create all required audio effects and apply the
* effect settings to the specified audio session.
@@ -888,48 +889,50 @@
* <p>If no stored settings are found for the calling application, default settings for the
* content type indicated by {@link #EXTRA_CONTENT_TYPE} will be applied. The default settings
* for a given content type are platform specific.
- * {@hide} pending API council approval
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION =
"android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION";
/**
- * This intent indicates to the effect control application or service that an audio session
+ * Intent to signal to the effect control application or service that an audio session
* is closed and that effects should not be applied anymore.
- * <p>The effect control application receiving this intent will delete all effects on this
- * session and store current settings in package specific storage.
+ * <p>The effect control application receiving this intent will delete all effects on
+ * this session and store current settings in package specific storage.
* <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the
* audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory.
* <p>It is good practice for applications to broadcast this intent when music playback stops
* and/or when exiting to free system resources consumed by audio effect engines.
- * {@hide} pending API council approval
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION =
"android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION";
/**
- * This extra indicates the ID of the audio session the effects should be applied to.
+ * Contains the ID of the audio session the effects should be applied to.
+ * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL},
+ * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
+ * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
* <p>The extra value is of type int and is the audio session ID.
- * @see android.media.MediaPlayer#setAudioSessionId(int) for details on audio sessions.
- * {@hide} pending API council approval
+ *
+ * @see android.media.MediaPlayer#setAudioSessionId(int)
*/
public static final String EXTRA_AUDIO_SESSION = "android.media.extra.AUDIO_SESSION";
/**
- * This extra indicates the package name of the calling application for
- * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
+ * Contains the package name of the calling application.
+ * <p>This extra is for use with {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
* {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
* <p>The extra value is a string containing the full package name.
- * {@hide} pending API council approval
*/
public static final String EXTRA_PACKAGE_NAME = "android.media.extra.PACKAGE_NAME";
/**
- * This extra indicates which type of content is played by the application. This information is
- * used by the effect control application to customize UI and default effect settings.
- * The content type is one of the following:
+ * Indicates which type of content is played by the application.
+ * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} and
+ * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intents.
+ * <p>This information is used by the effect control application to customize UI and select
+ * appropriate default effect settings. The content type is one of the following:
* <ul>
* <li>{@link #CONTENT_TYPE_MUSIC}</li>
* <li>{@link #CONTENT_TYPE_MOVIE}</li>
@@ -937,28 +940,23 @@
* <li>{@link #CONTENT_TYPE_VOICE}</li>
* </ul>
* If omitted, the content type defaults to {@link #CONTENT_TYPE_MUSIC}.
- * {@hide} pending API council approval
*/
public static final String EXTRA_CONTENT_TYPE = "android.media.extra.CONTENT_TYPE";
/**
* Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is music
- * {@hide} pending API council approval
*/
public static final int CONTENT_TYPE_MUSIC = 0;
/**
- * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is video of movie
- * {@hide} pending API council approval
+ * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is video or movie
*/
public static final int CONTENT_TYPE_MOVIE = 1;
/**
* Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is game audio
- * {@hide} pending API council approval
*/
public static final int CONTENT_TYPE_GAME = 2;
/**
* Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is voice audio
- * {@hide} pending API council approval
*/
public static final int CONTENT_TYPE_VOICE = 3;
diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
index c5b51c0..e4ed5e6 100644
--- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp
+++ b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
@@ -123,6 +123,8 @@
mAnchorTimeUs = 0;
mNumSamplesOutput = 0;
mStarted = true;
+ mNumDecodedBuffers = 0;
+ mUpsamplingFactor = 2;
return OK;
}
@@ -207,22 +209,65 @@
Int decoderErr = PVMP4AudioDecodeFrame(mConfig, mDecoderBuf);
- // Check on the sampling rate to see whether it is changed.
- int32_t sampleRate;
- CHECK(mMeta->findInt32(kKeySampleRate, &sampleRate));
- if (mConfig->samplingRate != sampleRate) {
- mMeta->setInt32(kKeySampleRate, mConfig->samplingRate);
- LOGW("Sample rate was %d, but now is %d",
- sampleRate, mConfig->samplingRate);
- buffer->release();
- mInputBuffer->release();
- mInputBuffer = NULL;
- return INFO_FORMAT_CHANGED;
+ /*
+ * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
+ * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
+ * rate system and the sampling rate in the final output is actually
+ * doubled compared with the core AAC decoder sampling rate.
+ *
+ * Explicit signalling is done by explicitly defining SBR audio object
+ * type in the bitstream. Implicit signalling is done by embedding
+ * SBR content in AAC extension payload specific to SBR, and hence
+ * requires an AAC decoder to perform pre-checks on actual audio frames.
+ *
+ * Thus, we could not say for sure whether a stream is
+ * AAC+/eAAC+ until the first data frame is decoded.
+ */
+ if (++mNumDecodedBuffers <= 2) {
+ LOGV("audio/extended audio object type: %d + %d",
+ mConfig->audioObjectType, mConfig->extendedAudioObjectType);
+ LOGV("aac+ upsampling factor: %d desired channels: %d",
+ mConfig->aacPlusUpsamplingFactor, mConfig->desiredChannels);
+
+ CHECK(mNumDecodedBuffers > 0);
+ if (mNumDecodedBuffers == 1) {
+ mUpsamplingFactor = mConfig->aacPlusUpsamplingFactor;
+ // Check on the sampling rate to see whether it is changed.
+ int32_t sampleRate;
+ CHECK(mMeta->findInt32(kKeySampleRate, &sampleRate));
+ if (mConfig->samplingRate != sampleRate) {
+ mMeta->setInt32(kKeySampleRate, mConfig->samplingRate);
+ LOGW("Sample rate was %d Hz, but now is %d Hz",
+ sampleRate, mConfig->samplingRate);
+ buffer->release();
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ return INFO_FORMAT_CHANGED;
+ }
+ } else { // mNumDecodedBuffers == 2
+ if (mConfig->extendedAudioObjectType == MP4AUDIO_AAC_LC ||
+ mConfig->extendedAudioObjectType == MP4AUDIO_LTP) {
+ if (mUpsamplingFactor == 2) {
+ // The stream turns out to be not aacPlus mode anyway
+ LOGW("Disable AAC+/eAAC+ since extended audio object type is %d",
+ mConfig->extendedAudioObjectType);
+ mConfig->aacPlusEnabled = 0;
+ }
+ } else {
+ if (mUpsamplingFactor == 1) {
+ // aacPlus mode does not buy us anything, but to cause
+ // 1. CPU load to increase, and
+ // 2. a half speed of decoding
+ LOGW("Disable AAC+/eAAC+ since upsampling factor is 1");
+ mConfig->aacPlusEnabled = 0;
+ }
+ }
+ }
}
size_t numOutBytes =
mConfig->frameLength * sizeof(int16_t) * mConfig->desiredChannels;
- if (mConfig->aacPlusUpsamplingFactor == 2) {
+ if (mUpsamplingFactor == 2) {
if (mConfig->desiredChannels == 1) {
memcpy(&mConfig->pOutputBuffer[1024], &mConfig->pOutputBuffer[2048], numOutBytes * 2);
}
diff --git a/media/libstagefright/include/AACDecoder.h b/media/libstagefright/include/AACDecoder.h
index 200f93c..886a3b7 100644
--- a/media/libstagefright/include/AACDecoder.h
+++ b/media/libstagefright/include/AACDecoder.h
@@ -53,6 +53,8 @@
int64_t mAnchorTimeUs;
int64_t mNumSamplesOutput;
status_t mInitCheck;
+ int64_t mNumDecodedBuffers;
+ int32_t mUpsamplingFactor;
MediaBuffer *mInputBuffer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index 7ccf210..0fc092e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -711,21 +711,20 @@
ConnectivityManager.EXTRA_NETWORK_INFO));
int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0);
Slog.d(TAG, "got CONNECTIVITY_ACTION - info=" + info + ", status = " + connectionStatus);
- if (info.isConnected() == false) return;
+
+ int inetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0);
switch (info.getType()) {
case ConnectivityManager.TYPE_MOBILE:
- if (info.isConnected()) {
- updateDataNetType(info.getSubtype(), connectionStatus);
- updateDataIcon();
- updateSignalStrength(); // apply any change in connectionStatus
- }
+ mInetCondition = inetCondition;
+ updateDataNetType(info.getSubtype());
+ updateDataIcon();
+ updateSignalStrength(); // apply any change in connectionStatus
break;
case ConnectivityManager.TYPE_WIFI:
+ mInetCondition = inetCondition;
if (info.isConnected()) {
mIsWifiConnected = true;
- mInetCondition =
- (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0);
int iconId;
if (mLastWifiSignalLevel == -1) {
iconId = sWifiSignalImages[mInetCondition][0];
@@ -738,7 +737,6 @@
} else {
mLastWifiSignalLevel = -1;
mIsWifiConnected = false;
- mInetCondition = 0;
int iconId = sWifiSignalImages[0][0];
mService.setIcon("wifi", iconId, 0);
@@ -777,9 +775,8 @@
@Override
public void onDataConnectionStateChanged(int state, int networkType) {
mDataState = state;
- updateDataNetType(networkType, 0);
+ updateDataNetType(networkType);
updateDataIcon();
- updateSignalStrength(); // apply the change in connection status
}
@Override
@@ -940,8 +937,7 @@
return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
}
- private final void updateDataNetType(int net, int inetCondition) {
- mInetCondition = (inetCondition > INET_CONDITION_THRESHOLD ? 1 : 0);
+ private final void updateDataNetType(int net) {
switch (net) {
case TelephonyManager.NETWORK_TYPE_EDGE:
mDataIconList = sDataNetType_e[mInetCondition];
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
index 55d31ec..29df28e 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
@@ -30,6 +30,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.hardware.Usb;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -70,11 +71,11 @@
static final boolean localLOGV = false;
/** Used to detect when the USB cable is unplugged, so we can call finish() */
- private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
+ private BroadcastReceiver mUsbStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction() == Intent.ACTION_BATTERY_CHANGED) {
- handleBatteryChanged(intent);
+ if (intent.getAction().equals(Usb.ACTION_USB_STATE)) {
+ handleUsbStateChanged(intent);
}
}
};
@@ -139,7 +140,7 @@
super.onResume();
mStorageManager.registerListener(mStorageListener);
- registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+ registerReceiver(mUsbStateReceiver, new IntentFilter(Usb.ACTION_USB_STATE));
try {
switchDisplay(mStorageManager.isUsbMassStorageEnabled());
} catch (Exception ex) {
@@ -151,15 +152,15 @@
protected void onPause() {
super.onPause();
- unregisterReceiver(mBatteryReceiver);
+ unregisterReceiver(mUsbStateReceiver);
if (mStorageManager == null && mStorageListener != null) {
mStorageManager.unregisterListener(mStorageListener);
}
}
- private void handleBatteryChanged(Intent intent) {
- int pluggedType = intent.getIntExtra("plugged", 0);
- if (pluggedType == 0) {
+ private void handleUsbStateChanged(Intent intent) {
+ boolean connected = intent.getExtras().getBoolean(Usb.USB_CONNECTED);
+ if (!connected) {
// It was disconnected from the plug, so finish
finish();
}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 8603e51..f38748b 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -920,11 +920,19 @@
newNet = tryFailover(prevNetType);
if (newNet != null) {
NetworkInfo switchTo = newNet.getNetworkInfo();
+ if (!switchTo.isConnected()) {
+ // if the other net is connected they've already reset this and perhaps even gotten
+ // a positive report we don't want to overwrite, but if not we need to clear this now
+ // to turn our cellular sig strength white
+ mDefaultInetConditionPublished = 0;
+ }
intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
} else {
+ mDefaultInetConditionPublished = 0; // we're not connected anymore
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
}
}
+ intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished);
// do this before we broadcast the change
handleConnectivityChange(prevNetType);
@@ -1083,12 +1091,20 @@
newNet = tryFailover(info.getType());
if (newNet != null) {
NetworkInfo switchTo = newNet.getNetworkInfo();
+ if (!switchTo.isConnected()) {
+ // if the other net is connected they've already reset this and perhaps even gotten
+ // a positive report we don't want to overwrite, but if not we need to clear this now
+ // to turn our cellular sig strength white
+ mDefaultInetConditionPublished = 0;
+ }
intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
} else {
+ mDefaultInetConditionPublished = 0;
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
}
}
+ intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished);
sendStickyBroadcast(intent);
/*
* If the failover network is already connected, then immediately send
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 87329e3..c047e10 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -173,6 +173,7 @@
private static final int MESSAGE_STOP_ACCESS_POINT = 7;
private static final int MESSAGE_SET_CHANNELS = 8;
private static final int MESSAGE_ENABLE_NETWORKS = 9;
+ private static final int MESSAGE_START_SCAN = 10;
private final WifiHandler mWifiHandler;
@@ -385,23 +386,12 @@
/**
* see {@link android.net.wifi.WifiManager#startScan()}
- * @return {@code true} if the operation succeeds
*/
- public boolean startScan(boolean forceActive) {
+ public void startScan(boolean forceActive) {
enforceChangePermission();
+ if (mWifiHandler == null) return;
- switch (mWifiStateTracker.getSupplicantState()) {
- case DISCONNECTED:
- case INACTIVE:
- case SCANNING:
- case DORMANT:
- break;
- default:
- mWifiStateTracker.setScanResultHandling(
- WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);
- break;
- }
- return mWifiStateTracker.scan(forceActive);
+ Message.obtain(mWifiHandler, MESSAGE_START_SCAN, forceActive ? 1 : 0, 0).sendToTarget();
}
/**
@@ -2001,6 +1991,21 @@
mWifiStateTracker.enableAllNetworks(getConfiguredNetworks());
break;
+ case MESSAGE_START_SCAN:
+ boolean forceActive = (msg.arg1 == 1);
+ switch (mWifiStateTracker.getSupplicantState()) {
+ case DISCONNECTED:
+ case INACTIVE:
+ case SCANNING:
+ case DORMANT:
+ break;
+ default:
+ mWifiStateTracker.setScanResultHandling(
+ WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);
+ break;
+ }
+ mWifiStateTracker.scan(forceActive);
+ break;
}
}
}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 0ee559e..198b1e6 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -42,7 +42,7 @@
boolean pingSupplicant();
- boolean startScan(boolean forceActive);
+ void startScan(boolean forceActive);
List<ScanResult> getScanResults();
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index dd162f2..f883588 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -570,7 +570,8 @@
*/
public boolean startScan() {
try {
- return mService.startScan(false);
+ mService.startScan(false);
+ return true;
} catch (RemoteException e) {
return false;
}
@@ -588,7 +589,8 @@
*/
public boolean startScanActive() {
try {
- return mService.startScan(true);
+ mService.startScan(true);
+ return true;
} catch (RemoteException e) {
return false;
}