Merge "Guard against null Context in attachInfo()." into klp-dev
diff --git a/api/current.txt b/api/current.txt
index e96eec2..e50e57f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -20,6 +20,7 @@
     field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
     field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
     field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
+    field public static final java.lang.String BIND_CALL_SERVICE = "android.permission.BIND_CALL_SERVICE";
     field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
     field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
     field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
@@ -5986,6 +5987,7 @@
     field public static final int MODE_PRIVATE = 0; // 0x0
     field public static final deprecated int MODE_WORLD_READABLE = 1; // 0x1
     field public static final deprecated int MODE_WORLD_WRITEABLE = 2; // 0x2
+    field public static final java.lang.String NETWORKMANAGEMENT_SERVICE = "network_management";
     field public static final java.lang.String NFC_SERVICE = "nfc";
     field public static final java.lang.String NOTIFICATION_SERVICE = "notification";
     field public static final java.lang.String NSD_SERVICE = "servicediscovery";
@@ -12769,6 +12771,25 @@
     field public static final java.lang.String KEY_WIDTH = "width";
   }
 
+  public abstract class MediaMetadataEditor {
+    method public synchronized void addEditableKey(int);
+    method public abstract void apply();
+    method public synchronized void clear();
+    method public synchronized android.graphics.Bitmap getBitmap(int, android.graphics.Bitmap) throws java.lang.IllegalArgumentException;
+    method public synchronized int[] getEditableKeys();
+    method public synchronized long getLong(int, long) throws java.lang.IllegalArgumentException;
+    method public synchronized java.lang.Object getObject(int, java.lang.Object) throws java.lang.IllegalArgumentException;
+    method public synchronized java.lang.String getString(int, java.lang.String) throws java.lang.IllegalArgumentException;
+    method public synchronized android.media.MediaMetadataEditor putBitmap(int, android.graphics.Bitmap) throws java.lang.IllegalArgumentException;
+    method public synchronized android.media.MediaMetadataEditor putLong(int, long) throws java.lang.IllegalArgumentException;
+    method public synchronized android.media.MediaMetadataEditor putObject(int, java.lang.Object) throws java.lang.IllegalArgumentException;
+    method public synchronized android.media.MediaMetadataEditor putString(int, java.lang.String) throws java.lang.IllegalArgumentException;
+    method public synchronized void removeEditableKeys();
+    field public static final int BITMAP_KEY_ARTWORK = 100; // 0x64
+    field public static final int RATING_KEY_BY_OTHERS = 101; // 0x65
+    field public static final int RATING_KEY_BY_USER = 268435457; // 0x10000001
+  }
+
   public class MediaMetadataRetriever {
     ctor public MediaMetadataRetriever();
     method public java.lang.String extractMetadata(int);
@@ -13185,6 +13206,29 @@
     ctor public NotProvisionedException(java.lang.String);
   }
 
