Merge "only make AccountManager throw an exception on blocking calls in the UI thread if the application's SDK is froyo or later"
diff --git a/api/current.xml b/api/current.xml
index b155c45..a8f7109 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -32785,6 +32785,30 @@
 <parameter name="mode" type="int">
 </parameter>
 </method>
+<method name="getExternalCacheDir"
+ return="java.io.File"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExternalFilesDir"
+ return="java.io.File"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</method>
 <method name="getFileStreamPath"
  return="java.io.File"
  abstract="true"
@@ -34293,6 +34317,30 @@
 <parameter name="mode" type="int">
 </parameter>
 </method>
+<method name="getExternalCacheDir"
+ return="java.io.File"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExternalFilesDir"
+ return="java.io.File"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</method>
 <method name="getFileStreamPath"
  return="java.io.File"
  abstract="false"
@@ -83028,6 +83076,25 @@
 <parameter name="mimeType" type="java.lang.String">
 </parameter>
 </method>
+<method name="scanFile"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="paths" type="java.lang.String[]">
+</parameter>
+<parameter name="mimeTypes" type="java.lang.String[]">
+</parameter>
+<parameter name="callback" type="android.media.MediaScannerConnection.ScanResultListener">
+</parameter>
+</method>
 </class>
 <interface name="MediaScannerConnection.MediaScannerConnectionClient"
  abstract="true"
@@ -83036,6 +83103,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.media.MediaScannerConnection.ScanResultListener">
+</implements>
 <method name="onMediaScannerConnected"
  return="void"
  abstract="true"
@@ -83063,6 +83132,29 @@
 </parameter>
 </method>
 </interface>
+<interface name="MediaScannerConnection.ScanResultListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onScanCompleted"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+</interface>
 <class name="Ringtone"
  extends="java.lang.Object"
  abstract="false"
@@ -112670,6 +112762,19 @@
  visibility="public"
 >
 </method>
+<method name="getExternalStoragePublicDirectory"
+ return="java.io.File"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</method>
 <method name="getExternalStorageState"
  return="java.lang.String"
  abstract="false"
@@ -112692,6 +112797,96 @@
  visibility="public"
 >
 </method>
+<field name="DIRECTORY_ALARMS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_DCIM"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_DOWNLOADS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_MOVIES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_MUSIC"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_NOTIFICATIONS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_PICTURES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_PODCASTS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_RINGTONES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="MEDIA_BAD_REMOVAL"
  type="java.lang.String"
  transient="false"
@@ -144243,6 +144438,30 @@
 <parameter name="mode" type="int">
 </parameter>
 </method>
+<method name="getExternalCacheDir"
+ return="java.io.File"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExternalFilesDir"
+ return="java.io.File"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</method>
 <method name="getFileStreamPath"
  return="java.io.File"
  abstract="false"
@@ -181293,6 +181512,17 @@
 <parameter name="t" type="android.view.animation.Transformation">
 </parameter>
 </method>
+<method name="cancel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="computeDurationHint"
  return="long"
  abstract="false"
diff --git a/common/java/com/android/common/Patterns.java b/common/java/com/android/common/Patterns.java
index 24a18c0..71c3a5e 100644
--- a/common/java/com/android/common/Patterns.java
+++ b/common/java/com/android/common/Patterns.java
@@ -25,87 +25,87 @@
 public class Patterns {
     /**
      *  Regular expression pattern to match all IANA top-level domains.
-     *  List accurate as of 2007/06/15.  List taken from:
+     *  List accurate as of 2010/02/05.  List taken from:
      *  http://data.iana.org/TLD/tlds-alpha-by-domain.txt
-     *  This pattern is auto-generated by //device/tools/make-iana-tld-pattern.py
+     *  This pattern is auto-generated by development/tools/make-iana-tld-pattern.py
      */
-    public static final Pattern TOP_LEVEL_DOMAIN
-        = Pattern.compile(
-                "((aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
-                + "|(biz|b[abdefghijmnorstvwyz])"
-                + "|(cat|com|coop|c[acdfghiklmnoruvxyz])"
-                + "|d[ejkmoz]"
-                + "|(edu|e[cegrstu])"
-                + "|f[ijkmor]"
-                + "|(gov|g[abdefghilmnpqrstuwy])"
-                + "|h[kmnrtu]"
-                + "|(info|int|i[delmnoqrst])"
-                + "|(jobs|j[emop])"
-                + "|k[eghimnrwyz]"
-                + "|l[abcikrstuvy]"
-                + "|(mil|mobi|museum|m[acdghklmnopqrstuvwxyz])"
-                + "|(name|net|n[acefgilopruz])"
-                + "|(org|om)"
-                + "|(pro|p[aefghklmnrstwy])"
-                + "|qa"
-                + "|r[eouw]"
-                + "|s[abcdeghijklmnortuvyz]"
-                + "|(tel|travel|t[cdfghjklmnoprtvwz])"
-                + "|u[agkmsyz]"
-                + "|v[aceginu]"
-                + "|w[fs]"
-                + "|y[etu]"
-                + "|z[amw])");
+    public static final Pattern TOP_LEVEL_DOMAIN = Pattern.compile(
+        "((aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
+        + "|(biz|b[abdefghijmnorstvwyz])"
+        + "|(cat|com|coop|c[acdfghiklmnoruvxyz])"
+        + "|d[ejkmoz]"
+        + "|(edu|e[cegrstu])"
+        + "|f[ijkmor]"
+        + "|(gov|g[abdefghilmnpqrstuwy])"
+        + "|h[kmnrtu]"
+        + "|(info|int|i[delmnoqrst])"
+        + "|(jobs|j[emop])"
+        + "|k[eghimnprwyz]"
+        + "|l[abcikrstuvy]"
+        + "|(mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])"
+        + "|(name|net|n[acefgilopruz])"
+        + "|(org|om)"
+        + "|(pro|p[aefghklmnrstwy])"
+        + "|qa"
+        + "|r[eosuw]"
+        + "|s[abcdeghijklmnortuvyz]"
+        + "|(tel|travel|t[cdfghjklmnoprtvwz])"
+        + "|u[agksyz]"
+        + "|v[aceginu]"
+        + "|w[fs]"
+        + "|(xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-zckzah)"
+        + "|y[etu]"
+        + "|z[amw])");
 
     /**
      *  Regular expression pattern to match RFC 1738 URLs
-     *  List accurate as of 2007/06/15.  List taken from:
+     *  List accurate as of 2010/02/05.  List taken from:
      *  http://data.iana.org/TLD/tlds-alpha-by-domain.txt
-     *  This pattern is auto-generated by //device/tools/make-iana-tld-pattern.py
+     *  This pattern is auto-generated by development/tools/make-iana-tld-pattern.py
      */
-    public static final Pattern WEB_URL
-        = Pattern.compile(
-            "((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
-            + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_"
-            + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?"
-            + "((?:(?:[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}\\.)+"   // named host
-            + "(?:"   // plus top level domain
-            + "(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
-            + "|(?:biz|b[abdefghijmnorstvwyz])"
-            + "|(?:cat|com|coop|c[acdfghiklmnoruvxyz])"
-            + "|d[ejkmoz]"
-            + "|(?:edu|e[cegrstu])"
-            + "|f[ijkmor]"
-            + "|(?:gov|g[abdefghilmnpqrstuwy])"
-            + "|h[kmnrtu]"
-            + "|(?:info|int|i[delmnoqrst])"
-            + "|(?:jobs|j[emop])"
-            + "|k[eghimnrwyz]"
-            + "|l[abcikrstuvy]"
-            + "|(?:mil|mobi|museum|m[acdghklmnopqrstuvwxyz])"
-            + "|(?:name|net|n[acefgilopruz])"
-            + "|(?:org|om)"
-            + "|(?:pro|p[aefghklmnrstwy])"
-            + "|qa"
-            + "|r[eouw]"
-            + "|s[abcdeghijklmnortuvyz]"
-            + "|(?:tel|travel|t[cdfghjklmnoprtvwz])"
-            + "|u[agkmsyz]"
-            + "|v[aceginu]"
-            + "|w[fs]"
-            + "|y[etu]"
-            + "|z[amw]))"
-            + "|(?:(?:25[0-5]|2[0-4]" // or ip address
-            + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]"
-            + "|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1]"
-            + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
-            + "|[1-9][0-9]|[0-9])))"
-            + "(?:\\:\\d{1,5})?)" // plus option port number
-            + "(\\/(?:(?:[a-zA-Z0-9\\;\\/\\?\\:\\@\\&\\=\\#\\~"  // plus option query params
-            + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?"
-            + "(?:\\b|$)"); // and finally, a word boundary or end of
-                            // input.  This is to stop foo.sure from
-                            // matching as foo.su
+    public static final Pattern WEB_URL = Pattern.compile(
+        "((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
+        + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_"
+        + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?"
+        + "((?:(?:[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}\\.)+"   // named host
+        + "(?:"   // plus top level domain
+        + "(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
+        + "|(?:biz|b[abdefghijmnorstvwyz])"
+        + "|(?:cat|com|coop|c[acdfghiklmnoruvxyz])"
+        + "|d[ejkmoz]"
+        + "|(?:edu|e[cegrstu])"
+        + "|f[ijkmor]"
+        + "|(?:gov|g[abdefghilmnpqrstuwy])"
+        + "|h[kmnrtu]"
+        + "|(?:info|int|i[delmnoqrst])"
+        + "|(?:jobs|j[emop])"
+        + "|k[eghimnprwyz]"
+        + "|l[abcikrstuvy]"
+        + "|(?:mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])"
+        + "|(?:name|net|n[acefgilopruz])"
+        + "|(?:org|om)"
+        + "|(?:pro|p[aefghklmnrstwy])"
+        + "|qa"
+        + "|r[eosuw]"
+        + "|s[abcdeghijklmnortuvyz]"
+        + "|(?:tel|travel|t[cdfghjklmnoprtvwz])"
+        + "|u[agksyz]"
+        + "|v[aceginu]"
+        + "|w[fs]"
+        + "|(?:xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-zckzah)"
+        + "|y[etu]"
+        + "|z[amw]))"
+        + "|(?:(?:25[0-5]|2[0-4]" // or ip address
+        + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]"
+        + "|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1]"
+        + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
+        + "|[1-9][0-9]|[0-9])))"
+        + "(?:\\:\\d{1,5})?)" // plus option port number
+        + "(\\/(?:(?:[a-zA-Z0-9\\;\\/\\?\\:\\@\\&\\=\\#\\~"  // plus option query params
+        + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?"
+        + "(?:\\b|$)"); // and finally, a word boundary or end of
+                        // input.  This is to stop foo.sure from
+                        // matching as foo.su
 
     public static final Pattern IP_ADDRESS
         = Pattern.compile(
diff --git a/common/tests/src/com/android/common/PatternsTest.java b/common/tests/src/com/android/common/PatternsTest.java
index 7fabe5e..635601e 100644
--- a/common/tests/src/com/android/common/PatternsTest.java
+++ b/common/tests/src/com/android/common/PatternsTest.java
@@ -31,6 +31,20 @@
         t = Patterns.TOP_LEVEL_DOMAIN.matcher("com").matches();
         assertTrue("Missed valid TLD", t);
 
+        // One of the new top level domain.
+        t = Patterns.TOP_LEVEL_DOMAIN.matcher("me").matches();
+        assertTrue("Missed valid TLD", t);
+
+        // One of the new top level test domain.
+        t = Patterns.TOP_LEVEL_DOMAIN.matcher("xn--0zwm56d").matches();
+        assertTrue("Missed valid TLD", t);
+
+        t = Patterns.TOP_LEVEL_DOMAIN.matcher("mem").matches();
+        assertFalse("Matched invalid TLD!", t);
+
+        t = Patterns.TOP_LEVEL_DOMAIN.matcher("xn").matches();
+        assertFalse("Matched invalid TLD!", t);
+
         t = Patterns.TOP_LEVEL_DOMAIN.matcher("xer").matches();
         assertFalse("Matched invalid TLD!", t);
     }