+  public final class Rating implements android.os.Parcelable {
+    method public int describeContents();
+    method public float getPercentRating();
+    method public int getRatingStyle();
+    method public float getStarRating();
+    method public boolean hasHeart();
+    method public boolean isRated();
+    method public boolean isThumbUp();
+    method public static android.media.Rating newHeartRating(boolean);
+    method public static android.media.Rating newPercentageRating(float);
+    method public static android.media.Rating newStarRating(int, float);
+    method public static android.media.Rating newThumbRating(boolean);
+    method public static android.media.Rating newUnratedRating(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int RATING_3_STARS = 3; // 0x3
+    field public static final int RATING_4_STARS = 4; // 0x4
+    field public static final int RATING_5_STARS = 5; // 0x5
+    field public static final int RATING_HEART = 1; // 0x1
+    field public static final int RATING_PERCENTAGE = 6; // 0x6
+    field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
+  }
+
   public class RemoteControlClient {
     ctor public RemoteControlClient(android.app.PendingIntent);
     ctor public RemoteControlClient(android.app.PendingIntent, android.os.Looper);
@@ -13216,21 +13260,9 @@
     field public static final int PLAYSTATE_STOPPED = 1; // 0x1
   }
 
-  public class RemoteControlClient.MetadataEditor {
-    method public synchronized void addEditableKey(int);
+  public class RemoteControlClient.MetadataEditor extends android.media.MediaMetadataEditor {
     method public synchronized void apply();
-    method public synchronized void clear();
-    method public synchronized void clearEditableKeys();
-    method public synchronized android.media.RemoteControlClient.MetadataEditor putBitmap(int, android.graphics.Bitmap) throws java.lang.IllegalArgumentException;
-    method public synchronized android.media.RemoteControlClient.MetadataEditor putLong(int, long) throws java.lang.IllegalArgumentException;
-    method public synchronized android.media.RemoteControlClient.MetadataEditor putString(int, java.lang.String) throws java.lang.IllegalArgumentException;
     field public static final int BITMAP_KEY_ARTWORK = 100; // 0x64
-    field public static final int LONG_KEY_RATING_BY_OTHERS = 102; // 0x66
-    field public static final int LONG_KEY_RATING_BY_USER = 268435457; // 0x10000001
-    field public static final int LONG_KEY_RATING_TYPE = 101; // 0x65
-    field public static final long RATING_HEART = -1L; // 0xffffffffffffffffL
-    field public static final long RATING_NOT_RATED = -101L; // 0xffffffffffffff9bL
-    field public static final long RATING_THUMB_UP_DOWN = -2L; // 0xfffffffffffffffeL
   }
 
   public static abstract interface RemoteControlClient.OnGetPlaybackPositionListener {
@@ -13238,9 +13270,7 @@
   }
 
   public static abstract interface RemoteControlClient.OnMetadataUpdateListener {
-    method public abstract void onMetadataUpdateBitmap(int, android.graphics.Bitmap);
-    method public abstract void onMetadataUpdateLong(int, long);
-    method public abstract void onMetadataUpdateString(int, java.lang.String);
+    method public abstract void onMetadataUpdate(int, java.lang.Object);
   }
 
   public static abstract interface RemoteControlClient.OnPlaybackPositionUpdateListener {
@@ -19115,12 +19145,54 @@
     field public static final android.print.PrintAttributes.MediaSize ISO_C7;
     field public static final android.print.PrintAttributes.MediaSize ISO_C8;
     field public static final android.print.PrintAttributes.MediaSize ISO_C9;
+    field public static final android.print.PrintAttributes.MediaSize JIS_B0;
+    field public static final android.print.PrintAttributes.MediaSize JIS_B1;
+    field public static final android.print.PrintAttributes.MediaSize JIS_B10;
+    field public static final android.print.PrintAttributes.MediaSize JIS_B2;
+    field public static final android.print.PrintAttributes.MediaSize JIS_B3;
+    field public static final android.print.PrintAttributes.MediaSize JIS_B4;
+    field public static final android.print.PrintAttributes.MediaSize JIS_B5;
+    field public static final android.print.PrintAttributes.MediaSize JIS_B6;
+    field public static final android.print.PrintAttributes.MediaSize JIS_B7;
+    field public static final android.print.PrintAttributes.MediaSize JIS_B8;
+    field public static final android.print.PrintAttributes.MediaSize JIS_B9;
+    field public static final android.print.PrintAttributes.MediaSize JIS_EXEC;
+    field public static final android.print.PrintAttributes.MediaSize JPN_CHOU2;
+    field public static final android.print.PrintAttributes.MediaSize JPN_CHOU3;
+    field public static final android.print.PrintAttributes.MediaSize JPN_CHOU4;
+    field public static final android.print.PrintAttributes.MediaSize JPN_HAGAKI;
+    field public static final android.print.PrintAttributes.MediaSize JPN_KAHU;
+    field public static final android.print.PrintAttributes.MediaSize JPN_KAKU2;
+    field public static final android.print.PrintAttributes.MediaSize JPN_OUFUKU;
+    field public static final android.print.PrintAttributes.MediaSize JPN_YOU4;
+    field public static final android.print.PrintAttributes.MediaSize NA_FOOLSCAP;
     field public static final android.print.PrintAttributes.MediaSize NA_GOVT_LETTER;
+    field public static final android.print.PrintAttributes.MediaSize NA_INDEX_3X5;
+    field public static final android.print.PrintAttributes.MediaSize NA_INDEX_4X6;
+    field public static final android.print.PrintAttributes.MediaSize NA_INDEX_5X8;
     field public static final android.print.PrintAttributes.MediaSize NA_JUNIOR_LEGAL;
     field public static final android.print.PrintAttributes.MediaSize NA_LEDGER;
     field public static final android.print.PrintAttributes.MediaSize NA_LEGAL;
     field public static final android.print.PrintAttributes.MediaSize NA_LETTER;
+    field public static final android.print.PrintAttributes.MediaSize NA_MONARCH;
+    field public static final android.print.PrintAttributes.MediaSize NA_QUARTO;
     field public static final android.print.PrintAttributes.MediaSize NA_TBLOID;
+    field public static final android.print.PrintAttributes.MediaSize OM_DAI_PA_KAI;
+    field public static final android.print.PrintAttributes.MediaSize OM_JUURO_KU_KAI;
+    field public static final android.print.PrintAttributes.MediaSize OM_PA_KAI;
+    field public static final android.print.PrintAttributes.MediaSize PRC_1;
+    field public static final android.print.PrintAttributes.MediaSize PRC_10;
+    field public static final android.print.PrintAttributes.MediaSize PRC_16k;
+    field public static final android.print.PrintAttributes.MediaSize PRC_2;
+    field public static final android.print.PrintAttributes.MediaSize PRC_3;
+    field public static final android.print.PrintAttributes.MediaSize PRC_4;
+    field public static final android.print.PrintAttributes.MediaSize PRC_5;
+    field public static final android.print.PrintAttributes.MediaSize PRC_6;
+    field public static final android.print.PrintAttributes.MediaSize PRC_7;
+    field public static final android.print.PrintAttributes.MediaSize PRC_8;
+    field public static final android.print.PrintAttributes.MediaSize PRC_9;
+    field public static final android.print.PrintAttributes.MediaSize ROC_16K;
+    field public static final android.print.PrintAttributes.MediaSize ROC_8K;
   }
 
   public static final class PrintAttributes.Resolution {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 3e4795c..bf2a1e4 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -65,7 +65,7 @@
 
     // when adding one of these:
     //  - increment _NUM_OP
-    //  - add rows to sOpToSwitch, sOpNames, sOpPerms
+    //  - add rows to sOpToSwitch, sOpNames, sOpPerms, sOpDefaultMode
     //  - add descriptive strings to Settings/res/values/arrays.xml
     //  - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app)
 
@@ -315,6 +315,55 @@
     };
 
     /**
+     * This specifies the default mode for each operation.
+     */
+    private static int[] sOpDefaultMode = new int[] {
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_IGNORED, // OP_WRITE_SMS
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+    };
+
+    /**
      * Retrieve the op switch that controls the given operation.
      * @hide
      */
@@ -339,6 +388,14 @@
     }
 
     /**
+     * Retrieve the default mode for the operation.
+     * @hide
+     */
+    public static int opToDefaultMode(int op) {
+        return sOpDefaultMode[op];
+    }
+
+    /**
      * Class holding all of the operation information associated with an app.
      * @hide
      */
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index d889353..efe6b15 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -373,27 +373,208 @@
 
         // North America
 
-        /** North America Letter media size: 8.5" x 11" */
+        /** North America Letter media size: 8.5" x 11" (279mm x 216mm) */
         public static final MediaSize NA_LETTER =
                 new MediaSize("NA_LETTER", "android", R.string.mediaSize_na_letter, 8500, 11000);
-        /** North America Government-Letter media size: 8.0" x 10.5" */
+        /** North America Government-Letter media size: 8.0" x 10.5" (203mm x 267mm) */
         public static final MediaSize NA_GOVT_LETTER =
                 new MediaSize("NA_GOVT_LETTER", "android",
                         R.string.mediaSize_na_gvrnmt_letter, 8000, 10500);
-        /** North America Legal media size: 8.5" x 14" */
+        /** North America Legal media size: 8.5" x 14" (216mm x 356mm) */
         public static final MediaSize NA_LEGAL =
                 new MediaSize("NA_LEGAL", "android", R.string.mediaSize_na_legal, 8500, 14000);
-        /** North America Junior Legal media size: 8.0" x 5.0" */
+        /** North America Junior Legal media size: 8.0" x 5.0" (203mm × 127mm) */
         public static final MediaSize NA_JUNIOR_LEGAL =
                 new MediaSize("NA_JUNIOR_LEGAL", "android",
                         R.string.mediaSize_na_junior_legal, 8000, 5000);
-        /** North America Ledger media size: 17" x 11" */
+        /** North America Ledger media size: 17" x 11" (432mm × 279mm) */
         public static final MediaSize NA_LEDGER =
                 new MediaSize("NA_LEDGER", "android", R.string.mediaSize_na_ledger, 17000, 11000);
-        /** North America Tabloid media size: 11" x 17" */
+        /** North America Tabloid media size: 11" x 17" (279mm × 432mm) */
         public static final MediaSize NA_TBLOID =
                 new MediaSize("NA_TABLOID", "android",
                         R.string.mediaSize_na_tabloid, 11000, 17000);
+        /** North America Index Card 3x5 media size: 3" x 5" (76mm x 127mm) */
+        public static final MediaSize NA_INDEX_3X5 =
+                new MediaSize("NA_INDEX_3X5", "android",
+                        R.string.mediaSize_na_index_3x5, 3000, 5000);
+        /** North America Index Card 4x6 media size: 4" x 6" (102mm x 152mm) */
+        public static final MediaSize NA_INDEX_4X6 =
+                new MediaSize("NA_INDEX_4X6", "android",
+                        R.string.mediaSize_na_index_4x6, 4000, 6000);
+        /** North America Index Card 5x8 media size: 5" x 8" (127mm x 203mm) */
+        public static final MediaSize NA_INDEX_5X8 =
+                new MediaSize("NA_INDEX_5X8", "android",
+                        R.string.mediaSize_na_index_5x8, 5000, 8000);
+        /** North America Monarch media size: 7.25" x 10.5" (184mm x 267mm) */
+        public static final MediaSize NA_MONARCH =
+                new MediaSize("NA_MONARCH", "android",
+                        R.string.mediaSize_na_monarch, 7250, 10500);
+        /** North America Quarto media size: 8" x 10" (203mm x 254mm) */
+        public static final MediaSize NA_QUARTO =
+                new MediaSize("NA_QUARTO", "android",
+                        R.string.mediaSize_na_quarto, 8000, 10000);
+        /** North America Foolscap media size: 8" x 13" (203mm x 330mm) */
+        public static final MediaSize NA_FOOLSCAP =
+                new MediaSize("NA_FOOLSCAP", "android",
+                        R.string.mediaSize_na_foolscap, 8000, 13000);
+
+        // Chinese
+
+        /** Chinese ROC 8K media size: 270mm x 390mm (10.629" x 15.3543") */
+        public static final MediaSize ROC_8K =
+                new MediaSize("ROC_8K", "android",
+                        R.string.mediaSize_chinese_roc_8k, 10629, 15354);
+        /** Chinese ROC 16K media size: 195mm x 270mm (7.677" x 10.629") */
+        public static final MediaSize ROC_16K =
+                new MediaSize("ROC_16K", "android",
+                        R.string.mediaSize_chinese_roc_16k, 7677, 10629);
+
+        /** Chinese PRC 1 media size: 102mm x 165mm (4.015" x 6.496") */
+        public static final MediaSize PRC_1 =
+                new MediaSize("PRC_1", "android",
+                        R.string.mediaSize_chinese_prc_1, 4015, 6496);
+        /** Chinese PRC 2 media size: 102mm x 176mm (4.015" x 6.929") */
+        public static final MediaSize PRC_2 =
+                new MediaSize("PRC_2", "android",
+                        R.string.mediaSize_chinese_prc_2, 4015, 6929);
+        /** Chinese PRC 3 media size: 125mm x 176mm (4.921" x 6.929") */
+        public static final MediaSize PRC_3 =
+                new MediaSize("PRC_3", "android",
+                        R.string.mediaSize_chinese_prc_3, 4921, 6929);
+        /** Chinese PRC 4 media size: 110mm x 208mm (4.330" x 8.189") */
+        public static final MediaSize PRC_4 =
+                new MediaSize("PRC_4", "android",
+                        R.string.mediaSize_chinese_prc_4, 4330, 8189);
+        /** Chinese PRC 5 media size: 110mm x 220mm (4.330" x 8.661") */
+        public static final MediaSize PRC_5 =
+                new MediaSize("PRC_5", "android",
+                        R.string.mediaSize_chinese_prc_5, 4330, 8661);
+        /** Chinese PRC 6 media size: 120mm x 320mm (4.724" x 12.599") */
+        public static final MediaSize PRC_6 =
+                new MediaSize("PRC_6", "android",
+                        R.string.mediaSize_chinese_prc_6, 4724, 12599);
+        /** Chinese PRC 7 media size: 160mm x 230mm (6.299" x 9.055") */
+        public static final MediaSize PRC_7 =
+                new MediaSize("PRC_7", "android",
+                        R.string.mediaSize_chinese_prc_7, 6299, 9055);
+        /** Chinese PRC 8 media size: 120mm x 309mm (4.724" x 12.165") */
+        public static final MediaSize PRC_8 =
+                new MediaSize("PRC_8", "android",
+                        R.string.mediaSize_chinese_prc_8, 4724, 12165);
+        /** Chinese PRC 9 media size: 229mm x 324mm (9.016" x 12.756") */
+        public static final MediaSize PRC_9 =
+                new MediaSize("PRC_9", "android",
+                        R.string.mediaSize_chinese_prc_9, 9016, 12756);
+        /** Chinese PRC 10 media size: 324mm x 458mm (12.756" x 18.032") */
+        public static final MediaSize PRC_10 =
+                new MediaSize("PRC_10", "android",
+                        R.string.mediaSize_chinese_prc_10, 12756, 18032);
+
+        /** Chinese PRC 16k media size: 146mm x 215mm (5.749" x 8.465") */
+        public static final MediaSize PRC_16k =
+                new MediaSize("PRC_16k", "android",
+                        R.string.mediaSize_chinese_prc_16k, 5749, 8465);
+        /** Chinese Pa Kai media size: 267mm x 389mm (10.512" x 15.315") */
+        public static final MediaSize OM_PA_KAI =
+                new MediaSize("OM_PA_KAI", "android",
+                        R.string.mediaSize_chinese_om_pa_kai, 10512, 15315);
+        /** Chinese Dai Pa Kai media size: 275mm x 395mm (10.827" x 15.551") */
+        public static final MediaSize OM_DAI_PA_KAI =
+                new MediaSize("OM_DAI_PA_KAI", "android",
+                        R.string.mediaSize_chinese_om_dai_pa_kai, 10827, 15551);
+        /** Chinese Jurro Ku Kai media size: 198mm x 275mm (7.796" x 10.827") */
+        public static final MediaSize OM_JUURO_KU_KAI =
+                new MediaSize("OM_JUURO_KU_KAI", "android",
+                        R.string.mediaSize_chinese_om_jurro_ku_kai, 7796, 10827);
+
+        // Japanese
+
+        /** Japanese JIS B10 media size: 32mm x 45mm (1.259" x 1.772") */
+        public static final MediaSize JIS_B10 =
+                new MediaSize("JIS_B10", "android",
+                        R.string.mediaSize_japanese_jis_b10, 1259, 1772);
+        /** Japanese JIS B9 media size: 45mm x 64mm (1.772" x 2.52") */
+        public static final MediaSize JIS_B9 =
+                new MediaSize("JIS_B9", "android",
+                        R.string.mediaSize_japanese_jis_b9, 1772, 2520);
+        /** Japanese JIS B8 media size: 64mm x 91mm (2.52" x 3.583") */
+        public static final MediaSize JIS_B8 =
+                new MediaSize("JIS_B8", "android",
+                        R.string.mediaSize_japanese_jis_b8, 2520, 3583);
+        /** Japanese JIS B7 media size: 91mm x 128mm (3.583" x 5.049") */
+        public static final MediaSize JIS_B7 =
+                new MediaSize("JIS_B7", "android",
+                        R.string.mediaSize_japanese_jis_b7, 3583, 5049);
+        /** Japanese JIS B6 media size: 128mm x 182mm (5.049" x 7.165") */
+        public static final MediaSize JIS_B6 =
+                new MediaSize("JIS_B6", "android",
+                        R.string.mediaSize_japanese_jis_b6, 5049, 7165);
+        /** Japanese JIS B5 media size: 182mm x 257mm (7.165" x 10.118") */
+        public static final MediaSize JIS_B5 =
+                new MediaSize("JIS_B5", "android",
+                        R.string.mediaSize_japanese_jis_b5, 7165, 10118);
+        /** Japanese JIS B4 media size: 257mm x 364mm (10.118" x 14.331") */
+        public static final MediaSize JIS_B4 =
+                new MediaSize("JIS_B4", "android",
+                        R.string.mediaSize_japanese_jis_b4, 10118, 14331);
+        /** Japanese JIS B3 media size: 364mm x 515mm (14.331" x 20.276") */
+        public static final MediaSize JIS_B3 =
+                new MediaSize("JIS_B3", "android",
+                        R.string.mediaSize_japanese_jis_b3, 14331, 20276);
+        /** Japanese JIS B2 media size: 515mm x 728mm (20.276" x 28.661") */
+        public static final MediaSize JIS_B2 =
+                new MediaSize("JIS_B2", "android",
+                        R.string.mediaSize_japanese_jis_b2, 20276, 28661);
+        /** Japanese JIS B1 media size: 728mm x 1030mm (28.661" x 40.551") */
+        public static final MediaSize JIS_B1 =
+                new MediaSize("JIS_B1", "android",
+                        R.string.mediaSize_japanese_jis_b1, 28661, 40551);
+        /** Japanese JIS B0 media size: 1030mm x 1456mm (40.551" x 57.323") */
+        public static final MediaSize JIS_B0 =
+                new MediaSize("JIS_B0", "android",
+                        R.string.mediaSize_japanese_jis_b0, 40551, 57323);
+
+        /** Japanese JIS Exec media size: 216mm x 330mm (8.504" x 12.992") */
+        public static final MediaSize JIS_EXEC =
+                new MediaSize("JIS_EXEC", "android",
+                        R.string.mediaSize_japanese_jis_exec, 8504, 12992);
+
+        /** Japanese Chou4 media size: 90mm x 205mm (3.543" x 8.071") */
+        public static final MediaSize JPN_CHOU4 =
+                new MediaSize("JPN_CHOU4", "android",
+                        R.string.mediaSize_japanese_chou4, 3543, 8071);
+        /** Japanese Chou3 media size: 120mm x 235mm (4.724" x 9.252") */
+        public static final MediaSize JPN_CHOU3 =
+                new MediaSize("JPN_CHOU3", "android",
+                        R.string.mediaSize_japanese_chou3, 4724, 9252);
+        /** Japanese Chou2 media size: 111.1mm x 146mm (4.374" x 5.748") */
+        public static final MediaSize JPN_CHOU2 =
+                new MediaSize("JPN_CHOU2", "android",
+                        R.string.mediaSize_japanese_chou2, 4374, 5748);
+
+        /** Japanese Hagaki media size: 100mm x 148mm (3.937" x 5.827") */
+        public static final MediaSize JPN_HAGAKI =
+                new MediaSize("JPN_HAGAKI", "android",
+                        R.string.mediaSize_japanese_hagaki, 3937, 5827);
+        /** Japanese Oufuku media size: 148mm x 200mm (5.827" x 7.874") */
+        public static final MediaSize JPN_OUFUKU =
+                new MediaSize("JPN_OUFUKU", "android",
+                        R.string.mediaSize_japanese_oufuku, 5827, 7874);
+
+        /** Japanese Kahu media size: 240mm x 322.1mm (9.449" x 12.681") */
+        public static final MediaSize JPN_KAHU =
+                new MediaSize("JPN_KAHU", "android",
+                        R.string.mediaSize_japanese_kahu, 9449, 12681);
+        /** Japanese Kaku2 media size: 240mm x 332mm (9.449" x 13.071") */
+        public static final MediaSize JPN_KAKU2 =
+                new MediaSize("JPN_KAKU2", "android",
+                        R.string.mediaSize_japanese_kaku2, 9449, 13071);
+
+        /** Japanese You4 media size: 105mm x 235mm (4.134" x 9.252") */
+        public static final MediaSize JPN_YOU4 =
+                new MediaSize("JPN_YOU4", "android",
+                        R.string.mediaSize_japanese_you4, 4134, 9252);
 
         private final String mId;
         /**@hide */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c8312e3..6c6635d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4355,6 +4355,12 @@
         public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
 
         /**
+         * Specifies the package name currently configured to be the primary sms application
+         * @hide
+         */
+        public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
+
+        /**
          * Name of a package that the current user has explicitly allowed to see all of that
          * user's notifications.
          *
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 969c94ba..7efcb6e 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -1639,6 +1639,8 @@
 
         @Override
         public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
+            TransitionManager.beginDelayedTransition(ActionBarView.this, sTransition);
+
             mExpandedActionView = item.getActionView();
             mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(getResources()));
             mCurrentExpandedItem = item;
@@ -1666,6 +1668,8 @@
 
         @Override
         public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
+            TransitionManager.beginDelayedTransition(ActionBarView.this, sTransition);
+
             // Do this before detaching the actionview from the hierarchy, in case
             // it needs to dismiss the soft keyboard, etc.
             if (mExpandedActionView instanceof CollapsibleActionView) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 499a930..c2a830d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1103,6 +1103,13 @@
         android:description="@string/permdesc_use_sip"
         android:label="@string/permlab_use_sip" />
 
+    <!-- Allows an application to request CallHandlerService implementations. -->
+    <permission android:name="android.permission.BIND_CALL_SERVICE"
+        android:permissionGroup="android.permission-group.PHONE_CALLS"
+        android:protectionLevel="system|signature"
+        android:description="@string/permdesc_bind_call_service"
+        android:label="@string/permlab_bind_call_service" />
+
     <!-- ================================== -->
     <!-- Permissions for sdcard interaction -->
     <!-- ================================== -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 98368a1..99b703b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1898,6 +1898,11 @@
     <string name="permdesc_use_sip">Allows the app to use the SIP service to make/receive Internet calls.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bind_call_service">interact with in-call screen</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bind_call_service">Allows the app to control when and how the user sees the in-call screen.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readNetworkUsageHistory">read historical network usage</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_readNetworkUsageHistory">Allows the app to read historical network usage for specific networks and apps.</string>
@@ -4296,19 +4301,111 @@
     <!-- ISO (European standard) C10 media (paper) size: 1.10" x 1.57" -->
     <string name="mediaSize_iso_c10">ISO C10</string>
 
-    <!-- North America Letter media (paper) size: 8.5" × 11" -->
+    <!-- North America Letter media (paper) size: 8.5" × 11" (279mm x 216mm) -->
     <string name="mediaSize_na_letter">Letter</string>
-    <!-- North America Government Letter media (paper) size: 8.0" × 10.5" -->
+    <!-- North America Government Letter media (paper) size: 8.0" × 10.5" (203mm x 267mm) -->
     <string name="mediaSize_na_gvrnmt_letter">Government Letter</string>
-    <!-- North America Legal media (paper) size: 8.5" × 14" -->
+    <!-- North America Legal media (paper) size: 8.5" × 14" (216mm x 356mm) -->
     <string name="mediaSize_na_legal">Legal</string>
-    <!-- North America Junior Legal media (paper) size: 8.0" × 5.0" -->
+    <!-- North America Junior Legal media (paper) size: 8.0" × 5.0" (203mm × 127mm) -->
     <string name="mediaSize_na_junior_legal">Junior Legal</string>
-    <!-- North America Ledger media (paper) size: 17" × 11" -->
+    <!-- North America Ledger media (paper) size: 17" × 11" (432mm × 279mm) -->
     <string name="mediaSize_na_ledger">Ledger</string>
-    <!-- North America Tabloid media (paper) size: 11" × 17" -->
+    <!-- North America Tabloid media (paper) size: 11" × 17" (279mm × 432mm) -->
     <string name="mediaSize_na_tabloid">Tabloid</string>
 
+    <!-- North America Index Card 3x5 media (paper) size: 3" x 5" (76mm x 127mm) -->
+    <string name="mediaSize_na_index_3x5">Index Card 3x5</string>
+    <!-- North America Index Card 4x6 media (paper) size: 4" x 6" (102mm x 152mm) -->
+    <string name="mediaSize_na_index_4x6">Index Card 4x6</string>
+    <!-- North America Index Card 5x8 media (paper) size: 5" x 8" (127mm x 203mm) -->
+    <string name="mediaSize_na_index_5x8">Index Card 5x8</string>
+    <!-- North America Monarch media (paper) size: 7.25" x 10.5" (184mm x 267mm) -->
+    <string name="mediaSize_na_monarch">Monarch</string>
+    <!-- North America Quarto media (paper) size: 8" x 10" (203mm x 254mm) -->
+    <string name="mediaSize_na_quarto">Quarto</string>
+    <!-- North America Foolscap media (paper) size: 8" x 13" (203mm x 330mm) -->
+    <string name="mediaSize_na_foolscap">Foolscap</string>
+
+    <!-- Chinese Roc 8k media (paper) size: 270mm x 390mm (10.629" x 15.3543") -->
+    <string name="mediaSize_chinese_roc_8k">ROC 8K</string>
+    <!-- Chinese Roc 16k media (paper) size: 195mm x 270mm (7.677" x 10.629") -->
+    <string name="mediaSize_chinese_roc_16k">ROC 16K</string>
+
+    <!-- Chinese PRC 1 media (paper) size: 102mm x 165mm (4.015" x 6.496") -->
+    <string name="mediaSize_chinese_prc_1">PRC 1</string>
+    <!-- Chinese PRC 2 media (paper) size: 102mm x 176mm (4.015" x 6.929") -->
+    <string name="mediaSize_chinese_prc_2">PRC 2</string>
+    <!-- Chinese PRC 3 media (paper) size: 125mm x 176mm (4.921" x 6.929") -->
+    <string name="mediaSize_chinese_prc_3">PRC 3</string>
+    <!-- Chinese PRC 4 media (paper) size: 110mm x 208mm (4.330" x 8.189") -->
+    <string name="mediaSize_chinese_prc_4">PRC 4</string>
+    <!-- Chinese PRC 5 media (paper) size: 110mm x 220mm (4.330" x 8.661") -->
+    <string name="mediaSize_chinese_prc_5">PRC 5</string>
+    <!-- Chinese PRC 6 media (paper) size: 120mm x 320mm (4.724" x 12.599") -->
+    <string name="mediaSize_chinese_prc_6">PRC 6</string>
+    <!-- Chinese PRC 7 media (paper) size: 160mm x 230mm (6.299" x 9.055") -->
+    <string name="mediaSize_chinese_prc_7">PRC 7</string>
+    <!-- Chinese PRC 8 media (paper) size: 120mm x 309mm (4.724" x 12.165") -->
+    <string name="mediaSize_chinese_prc_8">PRC 8</string>
+    <!-- Chinese PRC 9 media (paper) size: 229mm x 324mm (9.016" x 12.756") -->
+    <string name="mediaSize_chinese_prc_9">PRC 9</string>
+    <!-- Chinese PRC 10 media (paper) size: 324mm x 458mm (12.756" x 18.032") -->
+    <string name="mediaSize_chinese_prc_10">PRC 10</string>
+
+    <!-- Chinese RPC 16K media (paper) size: 146mm x 215mm (5.749" x 8.465") -->
+    <string name="mediaSize_chinese_prc_16k">PRC 16K</string>
+    <!-- Chinese Pa Kai media (paper) size: 146mm x 215mm (5.749" x 8.465") -->
+    <string name="mediaSize_chinese_om_pa_kai">Pa Kai</string>
+    <!-- Chinese Dai Pa Kai media (paper) size: 275mm x 395mm (10.827" x 15.551") -->
+    <string name="mediaSize_chinese_om_dai_pa_kai">Dai Pa Kai</string>
+    <!-- Chinese Jurro Ku Kai media (paper) size: 275mm x 395mm (10.827" x 15.551") -->
+    <string name="mediaSize_chinese_om_jurro_ku_kai">Jurro Ku Kai</string>
+
+    <!-- Japanese JIS B10 media (paper) size: 32mm x 45mm (1.259" x 1.772") -->
+    <string name="mediaSize_japanese_jis_b10">JIS B10</string>
+    <!-- Japanese JIS B9 media (paper) size: 45mm x 64mm (1.772" x 2.52") -->
+    <string name="mediaSize_japanese_jis_b9">JIS B9</string>
+    <!-- Japanese JIS B8 media (paper) size: 64mm x 91mm (2.52" x 3.583") -->
+    <string name="mediaSize_japanese_jis_b8">JIS B8</string>
+    <!-- Japanese JIS B7 media (paper) size: 91mm x 128mm (3.583" x 5.049") -->
+    <string name="mediaSize_japanese_jis_b7">JIS B7</string>
+    <!-- Japanese JIS B6 media (paper) size: 128mm x 182mm (5.049" x 7.165") -->
+    <string name="mediaSize_japanese_jis_b6">JIS B6</string>
+    <!-- Japanese JIS B5 media (paper) size: 182mm x 257mm (7.165" x 10.118") -->
+    <string name="mediaSize_japanese_jis_b5">JIS B5</string>
+    <!-- Japanese JIS B4 media (paper) size: 257mm x 364mm (10.118" x 14.331") -->
+    <string name="mediaSize_japanese_jis_b4">JIS B4</string>
+    <!-- Japanese JIS B3 media (paper) size: 364mm x 515mm (14.331" x 20.276") -->
+    <string name="mediaSize_japanese_jis_b3">JIS B3</string>
+    <!-- Japanese JIS B2 media (paper) size: 515mm x 728mm (20.276" x 28.661") -->
+    <string name="mediaSize_japanese_jis_b2">JIS B2</string>
+    <!-- Japanese JIS B1 media (paper) size: 728mm x 1030mm (28.661" x 40.551") -->
+    <string name="mediaSize_japanese_jis_b1">JIS B1</string>
+    <!-- Japanese JIS B0 media (paper) size: 1030mm x 1456mm (40.551" x 57.323") -->
+    <string name="mediaSize_japanese_jis_b0">JIS B0</string>
+
+    <!-- Japanese JIS Exec media (paper) size: 216mm x 330mm (8.504" x 12.992") -->
+    <string name="mediaSize_japanese_jis_exec">JIS Exec</string>
+
+    <!-- Japanese Chou4 media (paper) size: 90mm x 205mm (3.543" x 8.071") -->
+    <string name="mediaSize_japanese_chou4">Chou4</string>
+    <!-- Japanese Chou3 media (paper) size: 120mm x 235mm (4.724" x 9.252") -->
+    <string name="mediaSize_japanese_chou3">Chou3</string>
+    <!-- Japanese Chou2 media (paper) size: 111.1mm x 146mm (4.374" x 5.748") -->
+    <string name="mediaSize_japanese_chou2">Chou2</string>
+
+    <!-- Japanese Hagaki media (paper) size: 100mm x 148mm (3.937" x 5.827") -->
+    <string name="mediaSize_japanese_hagaki">Hagaki </string>
+    <!-- Japanese Oufuku media (paper) size: 148mm x 200mm (5.827" x 7.874") -->
+    <string name="mediaSize_japanese_oufuku">Oufuku </string>
+    <!-- Japanese Kahu media (paper) size: 240mm x 322.1mm (9.449" x 12.681") -->
+    <string name="mediaSize_japanese_kahu">Kahu</string>
+    <!-- Japanese Kaku2 media (paper) size: 240mm x 332mm (9.449" x 13.071") -->
+    <string name="mediaSize_japanese_kaku2">Kaku2</string>
+    <!-- Japanese You4 media (paper) size: 105mm x 235mm (4.134" x 9.252") -->
+    <string name="mediaSize_japanese_you4">You4</string>
+
     <!-- Write fail reason: printing was cancelled.[CHAR LIMIT=none] -->
     <string name="write_fail_reason_cancelled">Cancelled</string>
     <!-- Write fail reason: couldn't write the printed content. [CHAR LIMIT=none] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9cb0a37..e82c0e1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -878,6 +878,48 @@
   <java-symbol type="string" name="mediaSize_na_junior_legal" />
   <java-symbol type="string" name="mediaSize_na_ledger" />
   <java-symbol type="string" name="mediaSize_na_tabloid" />
+  <java-symbol type="string" name="mediaSize_na_index_3x5" />
+  <java-symbol type="string" name="mediaSize_na_index_4x6" />
+  <java-symbol type="string" name="mediaSize_na_index_5x8" />
+  <java-symbol type="string" name="mediaSize_na_monarch" />
+  <java-symbol type="string" name="mediaSize_na_quarto" />
+  <java-symbol type="string" name="mediaSize_na_foolscap" />
+  <java-symbol type="string" name="mediaSize_chinese_roc_8k" />
+  <java-symbol type="string" name="mediaSize_chinese_roc_16k" />
+  <java-symbol type="string" name="mediaSize_chinese_prc_1" />
+  <java-symbol type="string" name="mediaSize_chinese_prc_2" />
+  <java-symbol type="string" name="mediaSize_chinese_prc_3" />
+  <java-symbol type="string" name="mediaSize_chinese_prc_4" />
+  <java-symbol type="string" name="mediaSize_chinese_prc_5" />
+  <java-symbol type="string" name="mediaSize_chinese_prc_6" />
+  <java-symbol type="string" name="mediaSize_chinese_prc_7" />
+  <java-symbol type="string" name="mediaSize_chinese_prc_8" />
+  <java-symbol type="string" name="mediaSize_chinese_prc_9" />
+  <java-symbol type="string" name="mediaSize_chinese_prc_10" />
+  <java-symbol type="string" name="mediaSize_chinese_prc_16k" />
+  <java-symbol type="string" name="mediaSize_chinese_om_pa_kai" />
+  <java-symbol type="string" name="mediaSize_chinese_om_dai_pa_kai" />
+  <java-symbol type="string" name="mediaSize_chinese_om_jurro_ku_kai" />
+  <java-symbol type="string" name="mediaSize_japanese_jis_b10" />
+  <java-symbol type="string" name="mediaSize_japanese_jis_b9" />
+  <java-symbol type="string" name="mediaSize_japanese_jis_b8" />
+  <java-symbol type="string" name="mediaSize_japanese_jis_b7" />
+  <java-symbol type="string" name="mediaSize_japanese_jis_b6" />
+  <java-symbol type="string" name="mediaSize_japanese_jis_b5" />
+  <java-symbol type="string" name="mediaSize_japanese_jis_b4" />
+  <java-symbol type="string" name="mediaSize_japanese_jis_b3" />
+  <java-symbol type="string" name="mediaSize_japanese_jis_b2" />
+  <java-symbol type="string" name="mediaSize_japanese_jis_b1" />
+  <java-symbol type="string" name="mediaSize_japanese_jis_b0" />
+  <java-symbol type="string" name="mediaSize_japanese_jis_exec" />
+  <java-symbol type="string" name="mediaSize_japanese_chou4" />
+  <java-symbol type="string" name="mediaSize_japanese_chou3" />
+  <java-symbol type="string" name="mediaSize_japanese_chou2" />
+  <java-symbol type="string" name="mediaSize_japanese_hagaki" />
+  <java-symbol type="string" name="mediaSize_japanese_oufuku" />
+  <java-symbol type="string" name="mediaSize_japanese_kahu" />
+  <java-symbol type="string" name="mediaSize_japanese_kaku2" />
+  <java-symbol type="string" name="mediaSize_japanese_you4" />
   <java-symbol type="string" name="reason_unknown" />
   <java-symbol type="string" name="restr_pin_enter_admin_pin" />
   <java-symbol type="string" name="restr_pin_enter_pin" />
diff --git a/media/java/android/media/IRemoteControlClient.aidl b/media/java/android/media/IRemoteControlClient.aidl
index dd729b4..48079f2 100644
--- a/media/java/android/media/IRemoteControlClient.aidl
+++ b/media/java/android/media/IRemoteControlClient.aidl
@@ -17,6 +17,7 @@
 
 import android.graphics.Bitmap;
 import android.media.IRemoteControlDisplay;
+import android.media.Rating;
 
 /**
  * @hide
@@ -49,5 +50,5 @@
     void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h);
     void setWantsSyncForDisplay(IRemoteControlDisplay rcd, boolean wantsSync);
     void seekTo(int clientGeneration, long timeMs);
-    void updateMetadata(int clientGeneration, int key, long value);
+    void updateMetadata(int clientGeneration, int key, in Rating value);
 }
\ No newline at end of file
diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java
index ab686e6..34dc580 100644
--- a/media/java/android/media/MediaFocusControl.java
+++ b/media/java/android/media/MediaFocusControl.java
@@ -2093,8 +2093,11 @@
                 if ((mCurrentRcClient != null) && (mCurrentRcClientGen == genId)) {
                     try {
                         switch (key) {
-                            case RemoteControlClient.MetadataEditor.LONG_KEY_RATING_BY_USER:
-                                mCurrentRcClient.updateMetadata(genId, key, value);
+                            case RemoteControlClient.MetadataEditor.RATING_KEY_BY_USER:
+                                // TODO handle rating update, placeholder code here that sends
+                                //      an unrated percent-based rating
+                                mCurrentRcClient.updateMetadata(genId, key,
+                                        Rating.newUnratedRating(Rating.RATING_PERCENTAGE));
                                 break;
                             default:
                                 Log.e(TAG, "unhandled metadata key " + key + " update for RCC "
diff --git a/media/java/android/media/MediaMetadataEditor.java b/media/java/android/media/MediaMetadataEditor.java
new file mode 100644
index 0000000..b601016
--- /dev/null
+++ b/media/java/android/media/MediaMetadataEditor.java
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+/**
+ * An abstract class for editing and storing metadata that can be published by
+ * {@link RemoteControlClient}. See the {@link RemoteControlClient#editMetadata(boolean)}
+ * method to instantiate a {@link RemoteControlClient.MetadataEditor} object.
+ */
+public abstract class MediaMetadataEditor {
+
+    private final static String TAG = "MediaMetadataEditor";
+    /**
+     * @hide
+     */
+    protected MediaMetadataEditor() {
+    }
+
+    // Public keys for metadata used by RemoteControlClient and RemoteController.
+    // Note that these keys are defined here, and not in MediaMetadataRetriever
+    // because they are not supported by the MediaMetadataRetriever features.
+    /**
+     * The metadata key for the content artwork / album art.
+     */
+    public final static int BITMAP_KEY_ARTWORK =
+            RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK;
+
+    /**
+     * The metadata key for the content's average rating, not the user's rating.
+     * The value associated with this key is a {@link Rating} instance.
+     * @see #RATING_KEY_BY_USER
+     */
+    public final static int RATING_KEY_BY_OTHERS = 101;
+
+    /**
+     * The metadata key for the content's user rating.
+     * The value associated with this key is a {@link Rating} instance.
+     * This key can be flagged as "editable" (with {@link #addEditableKey(int)}) to enable
+     * receiving user rating values through the
+     * {@link android.media.RemoteControlClient.OnMetadataUpdateListener} interface.
+     */
+    public final static int RATING_KEY_BY_USER = 0x10000001;
+
+    /**
+     * @hide
+     * Editable key mask
+     */
+    public final static int KEY_EDITABLE_MASK = 0x1FFFFFFF;
+
+
+    /**
+     * Applies all of the metadata changes that have been set since the MediaMetadataEditor instance
+     * was created or since {@link #clear()} was called.
+     */
+    public abstract void apply();
+
+
+    /**
+     * @hide
+     * Mask of editable keys.
+     */
+    protected long mEditableKeys;
+
+    /**
+     * @hide
+     */
+    protected boolean mMetadataChanged = false;
+
+    /**
+     * @hide
+     */
+    protected boolean mApplied = false;
+
+    /**
+     * @hide
+     */
+    protected boolean mArtworkChanged = false;
+
+    /**
+     * @hide
+     */
+    protected Bitmap mEditorArtwork;
+
+    /**
+     * @hide
+     */
+    protected Bundle mEditorMetadata;
+
+
+    /**
+     * Clears all the pending metadata changes set since the MediaMetadataEditor instance was
+     * created or since this method was last called.
+     * Note that clearing the metadata doesn't reset the editable keys
+     * (use {@link #removeEditableKeys()} instead).
+     */
+    public synchronized void clear() {
+        if (mApplied) {
+            Log.e(TAG, "Can't clear a previously applied MediaMetadataEditor");
+            return;
+        }
+        mEditorMetadata.clear();
+        mEditorArtwork = null;
+    }
+
+    /**
+     * Flags the given key as being editable.
+     * This should only be used by metadata publishers, such as {@link RemoteControlClient},
+     * which will declare the metadata field as eligible to be updated, with new values
+     * received through the {@link RemoteControlClient.OnMetadataUpdateListener} interface.
+     * @param key the type of metadata that can be edited. The supported key is
+     *     {@link #RATING_KEY_BY_USER}.
+     */
+    public synchronized void addEditableKey(int key) {
+        if (mApplied) {
+            Log.e(TAG, "Can't change editable keys of a previously applied MetadataEditor");
+            return;
+        }
+        // only one editable key at the moment, so we're not wasting memory on an array
+        // of editable keys to check the validity of the key, just hardcode the supported key.
+        if (key == RATING_KEY_BY_USER) {
+            mEditableKeys |= (KEY_EDITABLE_MASK & key);
+            mMetadataChanged = true;
+        } else {
+            Log.e(TAG, "Metadata key " + key + " cannot be edited");
+        }
+    }
+
+    /**
+     * Causes all metadata fields to be read-only.
+     */
+    public synchronized void removeEditableKeys() {
+        if (mApplied) {
+            Log.e(TAG, "Can't remove all editable keys of a previously applied MetadataEditor");
+            return;
+        }
+        if (mEditableKeys != 0) {
+            mEditableKeys = 0;
+            mMetadataChanged = true;
+        }
+    }
+
+    /**
+     * Retrieves the keys flagged as editable.
+     * @return null if there are no editable keys, or an array containing the keys.
+     */
+    public synchronized int[] getEditableKeys() {
+        // only one editable key supported here
+        if (mEditableKeys == RATING_KEY_BY_USER) {
+            int[] keys = { RATING_KEY_BY_USER };
+            return keys;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Adds textual information.
+     * Note that none of the information added after {@link #apply()} has been called,
+     * will be available to consumers of metadata stored by the MediaMetadataEditor.
+     * @param key The identifier of a the metadata field to set. Valid values are
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER}.
+     * @param value The text for the given key, or {@code null} to signify there is no valid
+     *      information for the field.
+     * @return Returns a reference to the same MediaMetadataEditor object, so you can chain put
+     *      calls together.
+     */
+    public synchronized MediaMetadataEditor putString(int key, String value)
+            throws IllegalArgumentException {
+        if (mApplied) {
+            Log.e(TAG, "Can't edit a previously applied MediaMetadataEditor");
+            return this;
+        }
+        if (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID) != METADATA_TYPE_STRING) {
+            throw(new IllegalArgumentException("Invalid type 'String' for key "+ key));
+        }
+        mEditorMetadata.putString(String.valueOf(key), value);
+        mMetadataChanged = true;
+        return this;
+    }
+
+    /**
+     * Adds numerical information.
+     * Note that none of the information added after {@link #apply()} has been called
+     * will be available to consumers of metadata stored by the MediaMetadataEditor.
+     * @param key the identifier of a the metadata field to set. Valid values are
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} (with a value
+     *      expressed in milliseconds),
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
+     * @param value The long value for the given key
+     * @return Returns a reference to the same MediaMetadataEditor object, so you can chain put
+     *      calls together.
+     * @throws IllegalArgumentException
+     */
+    public synchronized MediaMetadataEditor putLong(int key, long value)
+            throws IllegalArgumentException {
+        if (mApplied) {
+            Log.e(TAG, "Can't edit a previously applied MediaMetadataEditor");
+            return this;
+        }
+        if (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID) != METADATA_TYPE_LONG) {
+            throw(new IllegalArgumentException("Invalid type 'long' for key "+ key));
+        }
+        mEditorMetadata.putLong(String.valueOf(key), value);
+        mMetadataChanged = true;
+        return this;
+    }
+
+    /**
+     * Adds image.
+     * @param key the identifier of the bitmap to set. The only valid value is
+     *      {@link #BITMAP_KEY_ARTWORK}
+     * @param bitmap The bitmap for the artwork, or null if there isn't any.
+     * @return Returns a reference to the same MediaMetadataEditor object, so you can chain put
+     *      calls together.
+     * @throws IllegalArgumentException
+     * @see android.graphics.Bitmap
+     */
+    public synchronized MediaMetadataEditor putBitmap(int key, Bitmap bitmap)
+            throws IllegalArgumentException {
+        if (mApplied) {
+            Log.e(TAG, "Can't edit a previously applied MediaMetadataEditor");
+            return this;
+        }
+        if (key != BITMAP_KEY_ARTWORK) {
+            throw(new IllegalArgumentException("Invalid type 'Bitmap' for key "+ key));
+        }
+        mEditorArtwork = bitmap;
+        mArtworkChanged = true;
+        return this;
+    }
+
+    /**
+     * Adds information stored as an instance.
+     * Note that none of the information added after {@link #apply()} has been called
+     * will be available to consumers of metadata stored by the MediaMetadataEditor.
+     * @param key the identifier of a the metadata field to set. Valid keys for a:
+     *     <ul>
+     *     <li>{@link Bitmap} object are {@link #BITMAP_KEY_ARTWORK},</li>
+     *     <li>{@link String} object are the same as for {@link #putString(int, String)}</li>
+     *     <li>{@link Long} object are the same as for {@link #putLong(int, long)}</li>
+     *     <li>{@link Rating} object are {@link #RATING_KEY_BY_OTHERS}
+     *         and {@link #RATING_KEY_BY_USER}.</li>
+     *     </ul>
+     * @param obj the metadata to add.
+     * @return Returns a reference to the same MediaMetadataEditor object, so you can chain put
+     *      calls together.
+     * @throws IllegalArgumentException
+     */
+    public synchronized MediaMetadataEditor putObject(int key, Object value)
+            throws IllegalArgumentException {
+        if (mApplied) {
+            Log.e(TAG, "Can't edit a previously applied MediaMetadataEditor");
+            return this;
+        }
+        switch(METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID)) {
+            case METADATA_TYPE_LONG:
+                if (value instanceof Long) {
+                    return putLong(key, ((Long)value).longValue());
+                } else {
+                    throw(new IllegalArgumentException("Not a non-null Long for key "+ key));
+                }
+            case METADATA_TYPE_STRING:
+                if ((value == null) || (value instanceof String)) {
+                    return putString(key, (String) value);
+                } else {
+                    throw(new IllegalArgumentException("Not a String for key "+ key));
+                }
+            case METADATA_TYPE_RATING:
+                mEditorMetadata.putParcelable(String.valueOf(key), (Parcelable)value);
+                mMetadataChanged = true;
+                break;
+            case METADATA_TYPE_BITMAP:
+                if ((value == null) || (value instanceof Bitmap))  {
+                    return putBitmap(key, (Bitmap) value);
+                } else {
+                    throw(new IllegalArgumentException("Not a Bitmap for key "+ key));
+                }
+            default:
+                throw(new IllegalArgumentException("Invalid key "+ key));
+        }
+        return this;
+    }
+
+
+    /**
+     * Returns the long value for the key.
+     * @param key one of the keys supported in {@link #putLong(int, long)}
+     * @param defaultValue the value returned if the key is not present
+     * @return the long value for the key, or the supplied default value if the key is not present
+     * @throws IllegalArgumentException
+     */
+    public synchronized long getLong(int key, long defaultValue)
+            throws IllegalArgumentException {
+        if (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID) != METADATA_TYPE_LONG) {
+            throw(new IllegalArgumentException("Invalid type 'long' for key "+ key));
+        }
+        return mEditorMetadata.getLong(String.valueOf(key), defaultValue);
+    }
+
+    /**
+     * Returns the {@link String} value for the key.
+     * @param key one of the keys supported in {@link #putString(int, String)}
+     * @param defaultValue the value returned if the key is not present
+     * @return the {@link String} value for the key, or the supplied default value if the key is
+     *     not present
+     * @throws IllegalArgumentException
+     */
+    public synchronized String getString(int key, String defaultValue)
+            throws IllegalArgumentException {
+        if (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID) != METADATA_TYPE_STRING) {
+            throw(new IllegalArgumentException("Invalid type 'String' for key "+ key));
+        }
+        return mEditorMetadata.getString(String.valueOf(key), defaultValue);
+    }
+
+    /**
+     * Returns the {@link Bitmap} value for the key.
+     * @param key the {@link #BITMAP_KEY_ARTWORK} key
+     * @param defaultValue the value returned if the key is not present
+     * @return the {@link Bitmap} value for the key, or the supplied default value if the key is
+     *     not present
+     * @throws IllegalArgumentException
+     */
+    public synchronized Bitmap getBitmap(int key, Bitmap defaultValue)
+            throws IllegalArgumentException {
+        if (key != BITMAP_KEY_ARTWORK) {
+            throw(new IllegalArgumentException("Invalid type 'Bitmap' for key "+ key));
+        }
+        return (mEditorArtwork != null ? mEditorArtwork : defaultValue);
+    }
+
+    /**
+     * Returns an object representation of the value for the key
+     * @param key one of the keys supported in {@link #putObject(int, Object)}
+     * @param defaultValue the value returned if the key is not present
+     * @return the object for the key, as a {@link Long}, {@link Bitmap}, {@link String}, or
+     *     {@link Rating} depending on the key value, or the supplied default value if the key is
+     *     not present
+     * @throws IllegalArgumentException
+     */
+    public synchronized Object getObject(int key, Object defaultValue)
+            throws IllegalArgumentException {
+        switch (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID)) {
+            case METADATA_TYPE_LONG:
+                if (mEditorMetadata.containsKey(String.valueOf(key))) {
+                    return mEditorMetadata.getLong(String.valueOf(key));
+                } else {
+                    return defaultValue;
+                }
+            case METADATA_TYPE_STRING:
+                if (mEditorMetadata.containsKey(String.valueOf(key))) {
+                    return mEditorMetadata.getString(String.valueOf(key));
+                } else {
+                    return defaultValue;
+                }
+            case METADATA_TYPE_RATING:
+                if (mEditorMetadata.containsKey(String.valueOf(key))) {
+                    return mEditorMetadata.getParcelable(String.valueOf(key));
+                } else {
+                    return defaultValue;
+                }
+            case METADATA_TYPE_BITMAP:
+                // only one key for Bitmap supported, value is not stored in mEditorMetadata Bundle
+                if (key == BITMAP_KEY_ARTWORK) {
+                    return (mEditorArtwork != null ? mEditorArtwork : defaultValue);
+                } // else: fall through to invalid key handling
+            default:
+                throw(new IllegalArgumentException("Invalid key "+ key));
+        }
+    }
+
+
+    /**
+     * @hide
+     */
+    protected static final int METADATA_TYPE_INVALID = -1;
+    /**
+     * @hide
+     */
+    protected static final int METADATA_TYPE_LONG = 0;
+
+    /**
+     * @hide
+     */
+    protected static final int METADATA_TYPE_STRING = 1;
+
+    /**
+     * @hide
+     */
+    protected static final int METADATA_TYPE_BITMAP = 2;
+
+    /**
+     * @hide
+     */
+    protected static final int METADATA_TYPE_RATING = 3;
+
+    /**
+     * @hide
+     */
+    protected static final SparseIntArray METADATA_KEYS_TYPE;
+
+    static {
+        METADATA_KEYS_TYPE = new SparseIntArray(17);
+        // NOTE: if adding to the list below, make sure you increment the array initialization size
+        // keys with long values
+        METADATA_KEYS_TYPE.put(
+                MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DURATION, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_YEAR, METADATA_TYPE_LONG);
+        // keys with String values
+        METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_ALBUM, METADATA_TYPE_STRING);
+        METADATA_KEYS_TYPE.put(
+                MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, METADATA_TYPE_STRING);
+        METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_TITLE, METADATA_TYPE_STRING);
+        METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_ARTIST, METADATA_TYPE_STRING);
+        METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_AUTHOR, METADATA_TYPE_STRING);
+        METADATA_KEYS_TYPE.put(
+                MediaMetadataRetriever.METADATA_KEY_COMPILATION, METADATA_TYPE_STRING);
+        METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_COMPOSER, METADATA_TYPE_STRING);
+        METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DATE, METADATA_TYPE_STRING);
+        METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_GENRE, METADATA_TYPE_STRING);
+        METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_WRITER, METADATA_TYPE_STRING);
+        // keys with Bitmap values
+        METADATA_KEYS_TYPE.put(BITMAP_KEY_ARTWORK, METADATA_TYPE_BITMAP);
+        // keys with Rating values
+        METADATA_KEYS_TYPE.put(RATING_KEY_BY_OTHERS, METADATA_TYPE_RATING);
+        METADATA_KEYS_TYPE.put(RATING_KEY_BY_USER, METADATA_TYPE_RATING);
+    }
+}
diff --git a/media/java/android/media/Rating.aidl b/media/java/android/media/Rating.aidl
new file mode 100644
index 0000000..1dc336a
--- /dev/null
+++ b/media/java/android/media/Rating.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+parcelable Rating;
diff --git a/media/java/android/media/Rating.java b/media/java/android/media/Rating.java
new file mode 100644
index 0000000..48443ff
--- /dev/null
+++ b/media/java/android/media/Rating.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * A class to encapsulate rating information used as content metadata.
+ * A rating is defined by its rating style (see {@link #RATING_HEART},
+ * {@link #RATING_THUMB_UP_DOWN}, {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
+ * {@link #RATING_5_STARS} or {@link #RATING_PERCENTAGE}) and the actual rating value (which may
+ * be defined as "unrated"), both of which are defined when the rating instance is constructed
+ * through one of the factory methods.
+ */
+public final class Rating implements Parcelable {
+
+    private final static String TAG = "Rating";
+
+    /**
+     * A rating style with a single degree of rating, "heart" vs "no heart". Can be used to
+     * indicate the content referred to is a favorite (or not).
+     */
+    public final static int RATING_HEART = 1;
+
+    /**
+     * A rating style for "thumb up" vs "thumb down".
+     */
+    public final static int RATING_THUMB_UP_DOWN = 2;
+
+    /**
+     * A rating style with 0 to 3 stars.
+     */
+    public final static int RATING_3_STARS = 3;
+
+    /**
+     * A rating style with 0 to 4 stars.
+     */
+    public final static int RATING_4_STARS = 4;
+
+    /**
+     * A rating style with 0 to 5 stars.
+     */
+    public final static int RATING_5_STARS = 5;
+
+    /**
+     * A rating style expressed as a percentage.
+     */
+    public final static int RATING_PERCENTAGE = 6;
+
+    private final static float RATING_NOT_RATED = -1.0f;
+
+    private final int mRatingStyle;
+
+    private final float mRatingValue;
+
+    private Rating(int ratingStyle, float rating) {
+        mRatingStyle = ratingStyle;
+        mRatingValue = rating;
+    }
+
+    @Override
+    public int describeContents() {
+        return mRatingStyle;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mRatingStyle);
+        dest.writeFloat(mRatingValue);
+    }
+
+    public static final Parcelable.Creator<Rating> CREATOR
+            = new Parcelable.Creator<Rating>() {
+        /**
+         * Rebuilds a Rating previously stored with writeToParcel().
+         * @param p    Parcel object to read the Rating from
+         * @return a new Rating created from the data in the parcel
+         */
+        public Rating createFromParcel(Parcel p) {
+            return new Rating(p.readInt(), p.readFloat());
+        }
+        public Rating[] newArray(int size) {
+            return new Rating[size];
+        }
+    };
+
+    /**
+     * Return a Rating instance with no rating.
+     * Create and return a new Rating instance with no rating known for the given
+     * rating style.
+     * @param ratingStyle one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN},
+     *    {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
+     *    or {@link #RATING_PERCENTAGE}.
+     * @return null if an invalid rating style is passed, a new Rating instance otherwise.
+     */
+    public static Rating newUnratedRating(int ratingStyle) {
+        switch(ratingStyle) {
+            case RATING_HEART:
+            case RATING_THUMB_UP_DOWN:
+            case RATING_3_STARS:
+            case RATING_4_STARS:
+            case RATING_5_STARS:
+            case RATING_PERCENTAGE:
+                return new Rating(ratingStyle, RATING_NOT_RATED);
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Return a Rating instance with a heart-based rating.
+     * Create and return a new Rating instance with a rating style of {@link #RATING_HEART},
+     * and a heart-based rating.
+     * @param hasHeart true for a "heart selected" rating, false for "heart unselected".
+     * @return a new Rating instance.
+     */
+    public static Rating newHeartRating(boolean hasHeart) {
+        return new Rating(RATING_HEART, hasHeart ? 1.0f : 0.0f);
+    }
+
+    /**
+     * Return a Rating instance with a thumb-based rating.
+     * Create and return a new Rating instance with a {@link #RATING_THUMB_UP_DOWN}
+     * rating style, and a "thumb up" or "thumb down" rating.
+     * @param thumbIsUp true for a "thumb up" rating, false for "thumb down".
+     * @return a new Rating instance.
+     */
+    public static Rating newThumbRating(boolean thumbIsUp) {
+        return new Rating(RATING_THUMB_UP_DOWN, thumbIsUp ? 1.0f : 0.0f);
+    }
+
+    /**
+     * Return a Rating instance with a star-based rating.
+     * Create and return a new Rating instance with one of the star-base rating styles
+     * and the given integer or fractional number of stars. Non integer values can for instance
+     * be used to represent an average rating value, which might not be an integer number of stars.
+     * @param starRatingStyle one of {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
+     *     {@link #RATING_5_STARS}.
+     * @param starRating a number ranging from 0.0f to 3.0f, 4.0f or 5.0f according to
+     *     the rating style.
+     * @return null if the rating style is invalid, or the rating is out of range,
+     *     a new Rating instance otherwise.
+     */
+    public static Rating newStarRating(int starRatingStyle, float starRating) {
+        float maxRating = -1.0f;
+        switch(starRatingStyle) {
+            case RATING_3_STARS:
+                maxRating = 3.0f;
+                break;
+            case RATING_4_STARS:
+                maxRating = 4.0f;
+                break;
+            case RATING_5_STARS:
+                maxRating = 5.0f;
+                break;
+            default:
+                Log.e(TAG, "Invalid rating style (" + starRatingStyle + ") for a star rating");
+                        return null;
+        }
+        if ((starRating < 0.0f) || (starRating > maxRating)) {
+            Log.e(TAG, "Trying to set out of range star-based rating");
+            return null;
+        }
+        return new Rating(starRatingStyle, starRating);
+    }
+
+    /**
+     * Return a Rating instance with a percentage-based rating.
+     * Create and return a new Rating instance with a {@link #RATING_PERCENTAGE}
+     * rating style, and a rating of the given percentage.
+     * @param percent the value of the rating
+     * @return null if the rating is out of range, a new Rating instance otherwise.
+     */
+    public static Rating newPercentageRating(float percent) {
+        if ((percent < 0.0f) || (percent > 100.0f)) {
+            Log.e(TAG, "Invalid percentage-based rating value");
+            return null;
+        } else {
+            return new Rating(RATING_PERCENTAGE, percent);
+        }
+    }
+
+    /**
+     * Return whether there is a rating value available.
+     * @return true if the instance was not created with {@link #newUnratedRating(int)}.
+     */
+    public boolean isRated() {
+        return mRatingValue >= 0.0f;
+    }
+
+    /**
+     * Return the rating style.
+     * @return one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN},
+     *    {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
+     *    or {@link #RATING_PERCENTAGE}.
+     */
+    public int getRatingStyle() {
+        return mRatingStyle;
+    }
+
+    /**
+     * Return whether the rating is "heart selected".
+     * @return true if the rating is "heart selected", false if the rating is "heart unselected",
+     *    if the rating style is not {@link #RATING_HEART} or if it is unrated.
+     */
+    public boolean hasHeart() {
+        if (mRatingStyle != RATING_HEART) {
+            return false;
+        } else {
+            return (mRatingValue == 1.0f);
+        }
+    }
+
+    /**
+     * Return whether the rating is "thumb up".
+     * @return true if the rating is "thumb up", false if the rating is "thumb down",
+     *    if the rating style is not {@link #RATING_THUMB_UP_DOWN} or if it is unrated.
+     */
+    public boolean isThumbUp() {
+        if (mRatingStyle != RATING_THUMB_UP_DOWN) {
+            return false;
+        } else {
+            return (mRatingValue == 1.0f);
+        }
+    }
+
+    /**
+     * Return the star-based rating value.
+     * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is
+     *    not star-based, or if it is unrated.
+     */
+    public float getStarRating() {
+        switch (mRatingStyle) {
+            case RATING_3_STARS:
+            case RATING_4_STARS:
+            case RATING_5_STARS:
+                if (isRated()) {
+                    return mRatingValue;
+                }
+            default:
+                return -1.0f;
+        }
+    }
+
+    /**
+     * Return the percentage-based rating value.
+     * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is
+     *    not percentage-based, or if it is unrated.
+     */
+    public float getPercentRating() {
+        if ((mRatingStyle != RATING_PERCENTAGE) || !isRated()) {
+            return -1.0f;
+        } else {
+            return mRatingValue;
+        }
+    }
+}
\ No newline at end of file
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 58f5d55..f8faf3a 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -30,6 +30,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -385,27 +386,6 @@
         mEventHandler = new EventHandler(this, looper);
     }
 
-    private static final int[] METADATA_KEYS_TYPE_STRING = {
-        MediaMetadataRetriever.METADATA_KEY_ALBUM,
-        MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST,
-        MediaMetadataRetriever.METADATA_KEY_TITLE,
-        MediaMetadataRetriever.METADATA_KEY_ARTIST,
-        MediaMetadataRetriever.METADATA_KEY_AUTHOR,
-        MediaMetadataRetriever.METADATA_KEY_COMPILATION,
-        MediaMetadataRetriever.METADATA_KEY_COMPOSER,
-        MediaMetadataRetriever.METADATA_KEY_DATE,
-        MediaMetadataRetriever.METADATA_KEY_GENRE,
-        MediaMetadataRetriever.METADATA_KEY_TITLE,
-        MediaMetadataRetriever.METADATA_KEY_WRITER };
-    private static final int[] METADATA_KEYS_TYPE_LONG = {
-        MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
-        MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER,
-        MediaMetadataRetriever.METADATA_KEY_DURATION,
-        MediaMetadataRetriever.METADATA_KEY_YEAR,
-        MetadataEditor.LONG_KEY_RATING_TYPE,
-        MetadataEditor.LONG_KEY_RATING_BY_OTHERS,
-        MetadataEditor.LONG_KEY_RATING_BY_USER};
-
     /**
      * Class used to modify metadata in a {@link RemoteControlClient} object.
      * Use {@link RemoteControlClient#editMetadata(boolean)} to create an instance of an editor,
@@ -414,28 +394,7 @@
      * for the associated client. Once the metadata has been "applied", you cannot reuse this
      * instance of the MetadataEditor.
      */
-    public class MetadataEditor {
-        /**
-         * Mask of editable keys.
-         */
-        private long mEditableKeys;
-        /**
-         * @hide
-         */
-        protected boolean mMetadataChanged;
-        /**
-         * @hide
-         */
-        protected boolean mArtworkChanged;
-        /**
-         * @hide
-         */
-        protected Bitmap mEditorArtwork;
-        /**
-         * @hide
-         */
-        protected Bundle mEditorMetadata;
-        private boolean mApplied = false;
+    public class MetadataEditor extends MediaMetadataEditor {
 
         // only use RemoteControlClient.editMetadata() to get a MetadataEditor instance
         private MetadataEditor() { }
@@ -450,73 +409,10 @@
          * The metadata key for the content artwork / album art.
          */
         public final static int BITMAP_KEY_ARTWORK = 100;
-        /**
-         * The metadata key qualifying the content rating.
-         * The value associated with this key may be: {@link #RATING_HEART},
-         * {@link #RATING_THUMB_UP_DOWN}, or a non-null positive integer expressing a maximum
-         * number of "stars" for the rating, for which a typical value is 3 or 5.
-         */
-        public final static int LONG_KEY_RATING_TYPE = 101;
-        /**
-         * The metadata key for the content's average rating, not the user's rating.
-         * The value associated with this key may be: an integer value between 0 and 100,
-         * or {@link #RATING_NOT_RATED} to express that no average rating is available.
-         * <p></p>
-         * Note that a rating value up to 100 is not incompatible with a rating type using up
-         * to 5 stars for instance, as the average may be an non-integer number of stars.
-         * <p></p>
-         * When the rating type is:
-         * <ul>
-         * <li>{@link #RATING_HEART}, a rating of 51 to 100 means "heart selected",</li>
-         * <li>{@link #RATING_THUMB_UP_DOWN}, a rating of 0 to 50 means "thumb down",
-         *     51 to 100 means "thumb up"</li>
-         * <li>a non-null positive integer, the rating value is mapped to the number of stars, e.g.
-         *     with a maximum of 5 stars, a rating of 0 maps to 0 stars, 1 to 20 maps to 1 star,
-         *     21 to 40 maps to 2 stars, etc.</li>
-         * </ul>
-         * @see #LONG_KEY_RATING_BY_USER
-         */
-        public final static int LONG_KEY_RATING_BY_OTHERS = 102;
-
-        // editable keys
-        /**
-         * @hide
-         * Editable key mask
-         */
-        public final static int KEY_EDITABLE_MASK = 0x1FFFFFFF;
-        /**
-         * The metadata key for the content's user rating.
-         * The value associated with this key may be: an integer value between 0 and 100,
-         * or {@link #RATING_NOT_RATED} to express that the user hasn't rated this content.
-         * Rules for the interpretation of the rating value according to the rating style are
-         * the same as for {@link #LONG_KEY_RATING_BY_OTHERS}.
-         * This key can be flagged as "editable" (with {@link #addEditableKey(int)}) to enable
-         * receiving user rating values through the
-         * {@link android.media.RemoteControlClient.OnMetadataUpdateListener} interface.
-         */
-        public final static int LONG_KEY_RATING_BY_USER = 0x10000001;
-
-        /**
-         * A rating style with a single degree of rating, "heart" vs "no heart". Can be used to
-         * indicate the content referred to is a favorite (or not).
-         * @see #LONG_KEY_RATING_TYPE
-         */
-        public final static long RATING_HEART = -1;
-        /**
-         * A rating style for "thumb up" vs "thumb down".
-         * @see #LONG_KEY_RATING_TYPE
-         */
-        public final static long RATING_THUMB_UP_DOWN = -2;
-        /**
-         * A rating value indicating no rating is available.
-         * @see #LONG_KEY_RATING_BY_OTHERS
-         * @see #LONG_KEY_RATING_BY_USER
-         */
-        public final static long RATING_NOT_RATED = -101;
 
         /**
          * @hide
-         * TODO(jmtrivi) have lockscreen and music move to the new key name
+         * TODO(jmtrivi) have lockscreen move to the new key name and remove
          */
         public final static int METADATA_KEY_ARTWORK = BITMAP_KEY_ARTWORK;
 
@@ -543,15 +439,7 @@
          */
         public synchronized MetadataEditor putString(int key, String value)
                 throws IllegalArgumentException {
-            if (mApplied) {
-                Log.e(TAG, "Can't edit a previously applied MetadataEditor");
-                return this;
-            }
-            if (!validTypeForKey(key, METADATA_KEYS_TYPE_STRING)) {
-                throw(new IllegalArgumentException("Invalid type 'String' for key "+ key));
-            }
-            mEditorMetadata.putString(String.valueOf(key), value);
-            mMetadataChanged = true;
+            super.putString(key, value);
             return this;
         }
 
@@ -564,9 +452,7 @@
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} (with a value
          *      expressed in milliseconds),
-         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR},
-         *      {@link #LONG_KEY_RATING_BY_OTHERS}, {@link #LONG_KEY_RATING_BY_USER},
-         *      {@link #LONG_KEY_RATING_TYPE}.
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
          * @param value The long value for the given key
          * @return Returns a reference to the same MetadataEditor object, so you can chain put
          *      calls together.
@@ -574,15 +460,7 @@
          */
         public synchronized MetadataEditor putLong(int key, long value)
                 throws IllegalArgumentException {
-            if (mApplied) {
-                Log.e(TAG, "Can't edit a previously applied MetadataEditor");
-                return this;
-            }
-            if (!validTypeForKey(key, METADATA_KEYS_TYPE_LONG)) {
-                throw(new IllegalArgumentException("Invalid type 'long' for key "+ key));
-            }
-            mEditorMetadata.putLong(String.valueOf(key), value);
-            mMetadataChanged = true;
+            super.putLong(key, value);
             return this;
         }
 
@@ -596,69 +474,22 @@
          * @throws IllegalArgumentException
          * @see android.graphics.Bitmap
          */
+        @Override
         public synchronized MetadataEditor putBitmap(int key, Bitmap bitmap)
                 throws IllegalArgumentException {
-            if (mApplied) {
-                Log.e(TAG, "Can't edit a previously applied MetadataEditor");
-                return this;
-            }
-            if (key != BITMAP_KEY_ARTWORK) {
-                throw(new IllegalArgumentException("Invalid type 'Bitmap' for key "+ key));
-            }
-            mEditorArtwork = bitmap;
-            mArtworkChanged = true;
+            super.putBitmap(key, bitmap);
             return this;
         }
 
         /**
-         * Clears all the metadata that has been set since the MetadataEditor instance was
-         *     created with {@link RemoteControlClient#editMetadata(boolean)}.
+         * Clears all the metadata that has been set since the MetadataEditor instance was created
+         * (with {@link RemoteControlClient#editMetadata(boolean)}).
          * Note that clearing the metadata doesn't reset the editable keys
-         * (use {@link #clearEditableKeys()} instead).
+         * (use {@link MediaMetadataEditor#removeEditableKeys()} instead).
          */
+        @Override
         public synchronized void clear() {
-            if (mApplied) {
-                Log.e(TAG, "Can't clear a previously applied MetadataEditor");
-                return;
-            }
-            mEditorMetadata.clear();
-            mEditorArtwork = null;
-        }
-
-        /**
-         * Flag the given key as being editable.
-         * This will declare the metadata field as eligible to be updated, with new values
-         * received through the {@link RemoteControlClient.OnMetadataUpdateListener} interface.
-         * @param key the type of metadata that can be edited. The supported key is
-         *     {@link #LONG_KEY_RATING_BY_USER}.
-         */
-        public synchronized void addEditableKey(int key) {
-            if (mApplied) {
-                Log.e(TAG, "Can't change editable keys of a previously applied MetadataEditor");
-                return;
-            }
-            // only one editable key at the moment, so we're not wasting memory on an array
-            // of editable keys to check the validity of the key, just hardcode the supported key.
-            if (key == MetadataEditor.LONG_KEY_RATING_BY_USER) {
-                mEditableKeys |= (MetadataEditor.KEY_EDITABLE_MASK & key);
-                mMetadataChanged = true;
-            } else {
-                Log.e(TAG, "Metadata key " + key + " cannot be edited");
-            }
-        }
-
-        /**
-         * Causes all metadata fields to be read-only.
-         */
-        public synchronized void clearEditableKeys() {
-            if (mApplied) {
-                Log.e(TAG, "Can't clear editable keys of a previously applied MetadataEditor");
-                return;
-            }
-            if (mEditableKeys != 0) {
-                mEditableKeys = 0;
-                mMetadataChanged = true;
-            }
+            super.clear();
         }
 
         /**
@@ -881,30 +712,17 @@
     /**
      * Interface definition for a callback to be invoked when one of the metadata values has
      * been updated.
+     * Implement this interface to receive metadata updates after registering your listener
+     * through {@link RemoteControlClient#setMetadataUpdateListener(OnMetadataUpdateListener)}.
      */
     public interface OnMetadataUpdateListener  {
         /**
          * Called on the implementer to notify that the metadata field for the given key has
-         * been updated to the new value of type <code>long</long>.
-         * @param key the identifier of the updated metadata field of type <code>long</long>.
-         * @param newValue the new <code>long</long> value for the key
+         * been updated to the new value.
+         * @param key the identifier of the updated metadata field.
+         * @param newValue the Object storing the new value for the key.
          */
-        void onMetadataUpdateLong(int key, long newValue);
-        /**
-         * Called on the implementer to notify that the metadata field for the given key has
-         * been updated to the new <code>String</long>.
-         * @param key the identifier of the updated metadata field of type <code>String</long>.
-         * @param newValue the new <code>String</long> value for the key
-         */
-        void onMetadataUpdateString(int key, String newValue);
-        /**
-         * Called on the implementer to notify that the metadata field for the given key has
-         * been updated to the new {@link android.graphics.Bitmap}.
-         * @param key the identifier of the updated metadata field of type
-         *     {@link android.graphics.Bitmap}.
-         * @param newValue the new {@link android.graphics.Bitmap} for the key
-         */
-        void onMetadataUpdateBitmap(int key, Bitmap newValue);
+        public abstract void onMetadataUpdate(int key, Object newValue);
     }
 
     /**
@@ -1338,12 +1156,11 @@
             }
         }
 
-        public void updateMetadata(int generationId, int key, long value) {
+        public void updateMetadata(int generationId, int key, Rating value) {
             // only post messages, we can't block here
             if (mEventHandler != null) {
                 mEventHandler.sendMessage(mEventHandler.obtainMessage(
-                        MSG_UPDATE_METADATA_LONG, generationId /* arg1 */, key /* arg2*/,
-                        new Long(value)));
+                        MSG_UPDATE_METADATA, generationId /* arg1 */, key /* arg2*/, value));
             }
         }
     };
@@ -1389,7 +1206,7 @@
     private final static int MSG_SEEK_TO = 10;
     private final static int MSG_POSITION_DRIFT_CHECK = 11;
     private final static int MSG_DISPLAY_WANTS_POS_SYNC = 12;
-    private final static int MSG_UPDATE_METADATA_LONG = 13;
+    private final static int MSG_UPDATE_METADATA = 13;
 
     private class EventHandler extends Handler {
         public EventHandler(RemoteControlClient rcc, Looper looper) {
@@ -1443,8 +1260,8 @@
                 case MSG_DISPLAY_WANTS_POS_SYNC:
                     onDisplayWantsSync((IRemoteControlDisplay)msg.obj, msg.arg1 == 1);
                     break;
-                case MSG_UPDATE_METADATA_LONG:
-                    onUpdateMetadata(msg.arg1, msg.arg2, ((Long)msg.obj).longValue());
+                case MSG_UPDATE_METADATA:
+                    onUpdateMetadata(msg.arg1, msg.arg2, msg.obj);
                     break;
                 default:
                     Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler");
@@ -1725,10 +1542,10 @@
         }
     }
 
-    private void onUpdateMetadata(int generationId, int key, long value) {
+    private void onUpdateMetadata(int generationId, int key, Object value) {
         synchronized (mCacheLock) {
             if ((mCurrentClientGenId == generationId) && (mMetadataUpdateListener != null)) {
-                mMetadataUpdateListener.onMetadataUpdateLong(key, value);
+                mMetadataUpdateListener.onMetadataUpdate(key, value);
             }
         }
     }
@@ -1771,24 +1588,6 @@
         return bitmap;
     }
 