@@ -42,6 +56,18 @@
         t = Patterns.WEB_URL.matcher("http://www.google.com").matches();
         assertTrue("Valid URL", t);
 
+        // Google in one of the new top level domain.
+        t = Patterns.WEB_URL.matcher("http://www.google.me").matches();
+        assertTrue("Valid URL", t);
+        t = Patterns.WEB_URL.matcher("google.me").matches();
+        assertTrue("Valid URL", t);
+
+        // Test url in Chinese: http://xn--fsqu00a.xn--0zwm56d
+        t = Patterns.WEB_URL.matcher("http://xn--fsqu00a.xn--0zwm56d").matches();
+        assertTrue("Valid URL", t);
+        t = Patterns.WEB_URL.matcher("xn--fsqu00a.xn--0zwm56d").matches();
+        assertTrue("Valid URL", t);
+
         t = Patterns.WEB_URL.matcher("ftp://www.example.com").matches();
         assertFalse("Matched invalid protocol", t);
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4923eee..9b9cbd5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -197,9 +197,9 @@
     private File mDatabasesDir;
     private File mPreferencesDir;
     private File mFilesDir;
-    
-
     private File mCacheDir;
+    private File mExternalFilesDir;
+    private File mExternalCacheDir;
     
     private static long sInstanceCount = 0;
 
@@ -438,6 +438,38 @@
     }
     
     @Override