-    /**
-     *  Fast routine to go through an array of allowed keys and return whether the key is part
-     *  of that array
-     * @param key the key value
-     * @param validKeys the array of valid keys for a given type
-     * @return true if the key is part of the array, false otherwise
-     */
-    private static boolean validTypeForKey(int key, int[] validKeys) {
-        try {
-            for (int i = 0 ; ; i++) {
-                if (key == validKeys[i]) {
-                    return true;
-                }
-            }
-        } catch (ArrayIndexOutOfBoundsException e) {
-            return false;
-        }
-    }
 
     /**
      * Returns whether, for the given playback state, the playback position is expected to
diff --git a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
index 6badaaf..8cdcb7a 100644
--- a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
@@ -102,6 +102,8 @@
                 == TelephonyManager.CALL_STATE_OFFHOOK) {
             mLockPatternUtils.resumeCall();
         } else {
+            final boolean bypassHandler = true;
+            KeyguardUpdateMonitor.getInstance(mContext).reportEmergencyCallAction(bypassHandler);
             Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                     | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java
index 3e499b2..c2cd32f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java
@@ -295,6 +295,13 @@
                 }
             }
         }
+
+        @Override
+        public void onEmergencyCallAction() {
+            if (mBiometricUnlock != null) {
+                mBiometricUnlock.stop();
+            }
+        }
     };
 
     @Override
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index dcec654..f4bbf9a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -91,6 +91,7 @@
     private static final int MSG_SET_CURRENT_CLIENT_ID = 315;
     protected static final int MSG_SET_PLAYBACK_STATE = 316;
     protected static final int MSG_USER_INFO_CHANGED = 317;
+    protected static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318;
 
 
     private static KeyguardUpdateMonitor sInstance;
@@ -181,6 +182,9 @@
                 case MSG_USER_INFO_CHANGED:
                     handleUserInfoChanged(msg.arg1);
                     break;
+                case MSG_REPORT_EMERGENCY_CALL_ACTION:
+                    handleReportEmergencyCallAction();
+                    break;
             }
         }
     };
@@ -758,6 +762,18 @@
         }
     }
 
+    /**
+     * Handle {@link #MSG_REPORT_EMERGENCY_CALL_ACTION}
+     */
+    private void handleReportEmergencyCallAction() {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onEmergencyCallAction();
+            }
+        }
+    }
+
     public boolean isKeyguardVisible() {
         return mKeyguardIsVisible;
     }
@@ -902,6 +918,22 @@
         handleSimStateChange(new SimArgs(IccCardConstants.State.READY));
     }
 
+    /**
+     * Report that the emergency call button has been pressed and the emergency dialer is
+     * about to be displayed.
+     *
+     * @param bypassHandler runs immediately.
+     *
+     * NOTE: Must be called from UI thread if bypassHandler == true.
+     */
+    public void reportEmergencyCallAction(boolean bypassHandler) {
+        if (!bypassHandler) {
+            mHandler.obtainMessage(MSG_REPORT_EMERGENCY_CALL_ACTION).sendToTarget();
+        } else {
+            handleReportEmergencyCallAction();
+        }
+    }
+
     public CharSequence getTelephonyPlmn() {
         return mTelephonyPlmn;
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 30b43f5..b0511e5 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -131,4 +131,8 @@
      */
     public void onMusicPlaybackStateChanged(int playbackState, long eventTime) { }
 
+    /**
+     * Called when the emergency call button is pressed.
+     */
+    void onEmergencyCallAction() { }
 }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index 4333aea..8ab4645 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -414,12 +414,25 @@
             // write anything and wait for the user to fix the range which will
             // trigger an update.
             mRequestedPages = mEditor.getRequestedPages();
-            if (mRequestedPages == null) {
+            if (mRequestedPages == null || mRequestedPages.length == 0) {
                 mEditor.updateUi();
                 if (mEditor.isDone()) {
                     PrintJobConfigActivity.this.finish();
                 }
                 return;
+            } else {
+                // If print is not confirmed we just ask for the first of the
+                // selected pages to emulate a behavior that shows preview
+                // increasing the chances that apps will implement the APIs
+                // correctly.
+                if (!mEditor.isPrintConfirmed()) {
+                    if (ALL_PAGES_ARRAY.equals(mRequestedPages)) {
+                        mRequestedPages = new PageRange[] {new PageRange(0, 0)};
+                    } else {
+                        final int firstPage = mRequestedPages[0].getStart();
+                        mRequestedPages = new PageRange[] {new PageRange(firstPage, firstPage)};
+                    }
+                }
             }
 
             // If the info and the layout did not change and we already have
@@ -1461,8 +1474,12 @@
 
                     if (dashIndex > 0) {
                         fromIndex = Integer.parseInt(range.substring(0, dashIndex)) - 1;
-                        toIndex = Integer.parseInt(range.substring(
-                                dashIndex + 1, range.length())) - 1;
+                        // It is possible that the dash is at the end since the input
+                        // verification can has to allow the user to keep entering if
+                        // this would lead to a valid input. So we handle this.
+                        toIndex = (dashIndex < range.length() - 1)
+                                ? Integer.parseInt(range.substring(dashIndex + 1,
+                                        range.length())) - 1 : fromIndex;
                     } else {
                         fromIndex = toIndex = Integer.parseInt(range) - 1;
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 2257617..2be8ee5 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -292,11 +292,7 @@
         c.drawRect(mFrame, mBatteryPaint);
         c.restore();
 
-        if (level <= EMPTY) {
-            final float x = mWidth * 0.5f;
-            final float y = (mHeight + mWarningTextHeight) * 0.48f;
-            c.drawText(mWarningString, x, y, mWarningTextPaint);
-        } else if (tracker.plugged) {
+        if (tracker.plugged) {
             // draw the bolt
             final int bl = (int)(mFrame.left + width / 4f);
             final int bt = (int)(mFrame.top + height / 6f);
@@ -319,6 +315,10 @@
                         mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
             }
             c.drawPath(mBoltPath, mBoltPaint);
+        } else if (level <= EMPTY) {
+            final float x = mWidth * 0.5f;
+            final float y = (mHeight + mWarningTextHeight) * 0.48f;
+            c.drawText(mWarningString, x, y, mWarningTextPaint);
         } else if (mShowPercent && !(tracker.level == 100 && !SHOW_100_PERCENT)) {
             mTextPaint.setTextSize(height *
                     (SINGLE_DIGIT_PERCENT ? 0.75f
diff --git a/packages/SystemUI/src/com/android/systemui/recent/ColorDrawableWithDimensions.java b/packages/SystemUI/src/com/android/systemui/recent/ColorDrawableWithDimensions.java
new file mode 100644
index 0000000..b4d3edd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/ColorDrawableWithDimensions.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recent;
+
+import android.graphics.drawable.ColorDrawable;
+
+public class ColorDrawableWithDimensions extends ColorDrawable {
+    private int mWidth;
+    private int mHeight;
+
+    public ColorDrawableWithDimensions(int color, int width, int height) {
+        super(color);
+        mWidth = width;
+        mHeight = height;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mWidth;
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mHeight;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java b/packages/SystemUI/src/com/android/systemui/recent/FadedEdgeDrawHelper.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java
rename to packages/SystemUI/src/com/android/systemui/recent/FadedEdgeDrawHelper.java
index f17766b..1cfc892 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/FadedEdgeDrawHelper.java
@@ -30,7 +30,7 @@
 
 import com.android.systemui.R;
 
-public class RecentsScrollViewPerformanceHelper {
+public class FadedEdgeDrawHelper {
     public static final boolean OPTIMIZE_SW_RENDERED_RECENTS = true;
     public static final boolean USE_DARK_FADE_IN_HW_ACCELERATED_MODE = true;
     private View mScrollView;
@@ -43,18 +43,18 @@
     private Matrix mFadeMatrix;
     private LinearGradient mFade;
 
-    public static RecentsScrollViewPerformanceHelper create(Context context,
+    public static FadedEdgeDrawHelper create(Context context,
             AttributeSet attrs, View scrollView, boolean isVertical) {
         boolean isTablet = context.getResources().
                 getBoolean(R.bool.config_recents_interface_for_tablets);
         if (!isTablet && (OPTIMIZE_SW_RENDERED_RECENTS || USE_DARK_FADE_IN_HW_ACCELERATED_MODE)) {
-            return new RecentsScrollViewPerformanceHelper(context, attrs, scrollView, isVertical);
+            return new FadedEdgeDrawHelper(context, attrs, scrollView, isVertical);
         } else {
             return null;
         }
     }
 
-    public RecentsScrollViewPerformanceHelper(Context context,
+    public FadedEdgeDrawHelper(Context context,
             AttributeSet attrs, View scrollView, boolean isVertical) {
         mScrollView = scrollView;
         TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View);
@@ -64,7 +64,7 @@
     }
 
     public void onAttachedToWindowCallback(
-            RecentsCallback callback, LinearLayout layout, boolean hardwareAccelerated) {
+            LinearLayout layout, boolean hardwareAccelerated) {
         mSoftwareRendered = !hardwareAccelerated;
         if ((mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS)
                 || USE_DARK_FADE_IN_HW_ACCELERATED_MODE) {
@@ -178,11 +178,11 @@
         }
     }
 
-    public int getVerticalFadingEdgeLengthCallback() {
+    public int getVerticalFadingEdgeLength() {
         return mFadingEdgeLength;
     }
 
-    public int getHorizontalFadingEdgeLengthCallback() {
+    public int getHorizontalFadingEdgeLength() {
         return mFadingEdgeLength;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index 8b2cd3f..c714d8b 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -25,7 +25,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Handler;
@@ -62,8 +62,8 @@
     private Handler mHandler;
 
     private int mIconDpi;
-    private Bitmap mDefaultThumbnailBackground;
-    private Bitmap mDefaultIconBackground;
+    private ColorDrawableWithDimensions mDefaultThumbnailBackground;
+    private ColorDrawableWithDimensions mDefaultIconBackground;
     private int mNumTasksInFirstScreenful = Integer.MAX_VALUE;
 
     private boolean mFirstScreenful;
@@ -100,7 +100,7 @@
         // Render default icon (just a blank image)
         int defaultIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.app_icon_size);
         int iconSize = (int) (defaultIconSize * mIconDpi / res.getDisplayMetrics().densityDpi);
-        mDefaultIconBackground = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
+        mDefaultIconBackground = new ColorDrawableWithDimensions(0x00000000, iconSize, iconSize);
 
         // Render the default thumbnail background
         int thumbnailWidth =
@@ -110,9 +110,7 @@
         int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background);
 
         mDefaultThumbnailBackground =
-                Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.ARGB_8888);
-        Canvas c = new Canvas(mDefaultThumbnailBackground);
-        c.drawColor(color);
+                new ColorDrawableWithDimensions(color, thumbnailWidth, thumbnailHeight);
     }
 
     public void setRecentsPanel(RecentsPanelView newRecentsPanel, RecentsPanelView caller) {
@@ -125,11 +123,11 @@
         }
     }
 
-    public Bitmap getDefaultThumbnail() {
+    public Drawable getDefaultThumbnail() {
         return mDefaultThumbnailBackground;
     }
 
-    public Bitmap getDefaultIcon() {
+    public Drawable getDefaultIcon() {
         return mDefaultIconBackground;
     }
 
@@ -199,7 +197,7 @@
                 + td + ": " + thumbnail);
         synchronized (td) {
             if (thumbnail != null) {
-                td.setThumbnail(thumbnail);
+                td.setThumbnail(new BitmapDrawable(mContext.getResources(), thumbnail));
             } else {
                 td.setThumbnail(mDefaultThumbnailBackground);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Recents.java b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
index f51e34b..f5670e1 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
@@ -22,7 +22,10 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -68,7 +71,14 @@
                 }
 
             } else {
-                Bitmap first = firstTask.getThumbnail();
+                Bitmap first = null;
+                if (firstTask.getThumbnail() instanceof BitmapDrawable) {
+                    first = ((BitmapDrawable) firstTask.getThumbnail()).getBitmap();
+                } else {
+                    first = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+                    Drawable d = RecentTasksLoader.getInstance(mContext).getDefaultThumbnail();
+                    d.draw(new Canvas(first));
+                }
                 final Resources res = mContext.getResources();
 
                 float thumbWidth = res
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index 217b7fd..be42bc0 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -49,16 +49,17 @@
     private RecentsCallback mCallback;
     protected int mLastScrollPosition;
     private SwipeHelper mSwipeHelper;
-    private RecentsScrollViewPerformanceHelper mPerformanceHelper;
+    private FadedEdgeDrawHelper mFadedEdgeDrawHelper;
     private HashSet<View> mRecycledViews;
     private int mNumItemsInOneScreenful;
+    private Runnable mOnScrollListener;
 
     public RecentsHorizontalScrollView(Context context, AttributeSet attrs) {
         super(context, attrs, 0);
         float densityScale = getResources().getDisplayMetrics().density;
         float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
         mSwipeHelper = new SwipeHelper(SwipeHelper.Y, this, densityScale, pagingTouchSlop);
-        mPerformanceHelper = RecentsScrollViewPerformanceHelper.create(context, attrs, this, false);
+        mFadedEdgeDrawHelper = FadedEdgeDrawHelper.create(context, attrs, this, false);
         mRecycledViews = new HashSet<View>();
     }
 
@@ -108,8 +109,8 @@
 
             final View view = mAdapter.getView(i, old, mLinearLayout);
 
-            if (mPerformanceHelper != null) {
-                mPerformanceHelper.addViewCallback(view);
+            if (mFadedEdgeDrawHelper != null) {
+                mFadedEdgeDrawHelper.addViewCallback(view);
             }
 
             OnTouchListener noOpListener = new OnTouchListener() {
@@ -234,26 +235,10 @@
     }
 
     @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
+    public void drawFadedEdges(Canvas canvas, int left, int right, int top, int bottom) {
+        if (mFadedEdgeDrawHelper != null) {
 
-        if (mPerformanceHelper != null) {
-            int paddingLeft = mPaddingLeft;
-            final boolean offsetRequired = isPaddingOffsetRequired();
-            if (offsetRequired) {
-                paddingLeft += getLeftPaddingOffset();
-            }
-
-            int left = mScrollX + paddingLeft;
-            int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
-            int top = mScrollY + getFadeTop(offsetRequired);
-            int bottom = top + getFadeHeight(offsetRequired);
-
-            if (offsetRequired) {
-                right += getRightPaddingOffset();
-                bottom += getBottomPaddingOffset();
-            }
-            mPerformanceHelper.drawCallback(canvas,
+            mFadedEdgeDrawHelper.drawCallback(canvas,
                     left, right, top, bottom, mScrollX, mScrollY,
                     0, 0,
                     getLeftFadingEdgeStrength(), getRightFadingEdgeStrength(), mPaddingTop);
@@ -261,9 +246,21 @@
     }
 
     @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+       super.onScrollChanged(l, t, oldl, oldt);
+       if (mOnScrollListener != null) {
+           mOnScrollListener.run();
+       }
+    }
+
+    public void setOnScrollListener(Runnable listener) {
+        mOnScrollListener = listener;
+    }
+
+    @Override
     public int getVerticalFadingEdgeLength() {
-        if (mPerformanceHelper != null) {
-            return mPerformanceHelper.getVerticalFadingEdgeLengthCallback();
+        if (mFadedEdgeDrawHelper != null) {
+            return mFadedEdgeDrawHelper.getVerticalFadingEdgeLength();
         } else {
             return super.getVerticalFadingEdgeLength();
         }
@@ -271,8 +268,8 @@
 
     @Override
     public int getHorizontalFadingEdgeLength() {
-        if (mPerformanceHelper != null) {
-            return mPerformanceHelper.getHorizontalFadingEdgeLengthCallback();
+        if (mFadedEdgeDrawHelper != null) {
+            return mFadedEdgeDrawHelper.getHorizontalFadingEdgeLength();
         } else {
             return super.getHorizontalFadingEdgeLength();
         }
@@ -290,9 +287,8 @@
 
     @Override
     public void onAttachedToWindow() {
-        if (mPerformanceHelper != null) {
-            mPerformanceHelper.onAttachedToWindowCallback(
-                    mCallback, mLinearLayout, isHardwareAccelerated());
+        if (mFadedEdgeDrawHelper != null) {
+            mFadedEdgeDrawHelper.onAttachedToWindowCallback(mLinearLayout, isHardwareAccelerated());
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 5ebd11e0..788e843 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -30,6 +30,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Shader.TileMode;
 import android.graphics.drawable.BitmapDrawable;
@@ -74,7 +75,7 @@
     private PopupMenu mPopup;
     private View mRecentsScrim;
     private View mRecentsNoApps;
-    private ViewGroup mRecentsContainer;
+    private RecentsScrollView mRecentsContainer;
 
     private boolean mShowing;
     private boolean mWaitingToShow;
@@ -98,6 +99,8 @@
         public void setCallback(RecentsCallback callback);
         public void setMinSwipeAlpha(float minAlpha);
         public View findViewForTask(int persistentTaskId);
+        public void drawFadedEdges(Canvas c, int left, int right, int top, int bottom);
+        public void setOnScrollListener(Runnable listener);
     }
 
     private final class OnLongClickDelegate implements View.OnLongClickListener {
@@ -113,7 +116,7 @@
     /* package */ final static class ViewHolder {
         View thumbnailView;
         ImageView thumbnailViewImage;
-        Bitmap thumbnailViewImageBitmap;
+        Drawable thumbnailViewDrawable;
         ImageView iconView;
         TextView labelView;
         TextView descriptionView;
@@ -151,7 +154,7 @@
             // the thumbnail later (if they both have the same dimensions)
             updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false);
             holder.iconView = (ImageView) convertView.findViewById(R.id.app_icon);
-            holder.iconView.setImageBitmap(mRecentTasksLoader.getDefaultIcon());
+            holder.iconView.setImageDrawable(mRecentTasksLoader.getDefaultIcon());
             holder.labelView = (TextView) convertView.findViewById(R.id.app_label);
             holder.calloutLine = convertView.findViewById(R.id.recents_callout_line);
             holder.descriptionView = (TextView) convertView.findViewById(R.id.app_description);
@@ -227,7 +230,7 @@
         public void recycleView(View v) {
             ViewHolder holder = (ViewHolder) v.getTag();
             updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false);
-            holder.iconView.setImageBitmap(mRecentTasksLoader.getDefaultIcon());
+            holder.iconView.setImageDrawable(mRecentTasksLoader.getDefaultIcon());
             holder.iconView.setVisibility(INVISIBLE);
             holder.iconView.animate().cancel();
             holder.labelView.setText(null);
@@ -270,13 +273,7 @@
     }
 
     public int numItemsInOneScreenful() {
-        if (mRecentsContainer instanceof RecentsScrollView){
-            RecentsScrollView scrollView
-                    = (RecentsScrollView) mRecentsContainer;
-            return scrollView.numItemsInOneScreenful();
-        }  else {
-            throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
-        }
+        return mRecentsContainer.numItemsInOneScreenful();
     }
 
     private boolean pointInside(int x, int y, View v) {
@@ -288,7 +285,7 @@
     }
 
     public boolean isInContentArea(int x, int y) {
-        return pointInside(x, y, mRecentsContainer);
+        return pointInside(x, y, (View) mRecentsContainer);
     }
 
     public void show(boolean show) {
@@ -436,16 +433,16 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mRecentsContainer = (ViewGroup) findViewById(R.id.recents_container);
+        mRecentsContainer = (RecentsScrollView) findViewById(R.id.recents_container);
+        mRecentsContainer.setOnScrollListener(new Runnable() {
+            public void run() {
+                // need to redraw the faded edges
+                invalidate();
+            }
+        });
         mListAdapter = new TaskDescriptionAdapter(mContext);
-        if (mRecentsContainer instanceof RecentsScrollView){
-            RecentsScrollView scrollView
-                    = (RecentsScrollView) mRecentsContainer;
-            scrollView.setAdapter(mListAdapter);
-            scrollView.setCallback(this);
-        } else {
-            throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
-        }
+        mRecentsContainer.setAdapter(mListAdapter);
+        mRecentsContainer.setCallback(this);
 
         mRecentsScrim = findViewById(R.id.recents_bg_protect);
         mRecentsNoApps = findViewById(R.id.recents_no_apps);
@@ -462,11 +459,7 @@
     }
 
     public void setMinSwipeAlpha(float minAlpha) {
-        if (mRecentsContainer instanceof RecentsScrollView){
-            RecentsScrollView scrollView
-                = (RecentsScrollView) mRecentsContainer;
-            scrollView.setMinSwipeAlpha(minAlpha);
-        }
+        mRecentsContainer.setMinSwipeAlpha(minAlpha);
     }
 
     private void createCustomAnimations(LayoutTransition transitioner) {
@@ -488,23 +481,23 @@
         }
     }
 
-    private void updateThumbnail(ViewHolder h, Bitmap thumbnail, boolean show, boolean anim) {
+    private void updateThumbnail(ViewHolder h, Drawable thumbnail, boolean show, boolean anim) {
         if (thumbnail != null) {
             // Should remove the default image in the frame
             // that this now covers, to improve scrolling speed.
             // That can't be done until the anim is complete though.
-            h.thumbnailViewImage.setImageBitmap(thumbnail);
+            h.thumbnailViewImage.setImageDrawable(thumbnail);
 
             // scale the image to fill the full width of the ImageView. do this only if
             // we haven't set a bitmap before, or if the bitmap size has changed
-            if (h.thumbnailViewImageBitmap == null ||
-                h.thumbnailViewImageBitmap.getWidth() != thumbnail.getWidth() ||
-                h.thumbnailViewImageBitmap.getHeight() != thumbnail.getHeight()) {
+            if (h.thumbnailViewDrawable == null ||
+                h.thumbnailViewDrawable.getIntrinsicWidth() != thumbnail.getIntrinsicWidth() ||
+                h.thumbnailViewDrawable.getIntrinsicHeight() != thumbnail.getIntrinsicHeight()) {
                 if (mFitThumbnailToXY) {
                     h.thumbnailViewImage.setScaleType(ScaleType.FIT_XY);
                 } else {
                     Matrix scaleMatrix = new Matrix();
-                    float scale = mThumbnailWidth / (float) thumbnail.getWidth();
+                    float scale = mThumbnailWidth / (float) thumbnail.getIntrinsicWidth();
                     scaleMatrix.setScale(scale, scale);
                     h.thumbnailViewImage.setScaleType(ScaleType.MATRIX);
                     h.thumbnailViewImage.setImageMatrix(scaleMatrix);
@@ -517,14 +510,14 @@
                 }
                 h.thumbnailView.setVisibility(View.VISIBLE);
             }
-            h.thumbnailViewImageBitmap = thumbnail;
+            h.thumbnailViewDrawable = thumbnail;
         }
     }
 
     void onTaskThumbnailLoaded(TaskDescription td) {
         synchronized (td) {
             if (mRecentsContainer != null) {
-                ViewGroup container = mRecentsContainer;
+                ViewGroup container = (ViewGroup) mRecentsContainer;
                 if (container instanceof RecentsScrollView) {
                     container = (ViewGroup) container.findViewById(
                             R.id.recents_linear_layout);
@@ -633,7 +626,7 @@
         final int items = mRecentTaskDescriptions != null
                 ? mRecentTaskDescriptions.size() : 0;
 
-        mRecentsContainer.setVisibility(items > 0 ? View.VISIBLE : View.GONE);
+        ((View) mRecentsContainer).setVisibility(items > 0 ? View.VISIBLE : View.GONE);
 
         // Set description for accessibility
         int numRecentApps = mRecentTaskDescriptions != null
@@ -650,33 +643,33 @@
     }
 
     public boolean simulateClick(int persistentTaskId) {
-        if (mRecentsContainer instanceof RecentsScrollView){
-            RecentsScrollView scrollView
-                = (RecentsScrollView) mRecentsContainer;
-            View v = scrollView.findViewForTask(persistentTaskId);
-            if (v != null) {
-                handleOnClick(v);
-                return true;
-            }
+        View v = mRecentsContainer.findViewForTask(persistentTaskId);
+        if (v != null) {
+            handleOnClick(v);
+            return true;
         }
         return false;
     }
 
     public void handleOnClick(View view) {
-        ViewHolder holder = (ViewHolder)view.getTag();
+        ViewHolder holder = (ViewHolder) view.getTag();
         TaskDescription ad = holder.taskDescription;
         final Context context = view.getContext();
         final ActivityManager am = (ActivityManager)
                 context.getSystemService(Context.ACTIVITY_SERVICE);
-        Bitmap bm = holder.thumbnailViewImageBitmap;
-        boolean usingDrawingCache;
-        if (bm.getWidth() == holder.thumbnailViewImage.getWidth() &&
-                bm.getHeight() == holder.thumbnailViewImage.getHeight()) {
-            usingDrawingCache = false;
-        } else {
+
+        Bitmap bm = null;
+        boolean usingDrawingCache = true;
+        if (holder.thumbnailViewDrawable instanceof BitmapDrawable) {
+            bm = ((BitmapDrawable) holder.thumbnailViewDrawable).getBitmap();
+            if (bm.getWidth() == holder.thumbnailViewImage.getWidth() &&
+                    bm.getHeight() == holder.thumbnailViewImage.getHeight()) {
+                usingDrawingCache = false;
+            }
+        }
+        if (usingDrawingCache) {
             holder.thumbnailViewImage.setDrawingCacheEnabled(true);
             bm = holder.thumbnailViewImage.getDrawingCache();
-            usingDrawingCache = true;
         }
         Bundle opts = (bm == null) ?
                 null :
@@ -771,7 +764,7 @@
         popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
             public boolean onMenuItemClick(MenuItem item) {
                 if (item.getItemId() == R.id.recent_remove_item) {
-                    mRecentsContainer.removeViewInLayout(selectedView);
+                    ((ViewGroup) mRecentsContainer).removeViewInLayout(selectedView);
                 } else if (item.getItemId() == R.id.recent_inspect_item) {
                     ViewHolder viewHolder = (ViewHolder) selectedView.getTag();
                     if (viewHolder != null) {
@@ -795,4 +788,26 @@
         });
         popup.show();
     }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
+
+        int paddingLeft = mPaddingLeft;
+        final boolean offsetRequired = isPaddingOffsetRequired();
+        if (offsetRequired) {
+            paddingLeft += getLeftPaddingOffset();
+        }
+
+        int left = mScrollX + paddingLeft;
+        int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
+        int top = mScrollY + getFadeTop(offsetRequired);
+        int bottom = top + getFadeHeight(offsetRequired);
+
+        if (offsetRequired) {
+            right += getRightPaddingOffset();
+            bottom += getBottomPaddingOffset();
+        }
+        mRecentsContainer.drawFadedEdges(canvas, left, right, top, bottom);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index ee076d9..6dddc39 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -49,9 +49,10 @@
     private RecentsCallback mCallback;
     protected int mLastScrollPosition;
     private SwipeHelper mSwipeHelper;
-    private RecentsScrollViewPerformanceHelper mPerformanceHelper;
+    private FadedEdgeDrawHelper mFadedEdgeDrawHelper;
     private HashSet<View> mRecycledViews;
     private int mNumItemsInOneScreenful;
+    private Runnable mOnScrollListener;
 
     public RecentsVerticalScrollView(Context context, AttributeSet attrs) {
         super(context, attrs, 0);
@@ -59,7 +60,7 @@
         float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
 
-        mPerformanceHelper = RecentsScrollViewPerformanceHelper.create(context, attrs, this, true);
+        mFadedEdgeDrawHelper = FadedEdgeDrawHelper.create(context, attrs, this, true);
         mRecycledViews = new HashSet<View>();
     }
 
@@ -112,8 +113,8 @@
             }
             final View view = mAdapter.getView(i, old, mLinearLayout);
 
-            if (mPerformanceHelper != null) {
-                mPerformanceHelper.addViewCallback(view);
+            if (mFadedEdgeDrawHelper != null) {
+                mFadedEdgeDrawHelper.addViewCallback(view);
             }
 
             OnTouchListener noOpListener = new OnTouchListener() {
@@ -243,36 +244,32 @@
     }
 
     @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
-
-        if (mPerformanceHelper != null) {
-            int paddingLeft = mPaddingLeft;
+    public void drawFadedEdges(Canvas canvas, int left, int right, int top, int bottom) {
+        if (mFadedEdgeDrawHelper != null) {
             final boolean offsetRequired = isPaddingOffsetRequired();
-            if (offsetRequired) {
-                paddingLeft += getLeftPaddingOffset();
-            }
-
-            int left = mScrollX + paddingLeft;
-            int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
-            int top = mScrollY + getFadeTop(offsetRequired);
-            int bottom = top + getFadeHeight(offsetRequired);
-
-            if (offsetRequired) {
-                right += getRightPaddingOffset();
-                bottom += getBottomPaddingOffset();
-            }
-            mPerformanceHelper.drawCallback(canvas,
-                    left, right, top, bottom, mScrollX, mScrollY,
+            mFadedEdgeDrawHelper.drawCallback(canvas,
+                    left, right, top + getFadeTop(offsetRequired), bottom, mScrollX, mScrollY,
                     getTopFadingEdgeStrength(), getBottomFadingEdgeStrength(),
                     0, 0, mPaddingTop);
         }
     }
 
     @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+       super.onScrollChanged(l, t, oldl, oldt);
+       if (mOnScrollListener != null) {
+           mOnScrollListener.run();
+       }
+    }
+
+    public void setOnScrollListener(Runnable listener) {
+        mOnScrollListener = listener;
+    }
+
+    @Override
     public int getVerticalFadingEdgeLength() {
-        if (mPerformanceHelper != null) {
-            return mPerformanceHelper.getVerticalFadingEdgeLengthCallback();
+        if (mFadedEdgeDrawHelper != null) {
+            return mFadedEdgeDrawHelper.getVerticalFadingEdgeLength();
         } else {
             return super.getVerticalFadingEdgeLength();
         }
@@ -280,8 +277,8 @@
 
     @Override
     public int getHorizontalFadingEdgeLength() {
-        if (mPerformanceHelper != null) {
-            return mPerformanceHelper.getHorizontalFadingEdgeLengthCallback();
+        if (mFadedEdgeDrawHelper != null) {
+            return mFadedEdgeDrawHelper.getHorizontalFadingEdgeLength();
         } else {
             return super.getHorizontalFadingEdgeLength();
         }
@@ -299,9 +296,8 @@
 
     @Override
     public void onAttachedToWindow() {
-        if (mPerformanceHelper != null) {
-            mPerformanceHelper.onAttachedToWindowCallback(
-                    mCallback, mLinearLayout, isHardwareAccelerated());
+        if (mFadedEdgeDrawHelper != null) {
+            mFadedEdgeDrawHelper.onAttachedToWindowCallback(mLinearLayout, isHardwareAccelerated());
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
index 7e979b7..2bc2821 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
@@ -29,7 +29,7 @@
     final String packageName; // used to override animations (see onClick())
     final CharSequence description;
 
-    private Bitmap mThumbnail; // generated by Activity.onCreateThumbnail()
+    private Drawable mThumbnail; // generated by Activity.onCreateThumbnail()
     private Drawable mIcon; // application package icon
     private CharSequence mLabel; // application package label
     private boolean mLoaded;
@@ -85,11 +85,11 @@
         mIcon = icon;
     }
 
-    public void setThumbnail(Bitmap thumbnail) {
+    public void setThumbnail(Drawable thumbnail) {
         mThumbnail = thumbnail;
     }
 
-    public Bitmap getThumbnail() {
+    public Drawable getThumbnail() {
         return mThumbnail;
     }
 }
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
index c6c4a94..67b2307 100644
--- a/services/java/com/android/server/AppOpsService.java
+++ b/services/java/com/android/server/AppOpsService.java
@@ -113,7 +113,7 @@
             uid = _uid;
             packageName = _packageName;
             op = _op;
-            mode = AppOpsManager.MODE_ALLOWED;
+            mode = AppOpsManager.opToDefaultMode(op);
         }
     }
 
@@ -191,7 +191,7 @@
         mHandler = new Handler();
         readState();
     }
-    
+
     public void publish(Context context) {
         mContext = context;
         ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
@@ -379,7 +379,7 @@
                         }
                         repCbs.addAll(cbs);
                     }
-                    if (mode == AppOpsManager.MODE_ALLOWED) {
+                    if (mode == AppOpsManager.opToDefaultMode(op.op)) {
                         // If going into the default mode, prune this op
                         // if there is nothing else interesting in it.
                         pruneOp(op, uid, packageName);
@@ -435,8 +435,8 @@
                     Ops pkgOps = ent.getValue();
                     for (int j=pkgOps.size()-1; j>=0; j--) {
                         Op curOp = pkgOps.valueAt(j);
-                        if (curOp.mode != AppOpsManager.MODE_ALLOWED) {
-                            curOp.mode = AppOpsManager.MODE_ALLOWED;
+                        if (curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
+                            curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
                             changed = true;
                             callbacks = addCallbacks(callbacks, packageName, curOp.op,
                                     mOpModeWatchers.get(curOp.op));
@@ -545,7 +545,7 @@
         synchronized (this) {
             Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
             if (op == null) {
-                return AppOpsManager.MODE_ALLOWED;
+                return AppOpsManager.opToDefaultMode(code);
             }
             return op.mode;
         }
@@ -947,7 +947,7 @@
                             AppOpsManager.OpEntry op = ops.get(j);
                             out.startTag(null, "op");
                             out.attribute(null, "n", Integer.toString(op.getOp()));
-                            if (op.getMode() != AppOpsManager.MODE_ALLOWED) {
+                            if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
                                 out.attribute(null, "m", Integer.toString(op.getMode()));
                             }
                             long time = op.getTime();
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 2bac96e..80f4b00 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -14749,7 +14749,7 @@
     }
 
     private final void setProcessTrackerState(ProcessRecord proc, int memFactor, long now) {
-        if (proc.thread != null) {
+        if (proc.thread != null && proc.baseProcessTracker != null) {
             proc.baseProcessTracker.setState(proc.repProcState, memFactor, now, proc.pkgList);
         }
     }
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index f5d45b3..7756ff9 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1417,8 +1417,8 @@
                     final ActivityStack lastStack = getLastStack();
                     ActivityRecord curTop = lastStack == null?
                             null : lastStack.topRunningNonDelayedActivityLocked(notTop);
-                    if (curTop != null && (curTop.task != intentActivity.task) ||
-                            curTop.task != lastStack.topTask()) {
+                    if (curTop != null && (curTop.task != intentActivity.task ||
+                            curTop.task != lastStack.topTask())) {
                         r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
                         if (sourceRecord == null || (sourceStack.topActivity() != null &&
                                 sourceStack.topActivity().task == sourceRecord.task)) {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 4fdacb3..d230779 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -567,7 +567,8 @@
      */
     public boolean addPackage(String pkg, ProcessStatsService tracker) {
         if (!pkgList.containsKey(pkg)) {
-            pkgList.put(pkg, tracker.getProcessStateLocked(pkg, info.uid, processName));
+            pkgList.put(pkg, baseProcessTracker != null
+                    ? tracker.getProcessStateLocked(pkg, info.uid, processName) : null);
             return true;
         }
         return false;
@@ -592,25 +593,30 @@
      *  Delete all packages from list except the package indicated in info
      */
     public void resetPackageList(ProcessStatsService tracker) {
-        long now = SystemClock.uptimeMillis();
-        baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
-                tracker.getMemFactorLocked(), now, pkgList);
         final int N = pkgList.size();
-        if (N != 1) {
-            for (int i=0; i<N; i++) {
-                ProcessStats.ProcessState ps = pkgList.valueAt(i);
-                if (ps != null && ps != baseProcessTracker) {
-                    ps.makeInactive();
-                }
+        if (baseProcessTracker != null) {
+            long now = SystemClock.uptimeMillis();
+            baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
+                    tracker.getMemFactorLocked(), now, pkgList);
+            if (N != 1) {
+                for (int i=0; i<N; i++) {
+                    ProcessStats.ProcessState ps = pkgList.valueAt(i);
+                    if (ps != null && ps != baseProcessTracker) {
+                        ps.makeInactive();
+                    }
 
+                }
+                pkgList.clear();
+                ProcessStats.ProcessState ps = tracker.getProcessStateLocked(
+                        info.packageName, info.uid, processName);
+                pkgList.put(info.packageName, ps);
+                if (thread != null && ps != baseProcessTracker) {
+                    ps.makeActive();
+                }
             }
+        } else if (N != 1) {
             pkgList.clear();
-            ProcessStats.ProcessState ps = tracker.getProcessStateLocked(
-                    info.packageName, info.uid, processName);
-            pkgList.put(info.packageName, ps);
-            if (thread != null && ps != baseProcessTracker) {
-                ps.makeActive();
-            }
+            pkgList.put(info.packageName, null);
         }
     }
     
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index dbd42af..69ff449 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -923,7 +923,8 @@
                     //that was created later or a window at the top of the list of
                     //windows associated with this token.
                     if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
-                            "Adding window " + win + " at " + (newIdx + 1) + " of " + N);
+                            "not Base app: Adding window " + win + " at " + (newIdx + 1) + " of " +
+                            N);
                     windows.add(newIdx + 1, win);
                     if (newIdx < 0) {
                         // No window from token found on win's display.
@@ -1044,7 +1045,7 @@
             }
         }
         if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
-                "Adding window " + win + " at " + i + " of " + N);
+                "Based on layer: Adding window " + win + " at " + i + " of " + N);
         windows.add(i, win);
         mWindowsChanged = true;
         return tokenWindowsPos;
@@ -1063,7 +1064,7 @@
         }
         i++;
         if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
-                "Adding window " + win + " at " + i + " of " + windows.size());
+                "Free window: Adding window " + win + " at " + i + " of " + windows.size());
         windows.add(i, win);
         mWindowsChanged = true;
     }
@@ -1130,6 +1131,8 @@
     }
 
     private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) {
+        if (DEBUG_FOCUS_LIGHT) Slog.d(TAG, "addWindowToListInOrderLocked: win=" + win +
+                " Callers=" + Debug.getCallers(4));
         if (win.mAttachedWindow == null) {
             final WindowToken token = win.mToken;
             int tokenWindowsPos = 0;
@@ -1185,16 +1188,15 @@
         // same display. Or even when the current IME/target are not on the same screen as the next
         // IME/target. For now only look for input windows on the main screen.
         WindowList windows = getDefaultWindowListLocked();
-        final int N = windows.size();
         WindowState w = null;
-        int i = N;
-        while (i > 0) {
-            i--;
-            w = windows.get(i);
+        int i;
+        for (i = windows.size() - 1; i >= 0; --i) {
+            WindowState win = windows.get(i);
 
             if (DEBUG_INPUT_METHOD && willMove) Slog.i(TAG, "Checking window @" + i
-                    + " " + w + " fl=0x" + Integer.toHexString(w.mAttrs.flags));
-            if (canBeImeTarget(w)) {
+                    + " " + win + " fl=0x" + Integer.toHexString(win.mAttrs.flags));
+            if (canBeImeTarget(win)) {
+                w = win;
                 //Slog.i(TAG, "Putting input method here!");
 
                 // Yet more tricksyness!  If this window is a "starting"
@@ -7081,19 +7083,17 @@
                         }
                     }
 
-                    if (lastFocus != newFocus) {
-                        //System.out.println("Changing focus from " + lastFocus
-                        //                   + " to " + newFocus);
-                        if (newFocus != null) {
-                            if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "Gaining focus: " + newFocus);
-                            newFocus.reportFocusChangedSerialized(true, mInTouchMode);
-                            notifyFocusChanged();
-                        }
+                    //System.out.println("Changing focus from " + lastFocus
+                    //                   + " to " + newFocus);
+                    if (newFocus != null) {
+                        if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "Gaining focus: " + newFocus);
+                        newFocus.reportFocusChangedSerialized(true, mInTouchMode);
+                        notifyFocusChanged();
+                    }
 
-                        if (lastFocus != null) {
-                            if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "Losing focus: " + lastFocus);
-                            lastFocus.reportFocusChangedSerialized(false, mInTouchMode);
-                        }
+                    if (lastFocus != null) {
+                        if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "Losing focus: " + lastFocus);
+                        lastFocus.reportFocusChangedSerialized(false, mInTouchMode);
                     }
                 } break;
 
@@ -10277,14 +10277,13 @@
 
     void dumpWindowsNoHeaderLocked(PrintWriter pw, boolean dumpAll,
             ArrayList<WindowState> windows) {
-        int j = 0;
         final int numDisplays = mDisplayContents.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             final WindowList windowList = mDisplayContents.valueAt(displayNdx).getWindowList();
             for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) {
                 final WindowState w = windowList.get(winNdx);
                 if (windows == null || windows.contains(w)) {
-                    pw.print("  Window #"); pw.print(j++); pw.print(' ');
+                    pw.print("  Window #"); pw.print(winNdx); pw.print(' ');
                             pw.print(w); pw.println(":");
                     w.dump(pw, "    ", dumpAll || windows != null);
                 }