+    public File getExternalFilesDir(String type) {
+        synchronized (mSync) {
+            if (mExternalFilesDir == null) {
+                mExternalFilesDir = Environment.getExternalStorageAppFilesDirectory(
+                        getPackageName());
+            }
+            if (!mExternalFilesDir.exists()) {
+                try {
+                    (new File(Environment.getExternalStorageAndroidDataDir(),
+                            ".nomedia")).createNewFile();
+                } catch (IOException e) {
+                }
+                if (!mExternalFilesDir.mkdirs()) {
+                    Log.w(TAG, "Unable to create external files directory");
+                    return null;
+                }
+            }
+            if (type == null) {
+                return mExternalFilesDir;
+            }
+            File dir = new File(mExternalFilesDir, type);
+            if (!dir.exists()) {
+                if (!dir.mkdirs()) {
+                    Log.w(TAG, "Unable to create external media directory " + dir);
+                    return null;
+                }
+            }
+            return dir;
+        }
+    }
+    
+    @Override
     public File getCacheDir() {
         synchronized (mSync) {
             if (mCacheDir == null) {
@@ -457,7 +489,28 @@
         return mCacheDir;
     }
     
-
+    @Override
+    public File getExternalCacheDir() {
+        synchronized (mSync) {
+            if (mExternalCacheDir == null) {
+                mExternalCacheDir = Environment.getExternalStorageAppCacheDirectory(
+                        getPackageName());
+            }
+            if (!mExternalCacheDir.exists()) {
+                try {
+                    (new File(Environment.getExternalStorageAndroidDataDir(),
+                            ".nomedia")).createNewFile();
+                } catch (IOException e) {
+                }
+                if (!mExternalCacheDir.mkdirs()) {
+                    Log.w(TAG, "Unable to create external cache directory");
+                    return null;
+                }
+            }
+            return mExternalCacheDir;
+        }
+    }
+    
     @Override
     public File getFileStreamPath(String name) {
         return makeFilename(getFilesDir(), name);
diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java
index 804c8eb..3fd36a37 100644
--- a/core/java/android/app/IntentService.java
+++ b/core/java/android/app/IntentService.java
@@ -42,7 +42,7 @@
      * {@link #onStartCommand(Intent, int, int)} will return
      * {@link Service#START_REDELIVER_INTENT} instead of
      * {@link Service#START_NOT_STICKY}, so that if this service's process
-     * is called while it is executing the Intent in
+     * is killed while it is executing the Intent in
      * {@link #onHandleIntent(Intent)}, then when later restarted the same Intent
      * will be re-delivered to it, to retry its execution.
      */
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b4a0bf8..672e5f7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -25,6 +25,7 @@
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
+import android.media.MediaScannerConnection.ScanResultListener;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -137,7 +138,28 @@
     
     /**
      * Return the context of the single, global Application object of the
-     * current process.
+     * current process.  This generally should only be used if you need a
+     * Context whose lifecycle is separate from the current context, that is
+     * tied to the lifetime of the process rather than the current component.
+     * 
+     * <p>Consider for example how this interacts with
+     * {@ #registerReceiver(BroadcastReceiver, IntentFilter)}:
+     * <ul>
+     * <li> <p>If used from an Activity context, the receiver is being registered
+     * within that activity.  This means that you are expected to unregister
+     * before the activity is done being destroyed; in fact if you do not do
+     * so, the framework will clean up your leaked registration as it removes
+     * the activity and log an error.  Thus, if you use the Activity context
+     * to register a receiver that is static (global to the process, not
+     * associated with an Activity instance) then that registration will be
+     * removed on you at whatever point the activity you used is destroyed.
+     * <li> <p>If used from the Context returned here, the receiver is being
+     * registered with the global state associated with your application.  Thus
+     * it will never be unregistered for you.  This is necessary if the receiver
+     * is associated with static data, not a particular component.  However
+     * using the ApplicationContext elsewhere can easily lead to serious leaks
+     * if you forget to unregister, unbind, etc.
+     * </ul>
      */
     public abstract Context getApplicationContext();
 
@@ -393,11 +415,84 @@
     public abstract File getFilesDir();
     
     /**
+     * Returns the absolute path to the directory on the external filesystem
+     * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
+     * Environment.getExternalStorageDirectory()} where the application can
+     * place persistent files it owns.  These files are private to the
+     * applications, and not typically visible to the user as media.
+     * 
+     * <p>This is like {@link #getFilesDir()} in that these
+     * files will be deleted when the application is uninstalled, however there
+     * are some important differences:
+     * 
+     * <ul>
+     * <li>External files are not always available: they will disappear if the
+     * user mounts the external storage on a computer or removes it.  See the
+     * APIs on {@link android.os.Environment} for information in the storage state.
+     * <li>There is no security enforced with these files.  All applications
+     * can read and write files placed here.
+     * </ul>
+     * 
+     * <p>Here is an example of typical code to manipulate a file in
+     * an application's private storage:</p>
+     * 
+     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+     * private_file}
+     *
+     * <p>If you install a non-null <var>type</var> to this function, the returned
+     * file will be a path to a sub-directory of the given type.  Though these files
+     * are not automatically scanned by the media scanner, you can explicitly
+     * add them to the media database with
+     * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[],
+     *      ScanResultListener) MediaScannerConnection.scanFile}.
+     * Note that this is not the same as
+     * {@link android.os.Environment#getExternalStoragePublicDirectory
+     * Environment.getExternalStoragePublicDirectory()}, which provides
+     * directories of media shared by all applications.  The
+     * directories returned here are
+     * owned by the application, and its contents will be removed when the
+     * application is uninstalled.  Unlike
+     * {@link android.os.Environment#getExternalStoragePublicDirectory
+     * Environment.getExternalStoragePublicDirectory()}, the directory
+     * returned here will be automatically created for you.
+     * 
+     * <p>Here is an example of typical code to manipulate a picture in
+     * an application's private storage and add it to the media database:</p>
+     * 
+     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+     * private_picture}
+     * 
+     * @param type The type of files directory to return.  May be null for
+     * the root of the files directory or one of
+     * the following Environment constants for a subdirectory:
+     * {@link android.os.Environment#DIRECTORY_MUSIC},
+     * {@link android.os.Environment#DIRECTORY_PODCASTS},
+     * {@link android.os.Environment#DIRECTORY_RINGTONES},
+     * {@link android.os.Environment#DIRECTORY_ALARMS},
+     * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
+     * {@link android.os.Environment#DIRECTORY_PICTURES}, or
+     * {@link android.os.Environment#DIRECTORY_MOVIES}.
+     * 
+     * @return Returns the path of the directory holding application files
+     * on external storage.  Returns null if external storage is not currently
+     * mounted so it could not ensure the path exists; you will need to call
+     * this method again when it is available.
+     *
+     * @see #getFilesDir
+     */
+    public abstract File getExternalFilesDir(String type);
+    
+    /**
      * Returns the absolute path to the application specific cache directory 
      * on the filesystem. These files will be ones that get deleted first when the
-     * device runs low on storage
+     * device runs low on storage.
      * There is no guarantee when these files will be deleted.
-     *
+     * 
+     * <strong>Note: you should not <em>rely</em> on the system deleting these
+     * files for you; you should always have a reasonable maximum, such as 1 MB,
+     * for the amount of space you consume with cache files, and prune those
+     * files when exceeding that space.</strong>
+     * 
      * @return Returns the path of the directory holding application cache files.
      *
      * @see #openFileOutput
@@ -407,6 +502,37 @@
     public abstract File getCacheDir();
 
     /**
+     * Returns the absolute path to the directory on the external filesystem
+     * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
+     * Environment.getExternalStorageDirectory()} where the application can
+     * place cache files it owns.
+     * 
+     * <p>This is like {@link #getCacheDir()} in that these
+     * files will be deleted when the application is uninstalled, however there
+     * are some important differences:
+     * 
+     * <ul>
+     * <li>The platform does not monitor the space available in external storage,
+     * and thus will not automatically delete these files.  Note that you should
+     * be managing the maximum space you will use for these anyway, just like
+     * with {@link #getCacheDir()}.
+     * <li>External files are not always available: they will disappear if the
+     * user mounts the external storage on a computer or removes it.  See the
+     * APIs on {@link android.os.Environment} for information in the storage state.
+     * <li>There is no security enforced with these files.  All applications
+     * can read and write files placed here.
+     * </ul>
+     *
+     * @return Returns the path of the directory holding application cache files
+     * on external storage.  Returns null if external storage is not currently
+     * mounted so it could not ensure the path exists; you will need to call
+     * this method again when it is available.
+     *
+     * @see #getCacheDir
+     */
+    public abstract File getExternalCacheDir();
+    
+    /**
      * Returns an array of strings naming the private files associated with
      * this Context's application package.
      *
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 1b34320..a447108 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -179,11 +179,21 @@
     }
     
     @Override
+    public File getExternalFilesDir(String type) {
+        return mBase.getExternalFilesDir(type);
+    }
+    
+    @Override
     public File getCacheDir() {
         return mBase.getCacheDir();
     }
 
     @Override
+    public File getExternalCacheDir() {
+        return mBase.getExternalCacheDir();
+    }
+
+    @Override
     public File getDir(String name, int mode) {
         return mBase.getDir(name, mode);
     }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 54db5e0..2c8c112 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -305,4 +305,5 @@
      */
     void updateExternalMediaStatus(boolean mounted);
 
+    String nextPackageToClean(String lastPackage);
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e3b1694..fca8588 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -622,6 +622,13 @@
     public static final String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper";
 
     /**
+     * Action to external storage service to clean out removed apps.
+     * @hide
+     */
+    public static final String ACTION_CLEAN_EXTERNAL_STORAGE
+            = "android.content.pm.CLEAN_EXTERNAL_STORAGE";
+    
+    /**
      * Determines best place to install an application: either SD or internal FLASH.
      * Tweak the algorithm for best results.
      * @param appInfo ApplicationInfo object of the package to install.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index bac55cc..b31df32 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2138,7 +2138,7 @@
                     havePerm = true;
                 }
                 if (writePermission != null) {
-                    writePermission = readPermission.intern();
+                    writePermission = writePermission.intern();
                     havePerm = true;
                 }
 
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 540f4445..5d1e7cf 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -1880,8 +1880,7 @@
                  */
                 Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
                         getPath() + "; i.e., NO space for this sql statement in cache: " +
-                        sql + ". Make sure your sql " +
-                        "statements are using prepared-sql-statement syntax with '?' for " +
+                        sql + ". Please change your sql statements to use '?' for " +
                         "bindargs, instead of using actual values");
                 
                 /* increment the number of times this warnings has been printed.
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index ef1f3be..a9831aa 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -91,6 +91,14 @@
     private static final File EXTERNAL_STORAGE_DIRECTORY
             = getDirectory("EXTERNAL_STORAGE", "/sdcard");
 
+    private static final File EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY
+            = new File (new File(getDirectory("EXTERNAL_STORAGE", "/sdcard"),
+                    "Android"), "data");
+
+    private static final File EXTERNAL_STORAGE_ANDROID_MEDIA_DIRECTORY
+            = new File (new File(getDirectory("EXTERNAL_STORAGE", "/sdcard"),
+                    "Android"), "media");
+
     private static final File DOWNLOAD_CACHE_DIRECTORY
             = getDirectory("DOWNLOAD_CACHE", "/cache");
 
@@ -102,13 +110,183 @@
     }
 
     /**
-     * Gets the Android external storage directory.
+     * Gets the Android external storage directory.  This directory may not
+     * currently be accessible if it has been mounted by the user on their
+     * computer, has been removed from the device, or some other problem has
+     * happened.  You can determine its current state with
+     * {@link #getExternalStorageState()}.
+     * 
+     * <p>Here is an example of typical code to monitor the state of
+     * external storage:</p>
+     * 
+     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+     * monitor_storage}
      */
     public static File getExternalStorageDirectory() {
         return EXTERNAL_STORAGE_DIRECTORY;
     }
 
     /**
+     * Standard directory in which to place any audio files that should be
+     * in the regular list of music for the user.
+     * This may be combined with
+     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
+     * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
+     * of directories to categories a particular audio file as more than one
+     * type.
+     */
+    public static String DIRECTORY_MUSIC = "Music";
+    
+    /**
+     * Standard directory in which to place any audio files that should be
+     * in the list of podcasts that the user can select (not as regular
+     * music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_NOTIFICATIONS},
+     * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
+     * of directories to categories a particular audio file as more than one
+     * type.
+     */
+    public static String DIRECTORY_PODCASTS = "Podcasts";
+    
+    /**
+     * Standard directory in which to place any audio files that should be
+     * in the list of ringtones that the user can select (not as regular
+     * music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, and
+     * {@link #DIRECTORY_ALARMS} as a series
+     * of directories to categories a particular audio file as more than one
+     * type.
+     */
+    public static String DIRECTORY_RINGTONES = "Ringtones";
+    
+    /**
+     * Standard directory in which to place any audio files that should be
+     * in the list of alarms that the user can select (not as regular
+     * music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
+     * and {@link #DIRECTORY_RINGTONES} as a series
+     * of directories to categories a particular audio file as more than one
+     * type.
+     */
+    public static String DIRECTORY_ALARMS = "Alarms";
+    
+    /**
+     * Standard directory in which to place any audio files that should be
+     * in the list of notifications that the user can select (not as regular
+     * music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
+     * of directories to categories a particular audio file as more than one
+     * type.
+     */
+    public static String DIRECTORY_NOTIFICATIONS = "Notifications";
+    
+    /**
+     * Standard directory in which to place pictures that are available to
+     * the user.  Note that this is primarily a convention for the top-level
+     * public directory, as the media scanner will find and collect pictures
+     * in any directory.
+     */
+    public static String DIRECTORY_PICTURES = "Pictures";
+    
+    /**
+     * Standard directory in which to place movies that are available to
+     * the user.  Note that this is primarily a convention for the top-level
+     * public directory, as the media scanner will find and collect movies
+     * in any directory.
+     */
+    public static String DIRECTORY_MOVIES = "Movies";
+    
+    /**
+     * Standard directory in which to place files that have been downloaded by
+     * the user.  Note that this is primarily a convention for the top-level
+     * public directory, you are free to download files anywhere in your own
+     * private directories.
+     */
+    public static String DIRECTORY_DOWNLOADS = "Downloads";
+    
+    /**
+     * The traditional location for pictures and videos when mounting the
+     * device as a camera.  Note that this is primarily a convention for the
+     * top-level public directory, as this convention makes no sense elsewhere.
+     */
+    public static String DIRECTORY_DCIM = "DCIM";
+    
+    /**
+     * Get a top-level public external storage directory for placing files of
+     * a particular type.  This is where the user will typically place and
+     * manage their own files, so you should be careful about what you put here
+     * to ensure you don't erase their files or get in the way of their own
+     * organization.
+     * 
+     * <p>Here is an example of typical code to manipulate a picture on
+     * the public external storage:</p>
+     * 
+     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+     * public_picture}
+     * 
+     * @param type The type of storage directory to return.  Should be one of
+     * {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS},
+     * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES},
+     * {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or
+     * {@link #DIRECTORY_DCIM}.  May not be null.
+     * 
+     * @return Returns the File path for the directory.  Note that this
+     * directory may not yet exist, so you must make sure it exists before
+     * using it such as with {@link File#mkdirs File.mkdirs()}.
+     */
+    public static File getExternalStoragePublicDirectory(String type) {
+        return new File(getExternalStorageDirectory(), type);
+    }
+
+    /**
+     * Returns the path for android-specific data on the SD card.
+     * @hide
+     */
+    public static File getExternalStorageAndroidDataDir() {
+        return EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY;
+    }
+    
+    /**
+     * Generates the raw path to an application's data
+     * @hide
+     */
+    public static File getExternalStorageAppDataDirectory(String packageName) {
+        return new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY, packageName);
+    }
+    
+    /**
+     * Generates the raw path to an application's media
+     * @hide
+     */
+    public static File getExternalStorageAppMediaDirectory(String packageName) {
+        return new File(EXTERNAL_STORAGE_ANDROID_MEDIA_DIRECTORY, packageName);
+    }
+    
+    /**
+     * Generates the path to an application's files.
+     * @hide
+     */
+    public static File getExternalStorageAppFilesDirectory(String packageName) {
+        return new File(new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY,
+                packageName), "files");
+    }
+    
+    /**
+     * Generates the path to an application's cache.
+     * @hide
+     */
+    public static File getExternalStorageAppCacheDirectory(String packageName) {
+        return new File(new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY,
+                packageName), "cache");
+    }
+    
+    /**
      * Gets the Android Download/Cache content directory.
      */
     public static File getDownloadCacheDirectory() {
@@ -173,6 +351,8 @@
      * Gets the current state of the external storage device.
      * Note: This call should be deprecated as it doesn't support
      * multiple volumes.
+     * 
+     * <p>See {@link #getExternalStorageDirectory()} for an example of its use.
      */
     public static String getExternalStorageState() {
         try {
diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java
index 3457815..7e99f38 100644
--- a/core/java/android/os/FileObserver.java
+++ b/core/java/android/os/FileObserver.java
@@ -52,73 +52,75 @@
 
     public static final int ALL_EVENTS = ACCESS | MODIFY | ATTRIB | CLOSE_WRITE 
             | CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | DELETE | CREATE
-	    | DELETE_SELF | MOVE_SELF;
+            | DELETE_SELF | MOVE_SELF;
 
     private static final String LOG_TAG = "FileObserver";
 
     private static class ObserverThread extends Thread {
-	private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>();
-	private int m_fd;
+        private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>();
+        private int m_fd;
 
-	public ObserverThread() {
-	    super("FileObserver");
-	    m_fd = init();
-	}
+        public ObserverThread() {
+            super("FileObserver");
+            m_fd = init();
+        }
 
-	public void run() {
-	    observe(m_fd);
-	}
+        public void run() {
+            observe(m_fd);
+        }
 
-	public int startWatching(String path, int mask, FileObserver observer) {
-	    int wfd = startWatching(m_fd, path, mask);
+        public int startWatching(String path, int mask, FileObserver observer) {
+            int wfd = startWatching(m_fd, path, mask);
 
-	    Integer i = new Integer(wfd);
-	    if (wfd >= 0) {
-		synchronized (m_observers) {
-		    m_observers.put(i, new WeakReference(observer));
-		}
-	    }
+            Integer i = new Integer(wfd);
+            if (wfd >= 0) {
+                synchronized (m_observers) {
+                    m_observers.put(i, new WeakReference(observer));
+                }
+            }
 
-	    return i;
-	}
+            return i;
+        }
 
-	public void stopWatching(int descriptor) {
-	    stopWatching(m_fd, descriptor);
-	}
+        public void stopWatching(int descriptor) {
+            stopWatching(m_fd, descriptor);
+        }
 
-    public void onEvent(int wfd, int mask, String path) {
-        // look up our observer, fixing up the map if necessary...
-        FileObserver observer;
+        public void onEvent(int wfd, int mask, String path) {
+            // look up our observer, fixing up the map if necessary...
+            FileObserver observer = null;
 
-        synchronized (m_observers) {
-            WeakReference weak = m_observers.get(wfd);
-            observer = (FileObserver) weak.get();
-            if (observer == null) {
-                m_observers.remove(wfd);
+            synchronized (m_observers) {
+                WeakReference weak = m_observers.get(wfd);
+                if (weak != null) {  // can happen with lots of events from a dead wfd
+                    observer = (FileObserver) weak.get();
+                    if (observer == null) {
+                        m_observers.remove(wfd);
+                    }
+                }
+            }
+
+            // ...then call out to the observer without the sync lock held
+            if (observer != null) {
+                try {
+                    observer.onEvent(mask, path);
+                } catch (Throwable throwable) {
+                    Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable);
+                }
             }
         }
 
-        // ...then call out to the observer without the sync lock held
-        if (observer != null) {
-            try {
-                observer.onEvent(mask, path);
-            } catch (Throwable throwable) {
-                Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable);
-            }
-        }
-    }
-
-	private native int init();
-	private native void observe(int fd);
-	private native int startWatching(int fd, String path, int mask);
-	private native void stopWatching(int fd, int wfd);
+        private native int init();
+        private native void observe(int fd);
+        private native int startWatching(int fd, String path, int mask);
+        private native void stopWatching(int fd, int wfd);
     }
 
     private static ObserverThread s_observerThread;
 
     static {
-	s_observerThread = new ObserverThread();
-	s_observerThread.start();
+        s_observerThread = new ObserverThread();
+        s_observerThread.start();
     }
 
     // instance
@@ -127,30 +129,30 @@
     private int m_mask;
 
     public FileObserver(String path) {
-	this(path, ALL_EVENTS);
+        this(path, ALL_EVENTS);
     }
 
     public FileObserver(String path, int mask) {
-	m_path = path;
-	m_mask = mask;
-	m_descriptor = -1;
+        m_path = path;
+        m_mask = mask;
+        m_descriptor = -1;
     }
 
     protected void finalize() {
-	stopWatching();
+        stopWatching();
     }
 
     public void startWatching() {
-	if (m_descriptor < 0) {
-	    m_descriptor = s_observerThread.startWatching(m_path, m_mask, this);
-	}
+        if (m_descriptor < 0) {
+            m_descriptor = s_observerThread.startWatching(m_path, m_mask, this);
+        }
     }
 
     public void stopWatching() {
-	if (m_descriptor >= 0) {
-	    s_observerThread.stopWatching(m_descriptor);
-	    m_descriptor = -1;
-	}
+        if (m_descriptor >= 0) {
+            s_observerThread.stopWatching(m_descriptor);
+            m_descriptor = -1;
+        }
     }
 
     public abstract void onEvent(int event, String path);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f5c465e..889985a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8281,6 +8281,9 @@
      * Cancels any animations for this view.
      */
     public void clearAnimation() {
+        if (mCurrentAnimation != null) {
+            mCurrentAnimation.cancel();
+        }
         mCurrentAnimation = null;
     }
 
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 000e4ce..ad98259 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -256,6 +256,27 @@
     }
 
     /**
+     * Cancel the animation. Cancelling an animation invokes the animation
+     * listener, if set, to notify the end of the animation.
+     * 
+     * If you cancel an animation manually, you must call {@link #reset()}
+     * before starting the animation again.
+     * 
+     * @see #reset() 
+     * @see #start() 
+     * @see #startNow() 
+     */
+    public void cancel() {
+        if (mStarted && !mEnded) {
+            if (mListener != null) mListener.onAnimationEnd(this);
+            mEnded = true;
+        }
+        // Make sure we move the animation to the end
+        mStartTime = Long.MIN_VALUE;
+        mMore = mOneMoreTime = false;
+    }
+
+    /**
      * Whether or not the animation has been initialized.
      *
      * @return Has this animation been initialized.
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 68be08d..9e9cc7e 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -816,6 +816,9 @@
         int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
                 | EditorInfo.IME_FLAG_NO_FULLSCREEN;
         switch (type) {
+            case 0: // NORMAL_TEXT_FIELD
+                imeOptions |= EditorInfo.IME_ACTION_GO;
+                break;
             case 1: // TEXT_AREA
                 single = false;
                 inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
@@ -851,6 +854,7 @@
                 imeOptions |= EditorInfo.IME_ACTION_GO;
                 break;
             default:
+                imeOptions |= EditorInfo.IME_ACTION_GO;
                 break;
         }
         setHint(null);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 2a7dc9c..8fa8f09 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3379,6 +3379,10 @@
                 text = "";
             }
             mWebTextView.setTextAndKeepSelection(text);
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null && imm.isActive(mWebTextView)) {
+                imm.restartInput(mWebTextView);
+            }
         }
         mWebTextView.requestFocus();
     }
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 299ed8a..9ef5e0b 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -103,6 +103,18 @@
         mMonthPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
         DateFormatSymbols dfs = new DateFormatSymbols();
         String[] months = dfs.getShortMonths();
+
+        /*
+         * If the user is in a locale where the month names are numeric,
+         * use just the number instead of the "month" character for
+         * consistency with the other fields.
+         */
+        if (months[0].startsWith("1")) {
+            for (int i = 0; i < months.length; i++) {
+                months[i] = String.valueOf(i + 1);
+            }
+        }
+
         mMonthPicker.setRange(1, 12, months);
         mMonthPicker.setSpeed(200);
         mMonthPicker.setOnChangeListener(new OnChangedListener() {
diff --git a/core/res/res/drawable-mdpi/ic_launcher_android.png b/core/res/res/drawable-mdpi/ic_launcher_android.png
index 855484a..6a97d5b 100644
--- a/core/res/res/drawable-mdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-mdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_def_app_icon.png b/core/res/res/drawable-mdpi/sym_def_app_icon.png
index 8be3b54..9777d11 100644
--- a/core/res/res/drawable-mdpi/sym_def_app_icon.png
+++ b/core/res/res/drawable-mdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/tests/coretests/src/android/database/DatabaseCursorTest.java b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
index fad4349..fb5a36f 100644
--- a/core/tests/coretests/src/android/database/DatabaseCursorTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
@@ -16,6 +16,7 @@
 
 package android.database;
 
+import dalvik.annotation.BrokenTest;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
@@ -373,7 +374,9 @@
         c.close();
     }
     
-    @LargeTest
+    //@LargeTest
+    @BrokenTest("Consistently times out")
+    @Suppress
     public void testLoadingThread() throws Exception {
         mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
         
@@ -398,7 +401,9 @@
         c.close();
     } 
     
-    @LargeTest
+    //@LargeTest
+    @BrokenTest("Consistently times out")
+    @Suppress
     public void testLoadingThreadClose() throws Exception {
         mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
         
@@ -450,9 +455,11 @@
         mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
         
         final int count = 36799; 
+        mDatabase.execSQL("BEGIN Transaction;");
         for (int i = 0; i < count; i++) {
             mDatabase.execSQL("INSERT INTO test (data) VALUES (" + i + ");");
         }
+        mDatabase.execSQL("COMMIT;");
 
         Cursor c = mDatabase.query("test", new String[]{"data"}, null, null, null, null, null);
         assertNotNull(c);
@@ -484,9 +491,11 @@
 
         // if cursor window size changed, adjust this value too  
         final int count = 600; // more than two fillWindow needed
+        mDatabase.execSQL("BEGIN Transaction;");
         for (int i = 0; i < count; i++) {
             mDatabase.execSQL(sql.toString());
         }
+        mDatabase.execSQL("COMMIT;");
 
         Cursor c = mDatabase.query("test", new String[]{"data"}, null, null, null, null, null);
         assertNotNull(c);
@@ -513,6 +522,7 @@
 
         // if cursor window size changed, adjust this value too  
         final int count = 600;
+        mDatabase.execSQL("BEGIN Transaction;");
         for (int i = 0; i < count; i++) {
             StringBuilder sql = new StringBuilder(2100);
             sql.append("INSERT INTO test (txt, data) VALUES ('");
@@ -522,6 +532,7 @@
             sql.append("');");
             mDatabase.execSQL(sql.toString());
         }
+        mDatabase.execSQL("COMMIT;");
 
         Cursor c = mDatabase.query("test", new String[]{"txt", "data"}, null, null, null, null, null);
         assertNotNull(c);
diff --git a/data/fonts/DroidSansFallback.ttf b/data/fonts/DroidSansFallback.ttf
old mode 100755
new mode 100644
index 61460b1..a935f16
--- a/data/fonts/DroidSansFallback.ttf
+++ b/data/fonts/DroidSansFallback.ttf
Binary files differ
diff --git a/data/fonts/DroidSansFallbackLegacy.ttf b/data/fonts/DroidSansFallbackLegacy.ttf
new file mode 100644
index 0000000..61460b1
--- /dev/null
+++ b/data/fonts/DroidSansFallbackLegacy.ttf
Binary files differ
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 965b7dd..2d6152e 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -39,7 +39,6 @@
 
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/PixelFormat.h>
-#include <ui/DisplayInfo.h>
 
 #include <pixelflinger/pixelflinger.h>
 #include <GLES/gl.h>
@@ -350,8 +349,8 @@
     mServerCblk->connected |= 1<<dpy;
     display_cblk_t* dcblk = mServerCblk->displays + dpy;
     memset(dcblk, 0, sizeof(display_cblk_t));
-    dcblk->w            = w;
-    dcblk->h            = h;
+    dcblk->w            = plane.getWidth();
+    dcblk->h            = plane.getHeight();
     dcblk->format       = f;
     dcblk->orientation  = ISurfaceComposer::eOrientationDefault;
     dcblk->xdpi         = hw.getDpiX();
@@ -621,14 +620,8 @@
             const DisplayHardware& hw(plane.displayHardware());
             volatile display_cblk_t* dcblk = mServerCblk->displays + dpy;
             dcblk->orientation = orientation;
-            if (orientation & eOrientationSwapMask) {
-                // 90 or 270 degrees orientation
-                dcblk->w = hw.getHeight();
-                dcblk->h = hw.getWidth();
-            } else {
-                dcblk->w = hw.getWidth();
-                dcblk->h = hw.getHeight();
-            }
+            dcblk->w = plane.getWidth();
+            dcblk->h = plane.getHeight();
 
             mVisibleRegionsDirty = true;
             mDirtyRegion.set(hw.bounds());
@@ -1795,13 +1788,47 @@
     return mHw ? true : false;
 }
 
-void GraphicPlane::setDisplayHardware(DisplayHardware *hw) {
-    mHw = hw;
+int GraphicPlane::getWidth() const {
+    return mWidth;
 }
 
-void GraphicPlane::setTransform(const Transform& tr) {
-    mTransform = tr;
-    mGlobalTransform = mOrientationTransform * mTransform;
+int GraphicPlane::getHeight() const {
+    return mHeight;
+}
+
+void GraphicPlane::setDisplayHardware(DisplayHardware *hw)
+{
+    mHw = hw;
+
+    // initialize the display orientation transform.
+    // it's a constant that should come from the display driver.
+    int displayOrientation = ISurfaceComposer::eOrientationDefault;
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get("ro.sf.hwrotation", property, NULL) > 0) {
+        //displayOrientation
+        switch (atoi(property)) {
+        case 90:
+            displayOrientation = ISurfaceComposer::eOrientation90;
+            break;
+        case 270:
+            displayOrientation = ISurfaceComposer::eOrientation270;
+            break;
+        }
+    }
+
+    const float w = hw->getWidth();
+    const float h = hw->getHeight();
+    GraphicPlane::orientationToTransfrom(displayOrientation, w, h,
+            &mDisplayTransform);
+    if (displayOrientation & ISurfaceComposer::eOrientationSwapMask) {
+        mDisplayWidth = h;
+        mDisplayHeight = w;
+    } else {
+        mDisplayWidth = w;
+        mDisplayHeight = h;
+    }
+
+    setOrientation(ISurfaceComposer::eOrientationDefault);
 }
 
 status_t GraphicPlane::orientationToTransfrom(
@@ -1810,8 +1837,9 @@
     float a, b, c, d, x, y;
     switch (orientation) {
     case ISurfaceComposer::eOrientationDefault:
-        a=1; b=0; c=0; d=1; x=0; y=0;
-        break;
+        // make sure the default orientation is optimal
+        tr->reset();
+        return NO_ERROR;
     case ISurfaceComposer::eOrientation90:
         a=0; b=-1; c=1; d=0; x=w; y=0;
         break;
@@ -1831,20 +1859,16 @@
 
 status_t GraphicPlane::setOrientation(int orientation)
 {
-    const DisplayHardware& hw(displayHardware());
-    const float w = hw.getWidth();
-    const float h = hw.getHeight();
-
-    if (orientation == ISurfaceComposer::eOrientationDefault) {
-        // make sure the default orientation is optimal
-        mOrientationTransform.reset();
-        mOrientation = orientation;
-        mGlobalTransform = mTransform;
-        return NO_ERROR;
-    }
-
     // If the rotation can be handled in hardware, this is where
     // the magic should happen.
+
+    const DisplayHardware& hw(displayHardware());
+    const float w = mDisplayWidth;
+    const float h = mDisplayHeight;
+    mWidth = int(w);
+    mHeight = int(h);
+
+    Transform orientationTransform;
     if (UNLIKELY(orientation == 42)) {
         float a, b, c, d, x, y;
         const float r = (3.14159265f / 180.0f) * 42.0f;
@@ -1853,14 +1877,18 @@
         a=co; b=-si; c=si; d=co;
         x = si*(h*0.5f) + (1-co)*(w*0.5f);
         y =-si*(w*0.5f) + (1-co)*(h*0.5f);
-        mOrientationTransform.set(a, b, c, d);
-        mOrientationTransform.set(x, y);
+        orientationTransform.set(a, b, c, d);
+        orientationTransform.set(x, y);
     } else {
         GraphicPlane::orientationToTransfrom(orientation, w, h,
-                &mOrientationTransform);
+                &orientationTransform);
+        if (orientation & ISurfaceComposer::eOrientationSwapMask) {
+            mWidth = int(h);
+            mHeight = int(w);
+        }
     }
     mOrientation = orientation;
-    mGlobalTransform = mOrientationTransform * mTransform;
+    mGlobalTransform = mDisplayTransform * orientationTransform;
     return NO_ERROR;
 }
 
diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h
index c0ab73d..2b7820c 100644
--- a/libs/surfaceflinger/SurfaceFlinger.h
+++ b/libs/surfaceflinger/SurfaceFlinger.h
@@ -116,9 +116,10 @@
         bool                    initialized() const;
 
         void                    setDisplayHardware(DisplayHardware *);
-        void                    setTransform(const Transform& tr);
         status_t                setOrientation(int orientation);
         int                     getOrientation() const { return mOrientation; }
+        int                     getWidth() const;
+        int                     getHeight() const;
 
         const DisplayHardware&  displayHardware() const;
         const Transform&        transform() const;
@@ -129,10 +130,13 @@
         GraphicPlane            operator = (const GraphicPlane&);
 
         DisplayHardware*        mHw;
-        Transform               mTransform;
-        Transform               mOrientationTransform;
         Transform               mGlobalTransform;
+        Transform               mDisplayTransform;
         int                     mOrientation;
+        float                   mDisplayWidth;
+        float                   mDisplayHeight;
+        int                     mWidth;
+        int                     mHeight;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java
index d2596b8..65b67a1 100644
--- a/media/java/android/media/MediaScannerConnection.java
+++ b/media/java/android/media/MediaScannerConnection.java
@@ -57,17 +57,32 @@
     };
 
     /**
+     * Interface for notifying clients of the result of scanning a
+     * requested media file.
+     */
+    public interface ScanResultListener {
+        /**
+         * Called to notify the client when the media scanner has finished
+         * scanning a file.
+         * @param path the path to the file that has been scanned.
+         * @param uri the Uri for the file if the scanning operation succeeded 
+         * and the file was added to the media database, or null if scanning failed. 
+         */    
+        public void onScanCompleted(String path, Uri uri);
+    }
+    
+    /**
      * An interface for notifying clients of MediaScannerConnection
      * when a connection to the MediaScanner service has been established
      * and when the scanning of a file has completed.
      */
-    public interface MediaScannerConnectionClient {
+    public interface MediaScannerConnectionClient extends ScanResultListener {
         /**
          * Called to notify the client when a connection to the 
          * MediaScanner service has been established.
          */    
         public void onMediaScannerConnected();
-
+        
         /**
          * Called to notify the client when the media scanner has finished
          * scanning a file.
@@ -136,11 +151,12 @@
 
     /**
      * Requests the media scanner to scan a file.
+     * Success or failure of the scanning operation cannot be determined until 
+     * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called.
+     * 
      * @param path the path to the file to be scanned.
      * @param mimeType  an optional mimeType for the file.
      * If mimeType is null, then the mimeType will be inferred from the file extension.
-     * Success or failure of the scanning operation cannot be determined until 
-     * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called.
      */
      public void scanFile(String path, String mimeType) {
         synchronized (this) {
@@ -159,7 +175,67 @@
             }
         }
     }
-
+    
+    static class ClientProxy implements MediaScannerConnectionClient {
+        final String[] mPaths;
+        final String[] mMimeTypes;
+        final ScanResultListener mClient;
+        MediaScannerConnection mConnection;
+        int mNextPath;
+        
+        ClientProxy(String[] paths, String[] mimeTypes, ScanResultListener client) {
+            mPaths = paths;
+            mMimeTypes = mimeTypes;
+            mClient = client;
+        }
+        
+        public void onMediaScannerConnected() {
+            scanNextPath();
+        }
+        
+        public void onScanCompleted(String path, Uri uri) {
+            if (mClient != null) {
+                mClient.onScanCompleted(path, uri);
+            }
+            scanNextPath();
+        }
+        
+        void scanNextPath() {
+            if (mNextPath >= mPaths.length) {
+                mConnection.disconnect();
+                return;
+            }
+            String mimeType = mMimeTypes != null ? mMimeTypes[mNextPath] : null;
+            mConnection.scanFile(mPaths[mNextPath], mimeType);
+            mNextPath++;
+        }
+    }
+    
+    /**
+     * Convenience for constructing a {@link MediaScannerConnection}, calling
+     * {@link #connect} on it, and calling {@link #scanFile} with the given
+     * <var>path</var> and <var>mimeType</var> when the connection is
+     * established.
+     * @param context The caller's Context, required for establishing a connection to
+     * the media scanner service.
+     * Success or failure of the scanning operation cannot be determined until 
+     * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called.
+     * @param paths Array of paths to be scanned.
+     * @param mimeTypes Optional array of MIME types for each path.
+     * If mimeType is null, then the mimeType will be inferred from the file extension.
+     * @param callback Optional callback through which you can receive the
+     * scanned URI and MIME type; If null, the file will be scanned but
+     * you will not get a result back.
+     * @see scanFile(String, String)
+     */
+    public static void scanFile(Context context, String[] paths, String[] mimeTypes,
+            ScanResultListener callback) {
+        ClientProxy client = new ClientProxy(paths, mimeTypes, callback);
+        MediaScannerConnection connection = new MediaScannerConnection(context, client);
+        client.mConnection = connection;
+        connection.connect();
+    }
+     
     /**
      * Part of the ServiceConnection interface.  Do not call.
      */
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index d25f7f6..a13b242 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -111,6 +111,7 @@
 AwesomePlayer::AwesomePlayer()
     : mTimeSource(NULL),
       mAudioPlayer(NULL),
+      mFlags(0),
       mLastVideoBuffer(NULL),
       mVideoBuffer(NULL) {
     CHECK_EQ(mClient.connect(), OK);
@@ -167,23 +168,17 @@
 
     reset_l();
 
-    sp<DataSource> dataSource = DataSource::CreateFromURI(uri, headers);
+    mUri = uri;
 
-    if (dataSource == NULL) {
-        return UNKNOWN_ERROR;
+    if (headers) {
+        mUriHeaders = *headers;
     }
 
-    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+    // The actual work will be done during preparation in the call to
+    // ::finishSetDataSource_l to avoid blocking the calling thread in
+    // setDataSource for any significant time.
 
-    if (extractor == NULL) {
-        return UNKNOWN_ERROR;
-    }
-
-    if (dataSource->flags() & DataSource::kWantsPrefetching) {
-        mPrefetcher = new Prefetcher;
-    }
-
-    return setDataSource_l(extractor);
+    return OK;
 }
 
 status_t AwesomePlayer::setDataSource(
@@ -242,6 +237,10 @@
 }
 
 void AwesomePlayer::reset_l() {
+    while (mFlags & PREPARING) {
+        mPreparedCondition.wait(mLock);
+    }
+
     cancelPlayerEvents();
 
     mVideoRenderer.clear();
@@ -290,6 +289,9 @@
     mSeekTimeUs = 0;
 
     mPrefetcher.clear();
+
+    mUri.setTo("");
+    mUriHeaders.clear();
 }
 
 void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
@@ -350,6 +352,14 @@
         return OK;
     }
 
+    if (!(mFlags & PREPARED)) {
+        status_t err = prepare_l();
+
+        if (err != OK) {
+            return err;
+        }
+    }
+
     mFlags |= PLAYING;
     mFlags |= FIRST_FRAME;
 
@@ -725,7 +735,7 @@
 
     if (latenessUs > 40000) {
         // We're more than 40ms late.
-        LOGI("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6);
+        LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6);
 
         mVideoBuffer->release();
         mVideoBuffer = NULL;
@@ -815,30 +825,49 @@
 
 status_t AwesomePlayer::prepare() {
     Mutex::Autolock autoLock(mLock);
+    return prepare_l();
+}
 
+status_t AwesomePlayer::prepare_l() {
+    if (mFlags & PREPARED) {
+        return OK;
+    }
+
+    if (mFlags & PREPARING) {
+        return UNKNOWN_ERROR;
+    }
+
+    mIsAsyncPrepare = false;
     status_t err = prepareAsync_l();
 
     if (err != OK) {
         return err;
     }
 
-    while (mAsyncPrepareEvent != NULL) {
+    while (mFlags & PREPARING) {
         mPreparedCondition.wait(mLock);
     }
 
-    return OK;
+    return mPrepareResult;
 }
 
 status_t AwesomePlayer::prepareAsync() {
     Mutex::Autolock autoLock(mLock);
+
+    if (mFlags & PREPARING) {
+        return UNKNOWN_ERROR;  // async prepare already pending
+    }
+
+    mIsAsyncPrepare = true;
     return prepareAsync_l();
 }
 
 status_t AwesomePlayer::prepareAsync_l() {
-    if (mAsyncPrepareEvent != NULL) {
-        return UNKNOWN_ERROR;  // async prepare already pending.
+    if (mFlags & PREPARING) {
+        return UNKNOWN_ERROR;  // async prepare already pending
     }
 
+    mFlags |= PREPARING;
     mAsyncPrepareEvent = new AwesomeEvent(
             this, &AwesomePlayer::onPrepareAsyncEvent);
 
@@ -847,7 +876,49 @@
     return OK;
 }
 
+status_t AwesomePlayer::finishSetDataSource_l() {
+    sp<DataSource> dataSource =
+        DataSource::CreateFromURI(mUri.string(), &mUriHeaders);
+
+    if (dataSource == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+
+    if (extractor == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    if (dataSource->flags() & DataSource::kWantsPrefetching) {
+        mPrefetcher = new Prefetcher;
+    }
+
+    return setDataSource_l(extractor);
+}
+
 void AwesomePlayer::onPrepareAsyncEvent() {
+    {
+        Mutex::Autolock autoLock(mLock);
+
+        if (mUri.size() > 0) {
+            status_t err = finishSetDataSource_l();
+
+            if (err != OK) {
+                if (mIsAsyncPrepare) {
+                    notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+                }
+
+                mPrepareResult = err;
+                mFlags &= ~PREPARING;
+                mAsyncPrepareEvent = NULL;
+                mPreparedCondition.broadcast();
+
+                return;
+            }
+        }
+    }
+
     sp<Prefetcher> prefetcher;
 
     {
@@ -861,16 +932,21 @@
 
     Mutex::Autolock autoLock(mLock);
 
-    if (mVideoWidth < 0 || mVideoHeight < 0) {
-        notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
-    } else {
-        notifyListener_l(MEDIA_SET_VIDEO_SIZE, mVideoWidth, mVideoHeight);
+    if (mIsAsyncPrepare) {
+        if (mVideoWidth < 0 || mVideoHeight < 0) {
+            notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
+        } else {
+            notifyListener_l(MEDIA_SET_VIDEO_SIZE, mVideoWidth, mVideoHeight);
+        }
+
+        notifyListener_l(MEDIA_PREPARED);
     }
 
-    notifyListener_l(MEDIA_PREPARED);
-
+    mPrepareResult = OK;
+    mFlags &= ~PREPARING;
+    mFlags |= PREPARED;
     mAsyncPrepareEvent = NULL;
-    mPreparedCondition.signal();
+    mPreparedCondition.broadcast();
 }
 
 }  // namespace android
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 6274a6c..4458006 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1070,6 +1070,11 @@
             metadataKey = kKeyGenre;
             break;
         }
+        case FOURCC(0xa9, 'g', 'e', 'n'):
+        {
+            metadataKey = kKeyGenre;
+            break;
+        }
         case FOURCC('t', 'r', 'k', 'n'):
         {
             if (size == 16 && flags == 0) {
@@ -1077,11 +1082,22 @@
                 sprintf(tmp, "%d/%d",
                         (int)buffer[size - 5], (int)buffer[size - 3]);
 
-                printf("track: %s\n", tmp);
                 mFileMetaData->setCString(kKeyCDTrackNumber, tmp);
             }
             break;
         }
+        case FOURCC('d', 'i', 's', 'k'):
+        {
+            if (size == 14 && flags == 0) {
+                char tmp[16];
+                sprintf(tmp, "%d/%d",
+                        (int)buffer[size - 3], (int)buffer[size - 1]);
+
+                mFileMetaData->setCString(kKeyDiscNumber, tmp);
+            }
+            break;
+        }
+
         default:
             break;
     }
@@ -1093,11 +1109,25 @@
                     buffer + 8, size - 8);
         } else if (metadataKey == kKeyGenre) {
             if (flags == 0) {
-                // uint8_t
+                // uint8_t genre code, iTunes genre codes are
+                // the standard id3 codes, except they start
+                // at 1 instead of 0 (e.g. Pop is 14, not 13)
+                // We use standard id3 numbering, so subtract 1.
+                int genrecode = (int)buffer[size - 1];
+                genrecode--;
+                if (genrecode < 0) {
+                    genrecode = 255; // reserved for 'unknown genre'
+                }
                 char genre[10];
-                sprintf(genre, "%d", (int)buffer[size - 1]);
+                sprintf(genre, "%d", genrecode);
 
                 mFileMetaData->setCString(metadataKey, genre);
+            } else if (flags == 1) {
+                // custom genre string
+                buffer[size] = '\0';
+
+                mFileMetaData->setCString(
+                        metadataKey, (const char *)buffer + 8);
             }
         } else {
             buffer[size] = '\0';
@@ -1198,7 +1228,7 @@
     CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate));
 
     if (prevSampleRate != sampleRate) {
-        LOGW("mpeg4 audio sample rate different from previous setting. "
+        LOGV("mpeg4 audio sample rate different from previous setting. "
              "was: %d, now: %d", prevSampleRate, sampleRate);
     }
 
@@ -1208,7 +1238,7 @@
     CHECK(mLastTrack->meta->findInt32(kKeyChannelCount, &prevChannelCount));
 
     if (prevChannelCount != numChannels) {
-        LOGW("mpeg4 audio channel count different from previous setting. "
+        LOGV("mpeg4 audio channel count different from previous setting. "
              "was: %d, now: %d", prevChannelCount, numChannels);
     }
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 0355a82..c6c6f21 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -139,6 +139,7 @@
 
 #define CODEC_LOGI(x, ...) LOGI("[%s] "x, mComponentName, ##__VA_ARGS__)
 #define CODEC_LOGV(x, ...) LOGV("[%s] "x, mComponentName, ##__VA_ARGS__)
+#define CODEC_LOGE(x, ...) LOGE("[%s] "x, mComponentName, ##__VA_ARGS__)
 
 struct OMXCodecObserver : public BnOMXObserver {
     OMXCodecObserver() {
@@ -1284,7 +1285,8 @@
                 CHECK_EQ(err, OK);
 
                 buffers->removeAt(i);
-            } else if (mPortStatus[kPortIndexInput] != SHUTTING_DOWN) {
+            } else if (mState != ERROR
+                    && mPortStatus[kPortIndexInput] != SHUTTING_DOWN) {
                 CHECK_EQ(mPortStatus[kPortIndexInput], ENABLED);
                 drainInputBuffer(&buffers->editItemAt(i));
             }
@@ -1930,10 +1932,17 @@
         srcLength = srcBuffer->range_length();
 
         if (info->mSize < srcLength) {
-            LOGE("info->mSize = %d, srcLength = %d",
+            CODEC_LOGE(
+                 "Codec's input buffers are too small to accomodate "
+                 "buffer read from source (info->mSize = %d, srcLength = %d)",
                  info->mSize, srcLength);
+
+            srcBuffer->release();
+            srcBuffer = NULL;
+
+            setState(ERROR);
+            return;
         }
-        CHECK(info->mSize >= srcLength);
         memcpy(info->mData,
                (const uint8_t *)srcBuffer->data() + srcBuffer->range_offset(),
                srcLength);
@@ -2250,7 +2259,7 @@
 }
 
 status_t OMXCodec::stop() {
-    CODEC_LOGV("stop");
+    CODEC_LOGV("stop mState=%d", mState);
 
     Mutex::Autolock autoLock(mLock);
 
@@ -2309,6 +2318,8 @@
 
     mSource->stop();
 
+    CODEC_LOGV("stopped");
+
     return OK;
 }
 
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 651b910..a19784b 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -58,6 +58,7 @@
     void reset();
 
     status_t prepare();
+    status_t prepare_l();
     status_t prepareAsync();
     status_t prepareAsync_l();
 
@@ -84,6 +85,8 @@
         PLAYING     = 1,
         LOOPING     = 2,
         FIRST_FRAME = 4,
+        PREPARING   = 8,
+        PREPARED    = 16,
     };
 
     mutable Mutex mLock;
@@ -97,6 +100,9 @@
 
     TimeSource *mTimeSource;
 
+    String8 mUri;
+    KeyedVector<String8, String8> mUriHeaders;
+
     sp<MediaSource> mVideoSource;
     sp<AwesomeRenderer> mVideoRenderer;
 
@@ -127,6 +133,8 @@
 
     sp<TimedEventQueue::Event> mAsyncPrepareEvent;
     Condition mPreparedCondition;
+    bool mIsAsyncPrepare;
+    status_t mPrepareResult;
 
     void postVideoEvent_l(int64_t delayUs = -1);
     void postBufferingEvent_l();
@@ -158,6 +166,7 @@
     void onBufferingUpdate();
     void onCheckAudioStatus();
     void onPrepareAsyncEvent();
+    status_t finishSetDataSource_l();
 
     AwesomePlayer(const AwesomePlayer &);
     AwesomePlayer &operator=(const AwesomePlayer &);
diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml
index 3d72017..5ec72df 100755
--- a/packages/DefaultContainerService/AndroidManifest.xml
+++ b/packages/DefaultContainerService/AndroidManifest.xml
@@ -5,9 +5,9 @@
     <uses-permission android:name="android.permission.ASEC_CREATE"/>
     <uses-permission android:name="android.permission.ASEC_DESTROY"/>
     <uses-permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
-    <application android:process="def.container.service"
-                 android:label="@string/service_name">
+    <application android:label="@string/service_name">
 
         <service android:name=".DefaultContainerService"
                  android:enabled="true"
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index fecd366..c418ccb 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -3,8 +3,11 @@
 import com.android.internal.app.IMediaContainerService;
 
 import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Debug;
+import android.os.Environment;
 import android.os.IBinder;
 import android.os.storage.IMountService;
 import android.os.storage.StorageResultCode;
@@ -12,6 +15,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.app.IntentService;
 import android.app.Service;
 import android.util.Log;
 
@@ -25,7 +29,6 @@
 
 import android.os.FileUtils;
 
-
 /*
  * This service copies a downloaded apk to a file passed in as
  * a ParcelFileDescriptor or to a newly created container specified
@@ -33,7 +36,7 @@
  * based on its uid. This process also needs the ACCESS_DOWNLOAD_MANAGER
  * permission to access apks downloaded via the download manager.
  */
-public class DefaultContainerService extends Service {
+public class DefaultContainerService extends IntentService {
     private static final String TAG = "DefContainer";
     private static final boolean localLOGV = false;
 
@@ -78,6 +81,40 @@
         }
     };
 
+    public DefaultContainerService() {
+        super("DefaultContainerService");
+        setIntentRedelivery(true);
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        if (PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE.equals(intent.getAction())) {
+            IPackageManager pm = IPackageManager.Stub.asInterface(
+                    ServiceManager.getService("package"));
+            String pkg = null;
+            try {
+                while ((pkg=pm.nextPackageToClean(pkg)) != null) {
+                    eraseFiles(Environment.getExternalStorageAppDataDirectory(pkg));
+                    eraseFiles(Environment.getExternalStorageAppMediaDirectory(pkg));
+                }
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    void eraseFiles(File path) {
+        if (path.isDirectory()) {
+            String[] files = path.list();
+            if (files != null) {
+                for (String file : files) {
+                    eraseFiles(new File(path, file));
+                }
+            }
+        }
+        //Log.i(TAG, "Deleting: " + path);
+        path.delete();
+    }
+    
     public IBinder onBind(Intent intent) {
         return mBinder;
     }
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index e4ee4ae..1625d9f 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -320,14 +320,18 @@
                 }
                 tag = parser.getName();
                 if ("admin".equals(tag)) {
-                    DeviceAdminInfo dai = findAdmin(
-                            ComponentName.unflattenFromString(
-                                    parser.getAttributeValue(null, "name")));
-                    if (dai != null) {
-                        ActiveAdmin ap = new ActiveAdmin(dai);
-                        ap.readFromXml(parser);
-                        mAdminMap.put(ap.info.getComponent(), ap);
-                        mAdminList.add(ap);
+                    String name = parser.getAttributeValue(null, "name");
+                    try {
+                        DeviceAdminInfo dai = findAdmin(
+                                ComponentName.unflattenFromString(name));
+                        if (dai != null) {
+                            ActiveAdmin ap = new ActiveAdmin(dai);
+                            ap.readFromXml(parser);
+                            mAdminMap.put(ap.info.getComponent(), ap);
+                            mAdminList.add(ap);
+                        }
+                    } catch (RuntimeException e) {
+                        Log.w(TAG, "Failed loading admin " + name, e);
                     }
                 } else if ("failed-password-attempts".equals(tag)) {
                     mFailedPasswordAttempts = Integer.parseInt(
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 6de2eff..8d45033 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -177,8 +177,12 @@
         String vs = getVolumeState(path);
         if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
             mUmsEnabling = enable; // Override for isUsbMassStorageEnabled()
-            doUnmountVolume(path);
+            int rc = doUnmountVolume(path);
             mUmsEnabling = false; // Clear override
+            if (rc != StorageResultCode.OperationSucceeded) {
+                Log.e(TAG, String.format("Failed to unmount before enabling UMS (%d)", rc));
+                return rc;
+            }
         }
 
         try {
@@ -517,7 +521,7 @@
     }
 
     private int doUnmountVolume(String path) {
-        if (getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
+        if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
             return VoldResponseCode.OpFailedVolNotMounted;
         }
 
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index c99480f..b1e5d32 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -148,6 +148,10 @@
     static final int SCAN_NEW_INSTALL = 1<<4;
     static final int SCAN_NO_PATHS = 1<<5;
 
+    static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
+            "com.android.defcontainer",
+            "com.android.defcontainer.DefaultContainerService");
+    
     final HandlerThread mHandlerThread = new HandlerThread("PackageManager",
             Process.THREAD_PRIORITY_BACKGROUND);
     final PackageHandler mHandler;
@@ -298,6 +302,7 @@
     static final int END_COPY = 4;
     static final int INIT_COPY = 5;
     static final int MCS_UNBIND = 6;
+    static final int START_CLEANING_PACKAGE = 7;
     // Delay time in millisecs
     static final int BROADCAST_DELAY = 10 * 1000;
     private ServiceConnection mDefContainerConn = new ServiceConnection() {
@@ -328,9 +333,7 @@
                 case INIT_COPY: {
                     InstallArgs args = (InstallArgs) msg.obj;
                     args.createCopyFile();
-                    Intent service = new Intent().setComponent(new ComponentName(
-                            "com.android.defcontainer",
-                            "com.android.defcontainer.DefaultContainerService"));
+                    Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
                     if (mContainerService != null) {
                         // No need to add to pending list. Use remote stub directly
                         handleStartCopy(args);
@@ -405,6 +408,15 @@
                     }
                     break;
                 }
+                case START_CLEANING_PACKAGE: {
+                    String packageName = (String)msg.obj;
+                    synchronized (mPackages) {
+                        if (!mSettings.mPackagesToBeCleaned.contains(packageName)) {
+                            mSettings.mPackagesToBeCleaned.add(packageName);
+                        }
+                    }
+                    startCleaningPackages();
+                } break;
             }
         }
 
@@ -2823,6 +2835,9 @@
             mSettings.insertPackageSettingLP(pkgSetting, pkg);
             // Add the new setting to mPackages
             mPackages.put(pkg.applicationInfo.packageName, pkg);
+            // Make sure we don't accidentally delete its data.
+            mSettings.mPackagesToBeCleaned.remove(pkgName);
+            
             int N = pkg.providers.size();
             StringBuilder r = null;
             int i;
@@ -4018,7 +4033,47 @@
             }
         }
     }
+    
+    public String nextPackageToClean(String lastPackage) {
+        synchronized (mPackages) {
+            if (!mMediaMounted) {
+                // If the external storage is no longer mounted at this point,
+                // the caller may not have been able to delete all of this
+                // packages files and can not delete any more.  Bail.
+                return null;
+            }
+            if (lastPackage != null) {
+                mSettings.mPackagesToBeCleaned.remove(lastPackage);
+            }
+            return mSettings.mPackagesToBeCleaned.size() > 0
+                    ? mSettings.mPackagesToBeCleaned.get(0) : null;
+        }
+    }
 
+    void schedulePackageCleaning(String packageName) {
+        mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE, packageName));
+    }
+    
+    void startCleaningPackages() {
+        synchronized (mPackages) {
+            if (!mMediaMounted) {
+                return;
+            }
+            if (mSettings.mPackagesToBeCleaned.size() <= 0) {
+                return;
+            }
+        }
+        Intent intent = new Intent(PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE);
+        intent.setComponent(DEFAULT_CONTAINER_COMPONENT);
+        IActivityManager am = ActivityManagerNative.getDefault();
+        if (am != null) {
+            try {
+                am.startService(null, intent, null);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+    
     private final class AppDirObserver extends FileObserver {
         public AppDirObserver(String path, int mask, boolean isrom) {
             super(path, mask);
@@ -5224,7 +5279,6 @@
      *  persisting settings for later use
      *  sending a broadcast if necessary
      */
-
     private boolean deletePackageX(String packageName, boolean sendBroadCast,
                                    boolean deleteCodeAndResources, int flags) {
         PackageRemovedInfo info = new PackageRemovedInfo();
@@ -5316,6 +5370,7 @@
                 File dataDir = new File(pkg.applicationInfo.dataDir);
                 dataDir.delete();
             }
+            schedulePackageCleaning(packageName);
             synchronized (mPackages) {
                 if (outInfo != null) {
                     outInfo.removedUid = mSettings.removePackageLP(packageName);
@@ -5328,7 +5383,7 @@
                 mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids);
             }
             // Save settings now
-            mSettings.writeLP ();
+            mSettings.writeLP();
         }
     }
 
@@ -6817,6 +6872,10 @@
         final HashMap<String, BasePermission> mPermissionTrees =
                 new HashMap<String, BasePermission>();
 
+        // Packages that have been uninstalled and still need their external
+        // storage data deleted.
+        final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>();
+        
         private final StringBuilder mReadMessages = new StringBuilder();
 
         private static final class PendingPackage extends PackageSettingBase {
@@ -7380,6 +7439,14 @@
                     serializer.endTag(null, "shared-user");
                 }
 
+                if (mPackagesToBeCleaned.size() > 0) {
+                    for (int i=0; i<mPackagesToBeCleaned.size(); i++) {
+                        serializer.startTag(null, "cleaning-package");
+                        serializer.attribute(null, "name", mPackagesToBeCleaned.get(i));
+                        serializer.endTag(null, "cleaning-package");
+                    }
+                }
+                
                 serializer.endTag(null, "packages");
 
                 serializer.endDocument();
@@ -7412,7 +7479,7 @@
         }
 
         void writeDisabledSysPackage(XmlSerializer serializer, final PackageSetting pkg)
-        throws java.io.IOException {
+                throws java.io.IOException {
             serializer.startTag(null, "updated-package");
             serializer.attribute(null, "name", pkg.name);
             serializer.attribute(null, "codePath", pkg.codePathString);
@@ -7450,7 +7517,7 @@
         }
 
         void writePackage(XmlSerializer serializer, final PackageSetting pkg)
-        throws java.io.IOException {
+                throws java.io.IOException {
             serializer.startTag(null, "package");
             serializer.attribute(null, "name", pkg.name);
             serializer.attribute(null, "codePath", pkg.codePathString);
@@ -7640,6 +7707,11 @@
                         readPreferredActivitiesLP(parser);
                     } else if(tagName.equals("updated-package")) {
                         readDisabledSysPackageLP(parser);
+                    } else if (tagName.equals("cleaning-package")) {
+                        String name = parser.getAttributeValue(null, "name");
+                        if (name != null) {
+                            mPackagesToBeCleaned.add(name);
+                        }
                     } else {
                         Log.w(TAG, "Unknown element under <packages>: "
                               + parser.getName());
@@ -7765,7 +7837,7 @@
         }
 
         private void readDisabledSysPackageLP(XmlPullParser parser)
-        throws XmlPullParserException, IOException {
+                throws XmlPullParserException, IOException {
             String name = parser.getAttributeValue(null, "name");
             String codePathStr = parser.getAttributeValue(null, "codePath");
             String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
@@ -8435,19 +8507,21 @@
    }
 
    public void updateExternalMediaStatus(final boolean mediaStatus) {
-       if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
-               mediaStatus+", mMediaMounted=" + mMediaMounted);
-       if (mediaStatus == mMediaMounted) {
-           return;
-       }
-       mMediaMounted = mediaStatus;
-        // Queue up an async operation since the package installation may take a little while.
-       mHandler.post(new Runnable() {
-           public void run() {
-               mHandler.removeCallbacks(this);
-               updateExternalMediaStatusInner(mediaStatus);
+       synchronized (mPackages) {
+           if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
+                   mediaStatus+", mMediaMounted=" + mMediaMounted);
+           if (mediaStatus == mMediaMounted) {
+               return;
            }
-       });
+           mMediaMounted = mediaStatus;
+            // Queue up an async operation since the package installation may take a little while.
+           mHandler.post(new Runnable() {
+               public void run() {
+                   mHandler.removeCallbacks(this);
+                   updateExternalMediaStatusInner(mediaStatus);
+               }
+           });
+       }
    }
 
    void updateExternalMediaStatusInner(boolean mediaStatus) {
@@ -8505,6 +8579,7 @@
        if (mediaStatus) {
            if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
            loadMediaPackages(processCids, uidArr);
+           startCleaningPackages();
        } else {
            if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
            unloadMediaPackages(processCids, uidArr);
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 88aadbd..b57e543 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5465,7 +5465,6 @@
                 curFocus = mCurrentFocus;
                 // cache the paused state at ctor time as well
                 if (theFocus == null || theFocus.mToken == null) {
-                    Log.i(TAG, "focus " + theFocus + " mToken is null at event dispatch!");
                     focusPaused = false;
                 } else {
                     focusPaused = theFocus.mToken.paused;
@@ -5478,7 +5477,7 @@
                         + " fin=" + finished + " gfw=" + gotFirstWindow
                         + " ed=" + eventDispatching + " tts=" + timeToSwitch
                         + " wf=" + wasFrozen + " fp=" + focusPaused
-                        + " mcf=" + mCurrentFocus + "}}";
+                        + " mcf=" + curFocus + "}}";
             }
         };
         private DispatchState mDispatchState = null;
@@ -5651,10 +5650,11 @@
                     synchronized (this) {
                         Log.w(TAG, "Key dispatching timed out sending to " +
                               (targetWin != null ? targetWin.mAttrs.getTitle()
-                              : "<null>"));
+                              : "<null>: no window ready for key dispatch"));
                         // NOSHIP debugging
-                        Log.w(TAG, "Dispatch state: " + mDispatchState);
-                        Log.w(TAG, "Current state:  " + new DispatchState(nextKey, targetWin));
+                        Log.w(TAG, "Previous dispatch state: " + mDispatchState);
+                        Log.w(TAG, "Current dispatch state: " +
+                                new DispatchState(nextKey, targetWin));
                         // END NOSHIP
                         //dump();
                         if (targetWin != null) {
diff --git a/test-runner/android/test/mock/MockContext.java b/test-runner/android/test/mock/MockContext.java
index 57b22f8..ffd757c 100644
--- a/test-runner/android/test/mock/MockContext.java
+++ b/test-runner/android/test/mock/MockContext.java
@@ -158,11 +158,21 @@
     }
 
     @Override
+    public File getExternalFilesDir(String type) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public File getCacheDir() {
         throw new UnsupportedOperationException();
     }
 
     @Override
+    public File getExternalCacheDir() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public File getDir(String name, int mode) {
         throw new UnsupportedOperationException();
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
index b670eee..57b5d4e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -928,6 +928,12 @@
     }
 
     @Override
+    public File getExternalCacheDir() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
     public ContentResolver getContentResolver() {
         if (mContentResolver == null) {
             mContentResolver = new BridgeContentResolver(this);
@@ -960,6 +966,12 @@
     }
 
     @Override
+    public File getExternalFilesDir(String type) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
     public String getPackageCodePath() {
         // TODO Auto-generated method stub
         return null;