Merge "Audio Effect Framework: add effect suspend/restore"
diff --git a/Android.mk b/Android.mk
index ea8314c..752a5f8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -183,6 +183,7 @@
 	media/java/android/media/IAudioFocusDispatcher.aidl \
 	media/java/android/media/IMediaScannerListener.aidl \
 	media/java/android/media/IMediaScannerService.aidl \
+	media/java/android/media/IRemoteControlClient.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
 	telephony/java/com/android/internal/telephony/ITelephony.aidl \
diff --git a/api/current.txt b/api/current.txt
index 6cef3a7..876d555 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15,6 +15,7 @@
     field public static final java.lang.String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
     field public static final java.lang.String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE";
     field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
+    field public static final java.lang.String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL";
     field public static final java.lang.String AUTHENTICATE_ACCOUNTS = "android.permission.AUTHENTICATE_ACCOUNTS";
     field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
     field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
@@ -1031,6 +1032,7 @@
     field public static final int translationY = 16843555; // 0x1010323
     field public static final int type = 16843169; // 0x10101a1
     field public static final int typeface = 16842902; // 0x1010096
+    field public static final int uiOptions = 16843682; // 0x10103a2
     field public static final int uncertainGestureColor = 16843382; // 0x1010276
     field public static final int unfocusedMonthDateColor = 16843588; // 0x1010344
     field public static final int unselectedAlpha = 16843278; // 0x101020e
@@ -1524,15 +1526,15 @@
     field public static final int TextAppearance_Holo_Small = 16974081; // 0x1030101
     field public static final int TextAppearance_Holo_Small_Inverse = 16974082; // 0x1030102
     field public static final int TextAppearance_Holo_Widget = 16974085; // 0x1030105
-    field public static final int TextAppearance_Holo_Widget_ActionBar_Menu = 16974113; // 0x1030121
+    field public static final int TextAppearance_Holo_Widget_ActionBar_Menu = 16974112; // 0x1030120
     field public static final int TextAppearance_Holo_Widget_ActionBar_Subtitle = 16974099; // 0x1030113
-    field public static final int TextAppearance_Holo_Widget_ActionBar_Subtitle_Inverse = 16974110; // 0x103011e
+    field public static final int TextAppearance_Holo_Widget_ActionBar_Subtitle_Inverse = 16974109; // 0x103011d
     field public static final int TextAppearance_Holo_Widget_ActionBar_Title = 16974098; // 0x1030112
-    field public static final int TextAppearance_Holo_Widget_ActionBar_Title_Inverse = 16974109; // 0x103011d
+    field public static final int TextAppearance_Holo_Widget_ActionBar_Title_Inverse = 16974108; // 0x103011c
     field public static final int TextAppearance_Holo_Widget_ActionMode_Subtitle = 16974101; // 0x1030115
-    field public static final int TextAppearance_Holo_Widget_ActionMode_Subtitle_Inverse = 16974112; // 0x1030120
+    field public static final int TextAppearance_Holo_Widget_ActionMode_Subtitle_Inverse = 16974111; // 0x103011f
     field public static final int TextAppearance_Holo_Widget_ActionMode_Title = 16974100; // 0x1030114
-    field public static final int TextAppearance_Holo_Widget_ActionMode_Title_Inverse = 16974111; // 0x103011f
+    field public static final int TextAppearance_Holo_Widget_ActionMode_Title_Inverse = 16974110; // 0x103011e
     field public static final int TextAppearance_Holo_Widget_Button = 16974086; // 0x1030106
     field public static final int TextAppearance_Holo_Widget_DropDownHint = 16974091; // 0x103010b
     field public static final int TextAppearance_Holo_Widget_DropDownItem = 16974092; // 0x103010c
@@ -1586,6 +1588,7 @@
     field public static final int Theme_Holo_Dialog_NoActionBar_MinWidth = 16973938; // 0x1030072
     field public static final int Theme_Holo_InputMethod = 16973951; // 0x103007f
     field public static final int Theme_Holo_Light = 16973934; // 0x103006e
+    field public static final int Theme_Holo_Light_DarkActionBar = 16974105; // 0x1030119
     field public static final int Theme_Holo_Light_Dialog = 16973939; // 0x1030073
     field public static final int Theme_Holo_Light_DialogWhenLarge = 16973945; // 0x1030079
     field public static final int Theme_Holo_Light_DialogWhenLarge_NoActionBar = 16973946; // 0x103007a
@@ -1595,17 +1598,9 @@
     field public static final int Theme_Holo_Light_NoActionBar = 16974064; // 0x10300f0
     field public static final int Theme_Holo_Light_NoActionBar_Fullscreen = 16974065; // 0x10300f1
     field public static final int Theme_Holo_Light_Panel = 16973948; // 0x103007c
-    field public static final int Theme_Holo_Light_SolidActionBar = 16974122; // 0x103012a
-    field public static final int Theme_Holo_Light_SolidActionBar_Inverse = 16974123; // 0x103012b
-    field public static final int Theme_Holo_Light_SolidActionBar_Inverse_SplitActionBarWhenNarrow = 16974126; // 0x103012e
-    field public static final int Theme_Holo_Light_SolidActionBar_SplitActionBarWhenNarrow = 16974125; // 0x103012d
-    field public static final int Theme_Holo_Light_SplitActionBarWhenNarrow = 16974106; // 0x103011a
     field public static final int Theme_Holo_NoActionBar = 16973932; // 0x103006c
     field public static final int Theme_Holo_NoActionBar_Fullscreen = 16973933; // 0x103006d
     field public static final int Theme_Holo_Panel = 16973947; // 0x103007b
-    field public static final int Theme_Holo_SolidActionBar = 16974121; // 0x1030129
-    field public static final int Theme_Holo_SolidActionBar_SplitActionBarWhenNarrow = 16974124; // 0x103012c
-    field public static final int Theme_Holo_SplitActionBarWhenNarrow = 16974105; // 0x1030119
     field public static final int Theme_Holo_Wallpaper = 16973949; // 0x103007d
     field public static final int Theme_Holo_Wallpaper_NoTitleBar = 16973950; // 0x103007e
     field public static final int Theme_InputMethod = 16973908; // 0x1030054
@@ -1656,7 +1651,7 @@
     field public static final int Widget_GridView = 16973874; // 0x1030032
     field public static final int Widget_Holo = 16973962; // 0x103008a
     field public static final int Widget_Holo_ActionBar = 16974004; // 0x10300b4
-    field public static final int Widget_Holo_ActionBar_Solid = 16974114; // 0x1030122
+    field public static final int Widget_Holo_ActionBar_Solid = 16974113; // 0x1030121
     field public static final int Widget_Holo_ActionBar_TabBar = 16974071; // 0x10300f7
     field public static final int Widget_Holo_ActionBar_TabText = 16974070; // 0x10300f6
     field public static final int Widget_Holo_ActionBar_TabView = 16974069; // 0x10300f5
@@ -1668,7 +1663,7 @@
     field public static final int Widget_Holo_AutoCompleteTextView = 16973968; // 0x1030090
     field public static final int Widget_Holo_Button = 16973963; // 0x103008b
     field public static final int Widget_Holo_Button_Borderless = 16974050; // 0x10300e2
-    field public static final int Widget_Holo_Button_Borderless_Small = 16974107; // 0x103011b
+    field public static final int Widget_Holo_Button_Borderless_Small = 16974106; // 0x103011a
     field public static final int Widget_Holo_Button_Inset = 16973965; // 0x103008d
     field public static final int Widget_Holo_Button_Small = 16973964; // 0x103008c
     field public static final int Widget_Holo_Button_Toggle = 16973966; // 0x103008e
@@ -1686,22 +1681,22 @@
     field public static final int Widget_Holo_ImageButton = 16973974; // 0x1030096
     field public static final int Widget_Holo_Light = 16974005; // 0x10300b5
     field public static final int Widget_Holo_Light_ActionBar = 16974049; // 0x10300e1
-    field public static final int Widget_Holo_Light_ActionBar_Solid = 16974115; // 0x1030123
-    field public static final int Widget_Holo_Light_ActionBar_Solid_Inverse = 16974116; // 0x1030124
+    field public static final int Widget_Holo_Light_ActionBar_Solid = 16974114; // 0x1030122
+    field public static final int Widget_Holo_Light_ActionBar_Solid_Inverse = 16974115; // 0x1030123
     field public static final int Widget_Holo_Light_ActionBar_TabBar = 16974074; // 0x10300fa
-    field public static final int Widget_Holo_Light_ActionBar_TabBar_Inverse = 16974117; // 0x1030125
+    field public static final int Widget_Holo_Light_ActionBar_TabBar_Inverse = 16974116; // 0x1030124
     field public static final int Widget_Holo_Light_ActionBar_TabText = 16974073; // 0x10300f9
-    field public static final int Widget_Holo_Light_ActionBar_TabText_Inverse = 16974119; // 0x1030127
+    field public static final int Widget_Holo_Light_ActionBar_TabText_Inverse = 16974118; // 0x1030126
     field public static final int Widget_Holo_Light_ActionBar_TabView = 16974072; // 0x10300f8
-    field public static final int Widget_Holo_Light_ActionBar_TabView_Inverse = 16974118; // 0x1030126
+    field public static final int Widget_Holo_Light_ActionBar_TabView_Inverse = 16974117; // 0x1030125
     field public static final int Widget_Holo_Light_ActionButton = 16974045; // 0x10300dd
     field public static final int Widget_Holo_Light_ActionButton_CloseMode = 16974048; // 0x10300e0
     field public static final int Widget_Holo_Light_ActionButton_Overflow = 16974046; // 0x10300de
     field public static final int Widget_Holo_Light_ActionMode = 16974047; // 0x10300df
-    field public static final int Widget_Holo_Light_ActionMode_Inverse = 16974120; // 0x1030128
+    field public static final int Widget_Holo_Light_ActionMode_Inverse = 16974119; // 0x1030127
     field public static final int Widget_Holo_Light_AutoCompleteTextView = 16974011; // 0x10300bb
     field public static final int Widget_Holo_Light_Button = 16974006; // 0x10300b6
-    field public static final int Widget_Holo_Light_Button_Borderless_Small = 16974108; // 0x103011c
+    field public static final int Widget_Holo_Light_Button_Borderless_Small = 16974107; // 0x103011b
     field public static final int Widget_Holo_Light_Button_Inset = 16974008; // 0x10300b8
     field public static final int Widget_Holo_Light_Button_Small = 16974007; // 0x10300b7
     field public static final int Widget_Holo_Light_Button_Toggle = 16974009; // 0x10300b9
@@ -5637,6 +5632,7 @@
     field public static final int SCREEN_ORIENTATION_SENSOR_PORTRAIT = 7; // 0x7
     field public static final int SCREEN_ORIENTATION_UNSPECIFIED = -1; // 0xffffffff
     field public static final int SCREEN_ORIENTATION_USER = 2; // 0x2
+    field public static final int UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW = 1; // 0x1
     field public int configChanges;
     field public int flags;
     field public int launchMode;
@@ -5646,6 +5642,7 @@
     field public java.lang.String targetActivity;
     field public java.lang.String taskAffinity;
     field public int theme;
+    field public int uiOptions;
   }
 
   public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
@@ -5696,6 +5693,7 @@
     field public int targetSdkVersion;
     field public java.lang.String taskAffinity;
     field public int theme;
+    field public int uiOptions;
     field public int uid;
   }
 
@@ -23162,6 +23160,7 @@
     method public abstract void setTitle(java.lang.CharSequence);
     method public abstract void setTitleColor(int);
     method public void setType(int);
+    method public void setUiOptions(int);
     method public abstract void setVolumeControlStream(int);
     method public void setWindowAnimations(int);
     method public void setWindowManager(android.view.WindowManager, android.os.IBinder, java.lang.String);
@@ -24136,7 +24135,7 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int RESULT_ATTR_IN_THE_DICTIONARY = 1; // 0x1
-    field public static final int RESULT_ATTR_LOOKS_TYPO = 2; // 0x2
+    field public static final int RESULT_ATTR_LOOKS_LIKE_TYPO = 2; // 0x2
   }
 
   public final class TextInfo implements android.os.Parcelable {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d5b669e..98b867d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4399,6 +4399,9 @@
         if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
             mWindow.setSoftInputMode(info.softInputMode);
         }
+        if (info.uiOptions != 0) {
+            mWindow.setUiOptions(info.uiOptions);
+        }
         mUiThread = Thread.currentThread();
         
         mMainThread = aThread;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 8931675..c566104 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2503,6 +2503,7 @@
                     mAvailThumbnailBitmap = thumbnail;
                     thumbnail = null;
                 }
+                cv.setBitmap(null);
             }
 
         } catch (Exception e) {
diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
index 7b9bd60..8eb9ba4 100644
--- a/core/java/android/app/LauncherActivity.java
+++ b/core/java/android/app/LauncherActivity.java
@@ -314,6 +314,7 @@
                     icon.draw(canvas);
                     icon.setBounds(mOldBounds);
                     icon = new BitmapDrawable(getResources(), thumb);
+                    canvas.setBitmap(null);
                 } else if (iconWidth < width && iconHeight < height) {
                     final Bitmap.Config c = Bitmap.Config.ARGB_8888;
                     final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c);
@@ -326,6 +327,7 @@
                     icon.draw(canvas);
                     icon.setBounds(mOldBounds);
                     icon = new BitmapDrawable(getResources(), thumb);
+                    canvas.setBitmap(null);
                 }
             }
 
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 4858f14..bba329d 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -433,7 +433,19 @@
      * the mode from the theme will be used.
      */
     public int softInputMode;
-    
+
+    /**
+     * The desired extra UI options for this activity and its main window.
+     * Set from the {@link android.R.attr#uiOptions} attribute in the
+     * activity's manifest.
+     */
+    public int uiOptions = 0;
+
+    /**
+     * Flag for use with uiOptions.
+     */
+    public static final int UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW = 1;
+
     public ActivityInfo() {
     }
 
@@ -448,6 +460,7 @@
         screenOrientation = orig.screenOrientation;
         configChanges = orig.configChanges;
         softInputMode = orig.softInputMode;
+        uiOptions = orig.uiOptions;
     }
     
     /**
@@ -479,6 +492,9 @@
                     + " configChanges=0x" + Integer.toHexString(configChanges)
                     + " softInputMode=0x" + Integer.toHexString(softInputMode));
         }
+        if (uiOptions != 0) {
+            pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions));
+        }
         super.dumpBack(pw, prefix);
     }
     
@@ -503,6 +519,7 @@
         dest.writeInt(screenOrientation);
         dest.writeInt(configChanges);
         dest.writeInt(softInputMode);
+        dest.writeInt(uiOptions);
     }
 
     public static final Parcelable.Creator<ActivityInfo> CREATOR
@@ -526,5 +543,6 @@
         screenOrientation = source.readInt();
         configChanges = source.readInt();
         softInputMode = source.readInt();
+        uiOptions = source.readInt();
     }
 }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index ddb6ef0..65a8750 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -91,6 +91,13 @@
     public String backupAgentName;
 
     /**
+     * The default extra UI options for activities in this application.
+     * Set from the {@link android.R.attr#uiOptions} attribute in the
+     * activity's manifest.
+     */
+    public int uiOptions = 0;
+
+    /**
      * Value for {@link #flags}: if set, this application is installed in the
      * device's system image.
      */
@@ -456,6 +463,9 @@
         if (descriptionRes != 0) {
             pw.println(prefix + "description=0x"+Integer.toHexString(descriptionRes));
         }
+        if (uiOptions != 0) {
+            pw.println(prefix + "uiOptions=0x" + Integer.toHexString(uiOptions));
+        }
         super.dumpBack(pw, prefix);
     }
     
@@ -509,6 +519,7 @@
         installLocation = orig.installLocation;
         manageSpaceActivityName = orig.manageSpaceActivityName;
         descriptionRes = orig.descriptionRes;
+        uiOptions = orig.uiOptions;
     }
 
 
@@ -547,6 +558,7 @@
         dest.writeString(manageSpaceActivityName);
         dest.writeString(backupAgentName);
         dest.writeInt(descriptionRes);
+        dest.writeInt(uiOptions);
     }
 
     public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -584,6 +596,7 @@
         manageSpaceActivityName = source.readString();
         backupAgentName = source.readString();
         descriptionRes = source.readInt();
+        uiOptions = source.readInt();
     }
 
     /**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 53d6bb1..b6c64cb 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1645,6 +1645,9 @@
             }
         }
 
+        ai.uiOptions = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestApplication_uiOptions, 0);
+
         sa.recycle();
 
         if (outError[0] != null) {
@@ -1850,6 +1853,10 @@
         a.info.theme = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestActivity_theme, 0);
 
+        a.info.uiOptions = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestActivity_uiOptions,
+                a.info.applicationInfo.uiOptions);
+
         String str;
         str = sa.getNonConfigurationString(
                 com.android.internal.R.styleable.AndroidManifestActivity_permission, 0);
@@ -2091,6 +2098,7 @@
         info.screenOrientation = target.info.screenOrientation;
         info.taskAffinity = target.info.taskAffinity;
         info.theme = target.info.theme;
+        info.uiOptions = target.info.uiOptions;
         
         Activity a = new Activity(mParseActivityAliasArgs, info);
         if (outError[0] != null) {
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 7336550..5501f38 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -119,22 +119,22 @@
             switch (msg.what) {
                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                     if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                        if (DBG) {
+                        if (VDBG) {
                             mMdst.log("MdstHandler connected");
                         }
                         mMdst.mDataConnectionTrackerAc = (AsyncChannel) msg.obj;
                     } else {
-                        if (DBG) {
+                        if (VDBG) {
                             mMdst.log("MdstHandler %s NOT connected error=" + msg.arg1);
                         }
                     }
                     break;
                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
-                    mMdst.log("Disconnected from DataStateTracker");
+                    if (VDBG) mMdst.log("Disconnected from DataStateTracker");
                     mMdst.mDataConnectionTrackerAc = null;
                     break;
                 default: {
-                    mMdst.log("Ignorning unknown message=" + msg);
+                    if (VDBG) mMdst.log("Ignorning unknown message=" + msg);
                     break;
                 }
             }
@@ -221,13 +221,13 @@
                             mLinkProperties = intent.getParcelableExtra(
                                     Phone.DATA_LINK_PROPERTIES_KEY);
                             if (mLinkProperties == null) {
-                                log("CONNECTED event did not supply link properties.");
+                                loge("CONNECTED event did not supply link properties.");
                                 mLinkProperties = new LinkProperties();
                             }
                             mLinkCapabilities = intent.getParcelableExtra(
                                     Phone.DATA_LINK_CAPABILITIES_KEY);
                             if (mLinkCapabilities == null) {
-                                log("CONNECTED event did not supply link capabilities.");
+                                loge("CONNECTED event did not supply link capabilities.");
                                 mLinkCapabilities = new LinkCapabilities();
                             }
                             setDetailedState(DetailedState.CONNECTED, reason, apnName);
@@ -238,7 +238,7 @@
                     if (TextUtils.equals(reason, Phone.REASON_LINK_PROPERTIES_CHANGED)) {
                         mLinkProperties = intent.getParcelableExtra(Phone.DATA_LINK_PROPERTIES_KEY);
                         if (mLinkProperties == null) {
-                            log("No link property in LINK_PROPERTIES change event.");
+                            loge("No link property in LINK_PROPERTIES change event.");
                             mLinkProperties = new LinkProperties();
                         }
                         // Just update reason field in this NetworkInfo
@@ -269,7 +269,7 @@
                 setDetailedState(DetailedState.FAILED, reason, apnName);
             } else if (intent.getAction().
                     equals(DataConnectionTracker.ACTION_DATA_CONNECTION_TRACKER_MESSENGER)) {
-                if (DBG) log(mApnType + " got ACTION_DATA_CONNECTION_TRACKER_MESSENGER");
+                if (VDBG) log(mApnType + " got ACTION_DATA_CONNECTION_TRACKER_MESSENGER");
                 mMessenger = intent.getParcelableExtra(DataConnectionTracker.EXTRA_MESSENGER);
                 AsyncChannel ac = new AsyncChannel();
                 ac.connect(mContext, MobileDataStateTracker.this.mHandler, mMessenger);
@@ -437,7 +437,7 @@
          */
         for (int retry = 0; retry < 2; retry++) {
             if (mPhoneService == null) {
-                log("Ignoring mobile radio request because could not acquire PhoneService");
+                loge("Ignoring mobile radio request because could not acquire PhoneService");
                 break;
             }
 
@@ -448,7 +448,7 @@
             }
         }
 
-        log("Could not set radio power to " + (turnOn ? "on" : "off"));
+        loge("Could not set radio power to " + (turnOn ? "on" : "off"));
         return false;
     }
 
@@ -457,12 +457,12 @@
      */
     public void setDataEnable(boolean enabled) {
         try {
-            log("setDataEnable: E enabled=" + enabled);
+            if (DBG) log("setDataEnable: E enabled=" + enabled);
             mDataConnectionTrackerAc.sendMessage(DataConnectionTracker.CMD_SET_DATA_ENABLE,
                     enabled ? DataConnectionTracker.ENABLED : DataConnectionTracker.DISABLED);
-            log("setDataEnable: X enabled=" + enabled);
+            if (VDBG) log("setDataEnable: X enabled=" + enabled);
         } catch (Exception e) {
-            log("setDataEnable: X mAc was null" + e);
+            loge("setDataEnable: X mAc was null" + e);
         }
     }
 
@@ -473,15 +473,15 @@
     public void setDependencyMet(boolean met) {
         Bundle bundle = Bundle.forPair(DataConnectionTracker.APN_TYPE_KEY, mApnType);
         try {
-            log("setDependencyMet: E met=" + met);
+            if (DBG) log("setDependencyMet: E met=" + met);
             Message msg = Message.obtain();
             msg.what = DataConnectionTracker.CMD_SET_DEPENDENCY_MET;
             msg.arg1 = (met ? DataConnectionTracker.ENABLED : DataConnectionTracker.DISABLED);
             msg.setData(bundle);
             mDataConnectionTrackerAc.sendMessage(msg);
-            log("setDependencyMet: X met=" + met);
+            if (VDBG) log("setDependencyMet: X met=" + met);
         } catch (NullPointerException e) {
-            log("setDependencyMet: X mAc was null" + e);
+            loge("setDependencyMet: X mAc was null" + e);
         }
     }
 
@@ -508,7 +508,7 @@
          */
         for (int retry = 0; retry < 2; retry++) {
             if (mPhoneService == null) {
-                log("Ignoring feature request because could not acquire PhoneService");
+                loge("Ignoring feature request because could not acquire PhoneService");
                 break;
             }
 
@@ -523,7 +523,7 @@
             }
         }
 
-        log("Could not " + (enable ? "enable" : "disable") + " APN type \"" + apnType + "\"");
+        loge("Could not " + (enable ? "enable" : "disable") + " APN type \"" + apnType + "\"");
         return Phone.APN_REQUEST_FAILED;
     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 935281a5..ad76928 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10221,6 +10221,7 @@
             }
 
             canvas.restoreToCount(restoreCount);
+            canvas.setBitmap(null);
 
             if (attachInfo != null) {
                 // Restore the cached Canvas for our siblings
@@ -10289,9 +10290,9 @@
         mPrivateFlags = flags;
 
         canvas.restoreToCount(restoreCount);
+        canvas.setBitmap(null);
 
         if (attachInfo != null) {
-            canvas.setBitmap(null);
             // Restore the cached Canvas for our siblings
             attachInfo.mCanvas = canvas;
         }
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index dbcbd6e..9520958 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -220,6 +220,7 @@
     private final int mMaximumDrawingCacheSize;
     private final int mOverscrollDistance;
     private final int mOverflingDistance;
+    private final boolean mFadingMarqueeEnabled;
 
     private boolean sHasPermanentMenuKey;
     private boolean sHasPermanentMenuKeySet;
@@ -246,6 +247,7 @@
         mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE;
         mOverscrollDistance = OVERSCROLL_DISTANCE;
         mOverflingDistance = OVERFLING_DISTANCE;
+        mFadingMarqueeEnabled = true;
     }
 
     /**
@@ -297,6 +299,9 @@
                 sHasPermanentMenuKey = false;
             }
         }
+
+        mFadingMarqueeEnabled = res.getBoolean(
+                com.android.internal.R.bool.config_ui_enableFadingMarquee);
     }
 
     /**
@@ -673,4 +678,12 @@
     public boolean hasPermanentMenuKey() {
         return sHasPermanentMenuKey;
     }
+
+    /**
+     * @hide
+     * @return Whether or not marquee should use fading edges.
+     */
+    public boolean isFadingMarqueeEnabled() {
+        return mFadingMarqueeEnabled;
+    }
 }
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 96e550e..65e72c9 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1094,6 +1094,9 @@
                             }
 
                             public void post(Object... data) {
+                                if (data[1] != null) {
+                                    ((Canvas) data[1]).setBitmap(null);
+                                }
                                 if (data[0] != null) {
                                     ((Bitmap) data[0]).recycle();
                                 }
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index e07085c..6ac679c 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1213,5 +1213,10 @@
      * @see android.app.Activity#getVolumeControlStream()
      */
     public abstract int getVolumeControlStream();
-    
+
+    /**
+     * Set extra options that will influence the UI for this window.
+     * @param uiOptions Flags specifying extra options for this window.
+     */
+    public void setUiOptions(int uiOptions) { }
 }
diff --git a/core/java/android/view/textservice/SuggestionsInfo.java b/core/java/android/view/textservice/SuggestionsInfo.java
index 3332f1e..ed0f89d 100644
--- a/core/java/android/view/textservice/SuggestionsInfo.java
+++ b/core/java/android/view/textservice/SuggestionsInfo.java
@@ -34,9 +34,9 @@
     /**
      * Flag of the attributes of the suggestions that can be obtained by
      * {@link #getSuggestionsAttributes}: this tells that the text service thinks the requested
-     * word looks a typo.
+     * word looks like a typo.
      */
-    public static final int RESULT_ATTR_LOOKS_TYPO = 0x0002;
+    public static final int RESULT_ATTR_LOOKS_LIKE_TYPO = 0x0002;
     private final int mSuggestionsAttributes;
     private final String[] mSuggestions;
     private final boolean mSuggestionsAvailable;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index f4fd551..7620a63 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -8405,9 +8405,9 @@
         }
     }
 
-    // Called by JNI to invalidate the View, given rectangle coordinates in
-    // content space
-    private void pageSwapCallback() {
+    /** @hide Called by JNI when pages are swapped (only occurs with hardware
+     * acceleration) */
+    protected void pageSwapCallback() {
         if (inEditingMode()) {
             didUpdateWebTextViewDimensions(ANYWHERE);
         }
@@ -8426,11 +8426,11 @@
         WebViewCore.ViewState viewState = draw.mViewState;
         boolean isPictureAfterFirstLayout = viewState != null;
 
-        // Request a callback on pageSwap (to reposition the webtextview)
-        boolean registerPageSwapCallback =
-            !mZoomManager.isFixedLengthAnimationInProgress() && inEditingMode();
-
         if (updateBaseLayer) {
+            // Request a callback on pageSwap (to reposition the webtextview)
+            boolean registerPageSwapCallback =
+                !mZoomManager.isFixedLengthAnimationInProgress() && inEditingMode();
+
             setBaseLayer(draw.mBaseLayer, draw.mInvalRegion,
                     getSettings().getShowVisualIndicator(),
                     isPictureAfterFirstLayout, registerPageSwapCallback);
@@ -9084,6 +9084,16 @@
         }
     }
 
+    /** @hide send content invalidate */
+    protected void contentInvalidateAll() {
+        mWebViewCore.sendMessage(EventHub.CONTENT_INVALIDATE_ALL);
+    }
+
+    /** @hide call pageSwapCallback upon next page swap */
+    protected void registerPageSwapCallback() {
+        nativeRegisterPageSwapCallback();
+    }
+
     /**
      * Begin collecting per-tile profiling data
      *
@@ -9245,6 +9255,7 @@
     private native void     nativeStopGL();
     private native Rect     nativeSubtractLayers(Rect content);
     private native int      nativeTextGeneration();
+    private native void     nativeRegisterPageSwapCallback();
     private native void     nativeTileProfilingStart();
     private native float    nativeTileProfilingStop();
     private native void     nativeTileProfilingClear();
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 8d8023b..400cdbd 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -999,6 +999,7 @@
         static final int DUMP_V8COUNTERS = 173;
 
         static final int SET_JS_FLAGS = 174;
+        static final int CONTENT_INVALIDATE_ALL = 175;
         // Geolocation
         static final int GEOLOCATION_PERMISSIONS_PROVIDE = 180;
 
@@ -1503,6 +1504,10 @@
                             nativeSetJsFlags((String)msg.obj);
                             break;
 
+                        case CONTENT_INVALIDATE_ALL:
+                            nativeContentInvalidateAll();
+                            break;
+
                         case SAVE_WEBARCHIVE:
                             WebView.SaveWebArchiveMessage saveMessage =
                                 (WebView.SaveWebArchiveMessage)msg.obj;
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index c4ba7c8..4b08f2d 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -1406,6 +1406,7 @@
             v.setTranslationX(translationX);
 
             drawOutline(mCanvas, bitmap);
+            mCanvas.setBitmap(null);
             return bitmap;
         }
 
@@ -1417,6 +1418,7 @@
             dest.drawColor(0, PorterDuff.Mode.CLEAR);
             dest.setMatrix(mIdentityMatrix);
             dest.drawBitmap(mask, xy[0], xy[1], mHolographicPaint);
+            mMaskCanvas.setBitmap(null);
             mask.recycle();
         }
     }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index ab66676..65ee745 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -999,7 +999,8 @@
                 setEllipsize(TextUtils.TruncateAt.END);
                 break;
             case 4:
-                setHorizontalFadingEdgeEnabled(true);
+                setHorizontalFadingEdgeEnabled(
+                        ViewConfiguration.get(context).isFadingMarqueeEnabled());
                 setEllipsize(TextUtils.TruncateAt.MARQUEE);
                 break;
         }
@@ -3067,7 +3068,8 @@
 
         if (text instanceof Spanned &&
             ((Spanned) text).getSpanStart(TextUtils.TruncateAt.MARQUEE) >= 0) {
-            setHorizontalFadingEdgeEnabled(true);
+            setHorizontalFadingEdgeEnabled(
+                    ViewConfiguration.get(mContext).isFadingMarqueeEnabled());
             setEllipsize(TextUtils.TruncateAt.MARQUEE);
         }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f99a94c..21c3f1e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -294,12 +294,21 @@
 
    <!-- Allows an application to read/write the voicemails owned by its own
         package. -->
+   <!--  TODO: delete this permission when dependent content provider &
+        application code has been migrated to use ADD_VOICEMAIL instead -->
     <permission android:name="com.android.voicemail.permission.READ_WRITE_OWN_VOICEMAIL"
         android:permissionGroup="android.permission-group.PERSONAL_INFO"
         android:protectionLevel="dangerous"
         android:label="@string/permlab_readWriteOwnVoicemail"
         android:description="@string/permdesc_readWriteOwnVoicemail" />
 
+   <!-- Allows an application to add voicemails into the system. -->
+    <permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL"
+        android:permissionGroup="android.permission-group.PERSONAL_INFO"
+        android:protectionLevel="dangerous"
+        android:label="@string/permlab_addVoicemail"
+        android:description="@string/permdesc_addVoicemail" />
+
     <!-- ======================================= -->
     <!-- Permissions for accessing location info -->
     <!-- ======================================= -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index dd16bd0..847afa0 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -710,6 +710,22 @@
         <enum name="preferExternal" value="2" />
     </attr>
 
+    <!-- Extra options for an activity's UI. If specified on the application
+         tag these will be considered defaults for all activities in the
+         application. -->
+    <attr name="uiOptions">
+        <!-- No extra UI options. -->
+        <flag name="none" value="0" />
+        <!-- Split the options menu into a separate bar at the bottom of
+             the screen when severely constrained for horizontal space.
+             (e.g. portrait mode on a phone.) Instead of a small number
+             of action buttons appearing in the action bar at the top
+             of the screen, the action bar will split into the top navigation
+             section and the bottom menu section. Menu items will not be
+             split across the two bars; they will always appear together. -->
+        <flag name="splitActionBarWhenNarrow" value="1" />
+    </attr>
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -812,6 +828,7 @@
              application is running, the user will be informed of this.
              @hide -->
         <attr name="cantSaveState" format="boolean" />
+        <attr name="uiOptions" />
     </declare-styleable>
     
     <!-- The <code>permission</code> tag declares a security permission that can be
@@ -1302,6 +1319,7 @@
         <attr name="windowSoftInputMode" />
         <attr name="immersive" />
         <attr name="hardwareAccelerated" />
+        <attr name="uiOptions" />
     </declare-styleable>
     
     <!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 65dce49..73443a0 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -84,6 +84,9 @@
          specified for -large and -xlarge configurations. -->
     <dimen name="config_prefDialogWidth">320dp</dimen>
 
+    <!-- Enables or disables fading edges when marquee is enabled in TextView. -->
+    <bool name="config_ui_enableFadingMarquee">true</bool>
+
     <!-- Whether dialogs should close automatically when the user touches outside
          of them.  This should not normally be modified. -->
     <bool name="config_closeDialogWhenTouchOutside">false</bool>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 87e9249..2dfe453 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1783,11 +1783,11 @@
   <public type="attr" name="minResizeHeight" />
 
   <public type="attr" name="actionBarWidgetTheme" />
+  <public type="attr" name="uiOptions" />
 
   <public type="style" name="TextAppearance.SuggestionHighlight" />
-  <public type="style" name="Theme.Holo.SplitActionBarWhenNarrow" />
-  <public type="style" name="Theme.Holo.Light.SplitActionBarWhenNarrow" />
 
+  <public type="style" name="Theme.Holo.Light.DarkActionBar" />
   <public type="style" name="Widget.Holo.Button.Borderless.Small" />
   <public type="style" name="Widget.Holo.Light.Button.Borderless.Small" />
   <public type="style" name="TextAppearance.Holo.Widget.ActionBar.Title.Inverse" />
@@ -1802,12 +1802,6 @@
   <public type="style" name="Widget.Holo.Light.ActionBar.TabView.Inverse" />
   <public type="style" name="Widget.Holo.Light.ActionBar.TabText.Inverse" />
   <public type="style" name="Widget.Holo.Light.ActionMode.Inverse" />
-  <public type="style" name="Theme.Holo.SolidActionBar" />
-  <public type="style" name="Theme.Holo.Light.SolidActionBar" />
-  <public type="style" name="Theme.Holo.Light.SolidActionBar.Inverse" />
-  <public type="style" name="Theme.Holo.SolidActionBar.SplitActionBarWhenNarrow" />
-  <public type="style" name="Theme.Holo.Light.SolidActionBar.SplitActionBarWhenNarrow" />
-  <public type="style" name="Theme.Holo.Light.SolidActionBar.Inverse.SplitActionBarWhenNarrow" />
 
   <public type="integer" name="status_bar_notification_info_maxnum" />
   <public type="string" name="status_bar_notification_info_overflow" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c5aa4b2..a6c92f2 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2166,6 +2166,14 @@
       voicemails that its associated service can access.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether
+        they want to allow the application to do this. [CHAR LIMIT=NONE] -->
+    <string name="permlab_addVoicemail">add voicemail</string>
+    <!-- Description of an application permission, listed so the user can choose whether
+        they want to allow the application to do this. [CHAR LIMIT=NONE] -->
+    <string name="permdesc_addVoicemail">Allows the application to add messages
+      to your voicemail inbox.</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_writeGeolocationPermissions">Modify Browser geolocation permissions</string>
     <!-- Description of an application permission, listed so the user can choose whether
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 3e7c5ca..2b1b693 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -1207,7 +1207,7 @@
         <item name="windowNoTitle">false</item>
         <item name="windowFullscreen">false</item>
         <item name="windowIsFloating">false</item>
-        <item name="windowContentOverlay">@null</item>
+        <item name="android:windowContentOverlay">@android:drawable/ab_solid_shadow_holo</item>
         <item name="windowShowWallpaper">false</item>
         <item name="windowTitleStyle">@android:style/WindowTitle.Holo</item>
         <item name="windowTitleSize">25dip</item>
@@ -1337,7 +1337,7 @@
         <item name="actionBarTabTextStyle">@style/Widget.Holo.Light.ActionBar.TabText</item>
         <item name="actionModeStyle">@style/Widget.Holo.Light.ActionMode</item>
         <item name="actionModeCloseButtonStyle">@style/Widget.Holo.Light.ActionButton.CloseMode</item>
-        <item name="actionBarStyle">@android:style/Widget.Holo.Light.ActionBar</item>
+        <item name="android:actionBarStyle">@android:style/Widget.Holo.Light.ActionBar.Solid</item>
         <item name="actionBarSize">@dimen/action_bar_default_height</item>
         <item name="actionModePopupWindowStyle">@android:style/Widget.Holo.Light.PopupWindow.ActionMode</item>
         <item name="actionBarWidgetTheme">@null</item>
@@ -1384,22 +1384,10 @@
 
     </style>
 
-    <!-- Variant of the holographic (dark) theme that has a solid (opaque) action bar. -->
-    <style name="Theme.Holo.SolidActionBar">
-        <item name="android:actionBarStyle">@android:style/Widget.Holo.ActionBar.Solid</item>
-        <item name="android:windowContentOverlay">@android:drawable/ab_solid_shadow_holo</item>
-    </style>
-
-    <!-- Variant of the holographic (light) theme that has a solid (opaque) action bar. -->
-    <style name="Theme.Holo.Light.SolidActionBar">
-        <item name="android:actionBarStyle">@android:style/Widget.Holo.Light.ActionBar.Solid</item>
-        <item name="android:windowContentOverlay">@android:drawable/ab_solid_shadow_holo</item>
-    </style>
-
     <!-- Variant of the holographic (light) theme that has a solid (opaque) action bar
          with an inverse color profile. The dark action bar sharply stands out against
          the light content. -->
-    <style name="Theme.Holo.Light.SolidActionBar.Inverse">
+    <style name="Theme.Holo.Light.DarkActionBar">
         <item name="android:windowContentOverlay">@android:drawable/title_bar_shadow</item>
         <item name="android:actionBarStyle">@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse</item>
         <item name="actionBarWidgetTheme">@android:style/Theme.Holo</item>
@@ -1427,30 +1415,6 @@
         <item name="actionModeWebSearchDrawable">@android:drawable/ic_menu_search_holo_dark</item>
     </style>
 
-    <!-- Variant of the holographic (dark) theme that has a solid
-         (opaque) action bar. The action bar will split across both
-         the top and bottom of the screen when the screen is
-         especially constrained for horizontal space. -->
-    <style name="Theme.Holo.SolidActionBar.SplitActionBarWhenNarrow">
-        <item name="android:windowSplitActionBar">@android:bool/split_action_bar_is_narrow</item>
-    </style>
-
-    <!-- Variant of the holographic (light) theme that has a solid
-         (opaque) action bar. The action bar will split across both
-         the top and bottom of the screen when the screen is
-         especially constrained for horizontal space. -->
-    <style name="Theme.Holo.Light.SolidActionBar.SplitActionBarWhenNarrow">
-        <item name="android:windowSplitActionBar">@android:bool/split_action_bar_is_narrow</item>
-    </style>
-
-    <!-- Variant of the holographic (light) theme that has a solid (opaque) action bar
-         with an inverse color profile. The dark action bar sharply stands out against
-         the light content. The action bar will split across both the top and bottom of
-         the screen when the screen is especially constrained for horizontal space. -->
-    <style name="Theme.Holo.Light.SolidActionBar.Inverse.SplitActionBarWhenNarrow">
-        <item name="android:windowSplitActionBar">@android:bool/split_action_bar_is_narrow</item>
-    </style>
-
     <!-- Variant of the holographic (dark) theme with no action bar. -->
     <style name="Theme.Holo.NoActionBar">
         <item name="android:windowActionBar">false</item>
@@ -1654,18 +1618,4 @@
     <style name="Theme.Holo.Wallpaper.NoTitleBar">
         <item name="android:windowNoTitle">true</item>
     </style>
-
-    <!-- Variant of the holographic (dark) theme with an action bar that
-         splits across the top and bottom of the activity when constrained
-         for horizontal space. -->
-    <style name="Theme.Holo.SplitActionBarWhenNarrow">
-        <item name="android:windowSplitActionBar">@android:bool/split_action_bar_is_narrow</item>
-    </style>
-
-    <!-- Variant of the holographic (light) theme with an action bar that
-         splits across the top and bottom of the activity when constrained
-         for horizontal space. -->
-    <style name="Theme.Holo.Light.SplitActionBarWhenNarrow">
-        <item name="android:windowSplitActionBar">@android:bool/split_action_bar_is_narrow</item>
-    </style>
 </resources>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 40d54bb..3fc6463 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -567,6 +567,7 @@
         
         canvas.setBitmap(bitmap);
         canvas.drawBitmap(source, srcR, dstR, paint);
+        canvas.setBitmap(null);
 
         return bitmap;
     }
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 9bf3de8..9acf99b 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -44,9 +44,11 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
-        int flags, uint32_t italicStyle, uint32_t scaleX) :
+        int flags, uint32_t italicStyle, uint32_t scaleX,
+        SkPaint::Style style, uint32_t strokeWidth) :
         mState(state), mFontId(fontId), mFontSize(fontSize),
-        mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX) {
+        mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
+        mStyle(style), mStrokeWidth(mStrokeWidth) {
 }
 
 
@@ -283,19 +285,22 @@
 }
 
 Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
-        int flags, uint32_t italicStyle, uint32_t scaleX) {
+        int flags, uint32_t italicStyle, uint32_t scaleX,
+        SkPaint::Style style, uint32_t strokeWidth) {
     Vector<Font*> &activeFonts = state->mActiveFonts;
 
     for (uint32_t i = 0; i < activeFonts.size(); i++) {
         Font* font = activeFonts[i];
         if (font->mFontId == fontId && font->mFontSize == fontSize &&
                 font->mFlags == flags && font->mItalicStyle == italicStyle &&
-                font->mScaleX == scaleX) {
+                font->mScaleX == scaleX && font->mStyle == style &&
+                (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
             return font;
         }
     }
 
-    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle, scaleX);
+    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
+            scaleX, style, strokeWidth);
     activeFonts.push(newFont);
     return newFont;
 }
@@ -690,7 +695,11 @@
     uint32_t italicStyle = *(uint32_t*) &skewX;
     const float scaleXFloat = paint->getTextScaleX();
     uint32_t scaleX = *(uint32_t*) &scaleXFloat;
-    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, scaleX);
+    SkPaint::Style style = paint->getStyle();
+    const float strokeWidthFloat = paint->getStrokeWidth();
+    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
+    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
+            scaleX, style, strokeWidth);
 
     const float maxPrecacheFontSize = 40.0f;
     bool isNewFont = currentNumFonts != mActiveFonts.size();
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 24ed6fa..1922812 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -82,7 +82,8 @@
      * Creates a new font associated with the specified font state.
      */
     static Font* create(FontRenderer* state, uint32_t fontId, float fontSize,
-            int flags, uint32_t italicStyle, uint32_t scaleX);
+            int flags, uint32_t italicStyle, uint32_t scaleX, SkPaint::Style style,
+            uint32_t strokeWidth);
 
 protected:
     friend class FontRenderer;
@@ -128,7 +129,7 @@
     };
 
     Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
-            uint32_t scaleX);
+            uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
 
     // Cache of glyphs
     DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
@@ -157,6 +158,8 @@
     int mFlags;
     uint32_t mItalicStyle;
     uint32_t mScaleX;
+    SkPaint::Style mStyle;
+    uint32_t mStrokeWidth;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 7258e11..731d1f3 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1646,7 +1646,8 @@
         IAudioService service = getService();
         try {
             status = service.requestAudioFocus(streamType, durationHint, mICallBack,
-                    mAudioFocusDispatcher, getIdForAudioFocusListener(l));
+                    mAudioFocusDispatcher, getIdForAudioFocusListener(l),
+                    mContext.getPackageName() /* package name */);
         } catch (RemoteException e) {
             Log.e(TAG, "Can't call requestAudioFocus() from AudioService due to "+e);
         }
@@ -1682,7 +1683,9 @@
      *      in the application manifest.
      */
     public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
-        //TODO enforce the rule about the receiver being declared in the manifest
+        if (eventReceiver == null) {
+            return;
+        }
         IAudioService service = getService();
         try {
             service.registerMediaButtonEventReceiver(eventReceiver);
@@ -1697,6 +1700,9 @@
      *      that was registered with {@link #registerMediaButtonEventReceiver(ComponentName)}.
      */
     public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
+        if (eventReceiver == null) {
+            return;
+        }
         IAudioService service = getService();
         try {
             service.unregisterMediaButtonEventReceiver(eventReceiver);
@@ -1706,6 +1712,126 @@
     }
 
     /**
+     * @hide
+     * Registers the remote control client for providing information to display on the remotes.
+     * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
+     *      that will receive the media button intent, and associated with the remote control
+     *      client. This method has no effect if
+     *      {@link #registerMediaButtonEventReceiver(ComponentName)} hasn't been called
+     *      with the same eventReceiver, or if
+     *      {@link #unregisterMediaButtonEventReceiver(ComponentName)} has been called.
+     * @param rcClient the client associated with the event receiver, responsible for providing
+     *      the information to display on the remote control.
+     */
+    public void registerRemoteControlClient(ComponentName eventReceiver,
+            IRemoteControlClient rcClient) {
+        if (eventReceiver == null) {
+            return;
+        }
+        IAudioService service = getService();
+        try {
+            service.registerRemoteControlClient(eventReceiver, rcClient,
+                    // used to match media button event receiver and audio focus
+                    mContext.getPackageName());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in registerRemoteControlClient"+e);
+        }
+    }
+
+    /**
+     * @hide
+     * @param eventReceiver
+     */
+    public void unregisterRemoteControlClient(ComponentName eventReceiver) {
+        if (eventReceiver == null) {
+            return;
+        }
+        IAudioService service = getService();
+        try {
+            // unregistering a IRemoteControlClient is equivalent to setting it to null
+            service.registerRemoteControlClient(eventReceiver, null, mContext.getPackageName());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in unregisterRemoteControlClient"+e);
+        }
+    }
+
+    /**
+     * @hide
+     * Definitions of constants to be used in {@link android.media.IRemoteControlClient}.
+     */
+    public final class RemoteControlParameters {
+        public final static int PLAYSTATE_STOPPED            = 1;
+        public final static int PLAYSTATE_PAUSED             = 2;
+        public final static int PLAYSTATE_PLAYING            = 3;
+        public final static int PLAYSTATE_FAST_FORWARDING    = 4;
+        public final static int PLAYSTATE_REWINDING          = 5;
+        public final static int PLAYSTATE_SKIPPING_FORWARDS  = 6;
+        public final static int PLAYSTATE_SKIPPING_BACKWARDS = 7;
+        public final static int PLAYSTATE_BUFFERING          = 8;
+
+        public final static int FLAG_KEY_MEDIA_PREVIOUS = 1 << 0;
+        public final static int FLAG_KEY_MEDIA_REWIND = 1 << 1;
+        public final static int FLAG_KEY_MEDIA_PLAY = 1 << 2;
+        public final static int FLAG_KEY_MEDIA_PLAY_PAUSE = 1 << 3;
+        public final static int FLAG_KEY_MEDIA_PAUSE = 1 << 4;
+        public final static int FLAG_KEY_MEDIA_STOP = 1 << 5;
+        public final static int FLAG_KEY_MEDIA_FAST_FORWARD = 1 << 6;
+        public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7;
+    }
+
+    /**
+     * @hide
+     * Broadcast intent action indicating that the displays on the remote controls
+     * should be updated because a new remote control client is now active. If there is no
+     * {@link #EXTRA_REMOTE_CONTROL_CLIENT}, the remote control display should be cleared
+     * because there is no valid client to supply it with information.
+     *
+     * @see #EXTRA_REMOTE_CONTROL_CLIENT
+     */
+    public static final String REMOTE_CONTROL_CLIENT_CHANGED =
+            "android.media.REMOTE_CONTROL_CLIENT_CHANGED";
+
+    /**
+     * @hide
+     * The IRemoteControlClient monotonically increasing generation counter.
+     *
+     * @see #REMOTE_CONTROL_CLIENT_CHANGED_ACTION
+     */
+    public static final String EXTRA_REMOTE_CONTROL_CLIENT =
+            "android.media.EXTRA_REMOTE_CONTROL_CLIENT";
+
+    /**
+     * @hide
+     * FIXME to be changed to address Neel's comments
+     * Force a refresh of the remote control client associated with the event receiver.
+     * @param eventReceiver
+     */
+    public void refreshRemoteControlDisplay(ComponentName eventReceiver) {
+        IAudioService service = getService();
+        try {
+            service.refreshRemoteControlDisplay(eventReceiver);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in refreshRemoteControlDisplay"+e);
+        }
+    }
+
+    /**
+     * @hide
+     * FIXME API to be used by implementors of remote controls, not a candidate for SDK
+     */
+    public void registerRemoteControlObserver() {
+
+    }
+
+    /**
+     * @hide
+     * FIXME API to be used by implementors of remote controls, not a candidate for SDK
+     */
+    public void unregisterRemoteControlObserver() {
+
+    }
+
+    /**
      *  @hide
      *  Reload audio settings. This method is called by Settings backup
      *  agent when audio settings are restored and causes the AudioService
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 682560a..3e786c3 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -55,6 +55,7 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.lang.ref.SoftReference;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -113,6 +114,8 @@
     private static final int MSG_SET_FORCE_USE = 10;
     private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 11;
     private static final int MSG_BT_HEADSET_CNCT_FAILED = 12;
+    private static final int MSG_RCDISPLAY_CLEAR = 13;
+    private static final int MSG_RCDISPLAY_UPDATE = 14;
 
     private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
     // Timeout for connection to bluetooth headset service
@@ -371,7 +374,9 @@
 
         // Register for media button intent broadcasts.
         intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
-        intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+        // Workaround for bug on priority setting
+        //intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+        intentFilter.setPriority(Integer.MAX_VALUE);
         context.registerReceiver(mMediaButtonReceiver, intentFilter);
 
         // Register for phone state monitoring
@@ -885,7 +890,8 @@
                 requestAudioFocus(AudioManager.STREAM_RING,
                         AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
                         null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
-                        IN_VOICE_COMM_FOCUS_ID /*clientId*/);
+                        IN_VOICE_COMM_FOCUS_ID /*clientId*/,
+                        "system");
 
             }
         }
@@ -897,7 +903,8 @@
             requestAudioFocus(AudioManager.STREAM_RING,
                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
                     null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
-                    IN_VOICE_COMM_FOCUS_ID /*clientId*/);
+                    IN_VOICE_COMM_FOCUS_ID /*clientId*/,
+                    "system");
         }
         // if exiting call
         else if (newMode == AudioSystem.MODE_NORMAL) {
@@ -2155,6 +2162,33 @@
                     persistMediaButtonReceiver( (ComponentName) msg.obj );
                     break;
 
+                case MSG_RCDISPLAY_CLEAR:
+                    Log.i(TAG, "Clear remote control display");
+                    Intent clearIntent = new Intent(AudioManager.REMOTE_CONTROL_CLIENT_CHANGED);
+                    // no extra means no IRemoteControlClient, which is a request to clear
+                    clearIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    mContext.sendBroadcast(clearIntent);
+                    break;
+
+                case MSG_RCDISPLAY_UPDATE:
+                    synchronized(mCurrentRcLock) {
+                        if (mCurrentRcClientRef.get() == null) {
+                            // the remote control display owner has changed between the
+                            // the message to update the display was sent, and the time it
+                            // gets to be processed (now)
+                        } else {
+                            mCurrentRcClientGen++;
+                            Log.i(TAG, "Display/update remote control ");
+                            Intent rcClientIntent = new Intent(
+                                    AudioManager.REMOTE_CONTROL_CLIENT_CHANGED);
+                            rcClientIntent.putExtra(AudioManager.EXTRA_REMOTE_CONTROL_CLIENT,
+                                    mCurrentRcClientGen);
+                            rcClientIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                            mContext.sendBroadcast(rcClientIntent);
+                        }
+                    }
+                    break;
+
                 case MSG_BT_HEADSET_CNCT_FAILED:
                     resetBluetoothSco();
                     break;
@@ -2567,23 +2601,25 @@
 
     private static class FocusStackEntry {
         public int mStreamType = -1;// no stream type
-        public boolean mIsTransportControlReceiver = false;
         public IAudioFocusDispatcher mFocusDispatcher = null;
         public IBinder mSourceRef = null;
         public String mClientId;
         public int mFocusChangeType;
+        public String mPackageName;
+        public int mCallingUid;
 
         public FocusStackEntry() {
         }
 
-        public FocusStackEntry(int streamType, int duration, boolean isTransportControlReceiver,
-                IAudioFocusDispatcher afl, IBinder source, String id) {
+        public FocusStackEntry(int streamType, int duration,
+                IAudioFocusDispatcher afl, IBinder source, String id, String pn, int uid) {
             mStreamType = streamType;
-            mIsTransportControlReceiver = isTransportControlReceiver;
             mFocusDispatcher = afl;
             mSourceRef = source;
             mClientId = id;
             mFocusChangeType = duration;
+            mPackageName = pn;
+            mCallingUid = uid;
         }
     }
 
@@ -2600,13 +2636,15 @@
             while(stackIterator.hasNext()) {
                 FocusStackEntry fse = stackIterator.next();
                 pw.println("     source:" + fse.mSourceRef + " -- client: " + fse.mClientId
-                        + " -- duration: " +fse.mFocusChangeType);
+                        + " -- duration: " + fse.mFocusChangeType
+                        + " -- uid: " + fse.mCallingUid);
             }
         }
     }
 
     /**
      * Helper function:
+     * Called synchronized on mAudioFocusLock
      * Remove a focus listener from the focus stack.
      * @param focusListenerToRemove the focus listener
      * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
@@ -2621,6 +2659,10 @@
             if (signal) {
                 // notify the new top of the stack it gained focus
                 notifyTopOfAudioFocusStack();
+                // there's a new top of the stack, let the remote control know
+                synchronized(mRCStack) {
+                    checkUpdateRemoteControlDisplay();
+                }
             }
         } else {
             // focus is abandoned by a client that's not at the top of the stack,
@@ -2639,6 +2681,7 @@
 
     /**
      * Helper function:
+     * Called synchronized on mAudioFocusLock
      * Remove focus listeners from the focus stack for a particular client.
      */
     private void removeFocusStackEntryForClient(IBinder cb) {
@@ -2658,6 +2701,10 @@
             // we removed an entry at the top of the stack:
             //  notify the new top of the stack it gained focus.
             notifyTopOfAudioFocusStack();
+            // there's a new top of the stack, let the remote control know
+            synchronized(mRCStack) {
+                checkUpdateRemoteControlDisplay();
+            }
         }
     }
 
@@ -2700,7 +2747,7 @@
 
     /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
     public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
-            IAudioFocusDispatcher fd, String clientId) {
+            IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
         Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
         // the main stream type for the audio focus request is currently not used. It may
         // potentially be used to handle multiple stream type-dependent audio focuses.
@@ -2743,8 +2790,13 @@
             removeFocusStackEntry(clientId, false);
 
             // push focus requester at the top of the audio focus stack
-            mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, false, fd, cb,
-                    clientId));
+            mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
+                    clientId, callingPackageName, Binder.getCallingUid()));
+
+            // there's a new top of the stack, let the remote control know
+            synchronized(mRCStack) {
+                checkUpdateRemoteControlDisplay();
+            }
         }//synchronized(mAudioFocusLock)
 
         // handle the potential premature death of the new holder of the focus
@@ -2831,19 +2883,100 @@
         }
     }
 
-    private static class RemoteControlStackEntry {
-        public ComponentName mReceiverComponent;// always non null
-        // TODO implement registration expiration?
-        //public int mRegistrationTime;
+    private final static Object mCurrentRcLock = new Object();
+    /**
+     * The one remote control client to be polled for display information.
+     * This object is never null, but its reference might.
+     * Access protected by mCurrentRcLock.
+     */
+    private static SoftReference<IRemoteControlClient> mCurrentRcClientRef =
+            new SoftReference<IRemoteControlClient>(null);
 
-        public RemoteControlStackEntry() {
-        }
+    /**
+     * A monotonically increasing generation counter for mCurrentRcClientRef.
+     * Only accessed with a lock on mCurrentRcLock.
+     */
+    private static int mCurrentRcClientGen = 0;
 
-        public RemoteControlStackEntry(ComponentName r) {
-            mReceiverComponent = r;
+    /**
+     * Returns the current remote control client.
+     * @param rcClientId the counter value that matches the extra
+     *     {@link AudioManager#EXTRA_REMOTE_CONTROL_CLIENT} in the
+     *     {@link AudioManager#REMOTE_CONTROL_CLIENT_CHANGED} event
+     * @return the current IRemoteControlClient from which information to display on the remote
+     *     control can be retrieved, or null if rcClientId doesn't match the current generation
+     *     counter.
+     */
+    public static IRemoteControlClient getRemoteControlClient(int rcClientId) {
+        synchronized(mCurrentRcLock) {
+            if (rcClientId == mCurrentRcClientGen) {
+                return mCurrentRcClientRef.get();
+            } else {
+                return null;
+            }
         }
     }
 
+    /**
+     * Inner class to monitor remote control client deaths, and remove the client for the
+     * remote control stack if necessary.
+     */
+    private class RcClientDeathHandler implements IBinder.DeathRecipient {
+        private IBinder mCb; // To be notified of client's death
+        private ComponentName mRcEventReceiver;
+
+        RcClientDeathHandler(IBinder cb, ComponentName eventReceiver) {
+            mCb = cb;
+            mRcEventReceiver = eventReceiver;
+        }
+
+        public void binderDied() {
+            Log.w(TAG, "  RemoteControlClient died");
+            // remote control client died, make sure the displays don't use it anymore
+            //  by setting its remote control client to null
+            registerRemoteControlClient(mRcEventReceiver, null, null/*ignored*/);
+        }
+
+        public IBinder getBinder() {
+            return mCb;
+        }
+    }
+
+    private static class RemoteControlStackEntry {
+        /** the target for the ACTION_MEDIA_BUTTON events */
+        public ComponentName mReceiverComponent;// always non null
+        public String mCallingPackageName;
+        public int mCallingUid;
+
+        /** provides access to the information to display on the remote control */
+        public SoftReference<IRemoteControlClient> mRcClientRef;
+        public RcClientDeathHandler mRcClientDeathHandler;
+
+        public RemoteControlStackEntry(ComponentName r) {
+            mReceiverComponent = r;
+            mCallingUid = -1;
+            mRcClientRef = new SoftReference<IRemoteControlClient>(null);
+        }
+
+        public void unlinkToRcClientDeath() {
+            if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
+                try {
+                    mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
+                } catch (java.util.NoSuchElementException e) {
+                    // not much we can do here
+                    Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    /**
+     *  The stack of remote control event receivers.
+     *  Code sections and methods that modify the remote control event receiver stack are
+     *  synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
+     *  stack, audio focus or RC, can lead to a change in the remote control display
+     */
     private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
 
     /**
@@ -2855,8 +2988,10 @@
         synchronized(mRCStack) {
             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
             while(stackIterator.hasNext()) {
-                RemoteControlStackEntry fse = stackIterator.next();
-                pw.println("     receiver:" + fse.mReceiverComponent);
+                RemoteControlStackEntry rcse = stackIterator.next();
+                pw.println("     receiver: " + rcse.mReceiverComponent +
+                        "  -- client: " + rcse.mRcClientRef.get() +
+                        "  -- uid: " + rcse.mCallingUid);
             }
         }
     }
@@ -2909,6 +3044,7 @@
             ComponentName receiverComponentName = ComponentName.unflattenFromString(receiverName);
             registerMediaButtonEventReceiver(receiverComponentName);
         }
+        // upon restoring (e.g. after boot), do we want to refresh all remotes?
     }
 
     /**
@@ -2921,14 +3057,20 @@
             return;
         }
         Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+        RemoteControlStackEntry rcse = null;
+        boolean wasInsideStack = false;
         while(stackIterator.hasNext()) {
-            RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
+            rcse = (RemoteControlStackEntry)stackIterator.next();
             if(rcse.mReceiverComponent.equals(newReceiver)) {
+                wasInsideStack = true;
                 stackIterator.remove();
                 break;
             }
         }
-        mRCStack.push(new RemoteControlStackEntry(newReceiver));
+        if (!wasInsideStack) {
+            rcse = new RemoteControlStackEntry(newReceiver);
+        }
+        mRCStack.push(rcse);
 
         // post message to persist the default media button receiver
         mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
@@ -2950,13 +3092,88 @@
         }
     }
 
+    /**
+     * Helper function:
+     * Called synchronized on mRCStack
+     */
+    private boolean isCurrentRcController(ComponentName eventReceiver) {
+        if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(eventReceiver)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Helper function:
+     * Called synchronized on mRCStack
+     */
+    private void clearRemoteControlDisplay() {
+        synchronized(mCurrentRcLock) {
+            mCurrentRcClientRef.clear();
+        }
+        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
+    }
+
+    /**
+     * Helper function:
+     * Called synchronized on mRCStack
+     * mRCStack.empty() is false
+     */
+    private void updateRemoteControlDisplay() {
+        RemoteControlStackEntry rcse = mRCStack.peek();
+        // this is where we enforce opt-in for information display on the remote controls
+        //   with the new AudioManager.registerRemoteControlClient() API
+        if (rcse.mRcClientRef.get() == null) {
+            // FIXME remove log before release: this warning will be displayed for every AF change
+            Log.w(TAG, "Can't update remote control display with null remote control client");
+            clearRemoteControlDisplay();
+            return;
+        }
+        synchronized(mCurrentRcLock) {
+            mCurrentRcClientRef = rcse.mRcClientRef;
+        }
+        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE, 0, 0, rcse) );
+    }
+
+    /**
+     * Helper function:
+     * Called synchronized on mFocusLock, then mRCStack
+     * Check whether the remote control display should be updated, triggers the update if required
+     */
+    private void checkUpdateRemoteControlDisplay() {
+        // determine whether the remote control display should be refreshed
+        // if either stack is empty, there is a mismatch, so clear the RC display
+        if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
+            clearRemoteControlDisplay();
+            return;
+        }
+        // if the top of the two stacks belong to different packages, there is a mismatch, clear
+        if ((mRCStack.peek().mCallingPackageName != null)
+                && (mFocusStack.peek().mPackageName != null)
+                && !(mRCStack.peek().mCallingPackageName.compareTo(
+                        mFocusStack.peek().mPackageName) == 0)) {
+            clearRemoteControlDisplay();
+            return;
+        }
+        // if the audio focus didn't originate from the same Uid as the one in which the remote
+        //   control information will be retrieved, clear
+        if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) {
+            clearRemoteControlDisplay();
+            return;
+        }
+        // refresh conditions were verified: update the remote controls
+        updateRemoteControlDisplay();
+    }
 
     /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */
     public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
         Log.i(TAG, "  Remote Control   registerMediaButtonEventReceiver() for " + eventReceiver);
 
-        synchronized(mRCStack) {
-            pushMediaButtonReceiver(eventReceiver);
+        synchronized(mAudioFocusLock) {
+            synchronized(mRCStack) {
+                pushMediaButtonReceiver(eventReceiver);
+                checkUpdateRemoteControlDisplay();
+            }
         }
     }
 
@@ -2964,11 +3181,74 @@
     public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
         Log.i(TAG, "  Remote Control   unregisterMediaButtonEventReceiver() for " + eventReceiver);
 
-        synchronized(mRCStack) {
-            removeMediaButtonReceiver(eventReceiver);
+        synchronized(mAudioFocusLock) {
+            synchronized(mRCStack) {
+                boolean topOfStackWillChange = isCurrentRcController(eventReceiver);
+                removeMediaButtonReceiver(eventReceiver);
+                if (topOfStackWillChange) {
+                    checkUpdateRemoteControlDisplay();
+                }
+            }
         }
     }
 
+    /** see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...) */
+    public void registerRemoteControlClient(ComponentName eventReceiver,
+            IRemoteControlClient rcClient, String callingPackageName) {
+        synchronized(mAudioFocusLock) {
+            synchronized(mRCStack) {
+                // store the new display information
+                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+                while(stackIterator.hasNext()) {
+                    RemoteControlStackEntry rcse = stackIterator.next();
+                    if(rcse.mReceiverComponent.equals(eventReceiver)) {
+                        // already had a remote control client?
+                        if (rcse.mRcClientDeathHandler != null) {
+                            // stop monitoring the old client's death
+                            rcse.unlinkToRcClientDeath();
+                        }
+                        // save the new remote control client
+                        rcse.mRcClientRef = new SoftReference<IRemoteControlClient>(rcClient);
+                        rcse.mCallingPackageName = callingPackageName;
+                        rcse.mCallingUid = Binder.getCallingUid();
+                        if (rcClient == null) {
+                            break;
+                        }
+                        // monitor the new client's death
+                        IBinder b = rcClient.asBinder();
+                        RcClientDeathHandler rcdh =
+                                new RcClientDeathHandler(b, rcse.mReceiverComponent);
+                        try {
+                            b.linkToDeath(rcdh, 0);
+                        } catch (RemoteException e) {
+                            // remote control client is DOA, disqualify it
+                            Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
+                            rcse.mRcClientRef.clear();
+                        }
+                        rcse.mRcClientDeathHandler = rcdh;
+                        break;
+                    }
+                }
+                // if the eventReceiver is at the top of the stack
+                // then check for potential refresh of the remote controls
+                if (isCurrentRcController(eventReceiver)) {
+                    checkUpdateRemoteControlDisplay();
+                }
+            }
+        }
+    }
+
+    /** see AudioManager.refreshRemoteControlDisplay(ComponentName er) */
+    public void refreshRemoteControlDisplay(ComponentName eventReceiver) {
+        synchronized(mAudioFocusLock) {
+            synchronized(mRCStack) {
+                // only refresh if the eventReceiver is at the top of the stack
+                if (isCurrentRcController(eventReceiver)) {
+                    checkUpdateRemoteControlDisplay();
+                }
+            }
+        }
+    }
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index e3bd7b4..1a05f152 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -18,6 +18,9 @@
 
 import android.content.ComponentName;
 import android.media.IAudioFocusDispatcher;
+import android.media.IRemoteControlClient;
+import android.net.Uri;
+import android.os.Bundle;
 
 /**
  * {@hide}
@@ -77,7 +80,7 @@
     boolean isBluetoothScoOn();
 
     int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb, IAudioFocusDispatcher l,
-            String clientId);
+            String clientId, String callingPackageName);
 
     int abandonAudioFocus(IAudioFocusDispatcher l, String clientId);
     
@@ -87,6 +90,11 @@
 
     void unregisterMediaButtonEventReceiver(in ComponentName eventReceiver);
 
+    void registerRemoteControlClient(in ComponentName eventReceiver,
+           in IRemoteControlClient rcClient, in String callingPackageName);
+
+    void refreshRemoteControlDisplay(in ComponentName eventReceiver);
+
     void startBluetoothSco(IBinder cb);
 
     void stopBluetoothSco(IBinder cb);
diff --git a/media/java/android/media/IRemoteControlClient.aidl b/media/java/android/media/IRemoteControlClient.aidl
new file mode 100644
index 0000000..a49371c
--- /dev/null
+++ b/media/java/android/media/IRemoteControlClient.aidl
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 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;
+
+/**
+ * {@hide}
+ */
+interface IRemoteControlClient
+{
+    /**
+     * Called by a remote control to retrieve a String of information to display.
+     * @param field the identifier for a metadata field to retrieve. 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_CD_TRACK_NUMBER},
+     *      {@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_DISC_NUMBER},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
+     * @return null if the given field is not supported, or the String matching the metadata field.
+     */
+    String getMetadataString(int field);
+
+    /**
+     * Returns the current playback state.
+     * @return one of the following values:
+     *       {@link android.media.AudioManager.RemoteControl#PLAYSTATE_STOPPED},
+     *       {@link android.media.AudioManager.RemoteControl#PLAYSTATE_PAUSED},
+     *       {@link android.media.AudioManager.RemoteControl#PLAYSTATE_PLAYING},
+     *       {@link android.media.AudioManager.RemoteControl#PLAYSTATE_FAST_FORWARDING},
+     *       {@link android.media.AudioManager.RemoteControl#PLAYSTATE_REWINDING},
+     *       {@link android.media.AudioManager.RemoteControl#PLAYSTATE_SKIPPING_FORWARDS},
+     *       {@link android.media.AudioManager.RemoteControl#PLAYSTATE_SKIPPING_BACKWARDS},
+     *       {@link android.media.AudioManager.RemoteControl#PLAYSTATE_BUFFERING}.
+     */
+    int getPlaybackState();
+
+    /**
+     * Returns the flags for the media transport control buttons this client supports.
+     * @see {@link android.media.AudioManager.RemoteControl#FLAG_KEY_MEDIA_PREVIOUS},
+     *      {@link android.media.AudioManager.RemoteControl#FLAG_KEY_MEDIA_REWIND},
+     *      {@link android.media.AudioManager.RemoteControl#FLAG_KEY_MEDIA_PLAY},
+     *      {@link android.media.AudioManager.RemoteControl#FLAG_KEY_MEDIA_PLAY_PAUSE},
+     *      {@link android.media.AudioManager.RemoteControl#FLAG_KEY_MEDIA_PAUSE},
+     *      {@link android.media.AudioManager.RemoteControl#FLAG_KEY_MEDIA_STOP},
+     *      {@link android.media.AudioManager.RemoteControl#FLAG_KEY_MEDIA_FAST_FORWARD},
+     *      {@link android.media.AudioManager.RemoteControl#FLAG_KEY_MEDIA_NEXT}
+     */
+    int getTransportControlFlags();
+
+    Bitmap getAlbumArt(int width, int height);
+}
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 7c181ee..078d4af 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -387,6 +387,7 @@
             if (recycle) {
                 source.recycle();
             }
+            c.setBitmap(null);
             return b2;
         }
         float bitmapWidthF = source.getWidth();
diff --git a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
index 6b0fb12..5bfdcdb 100644
--- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
+++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
@@ -3761,6 +3761,7 @@
             final Canvas canvas = new Canvas(bitmap);
             canvas.drawBitmap(tempBitmap, new Rect(0, 0, newWidth, newHeight),
                                           new Rect(0, 0, width, height), sResizePaint);
+            canvas.setBitmap(null);
         }
 
         if (tempBitmap != null) {
@@ -3837,6 +3838,7 @@
                 final Canvas canvas = new Canvas(bitmaps[i]);
                 canvas.drawBitmap(tempBitmap, new Rect(0, 0, newWidth, newHeight),
                                               new Rect(0, 0, width, height), sResizePaint);
+                canvas.setBitmap(null);
             }
         }
 
diff --git a/media/java/android/media/videoeditor/MediaImageItem.java b/media/java/android/media/videoeditor/MediaImageItem.java
index 73cc7e2..f0cc1fe 100755
--- a/media/java/android/media/videoeditor/MediaImageItem.java
+++ b/media/java/android/media/videoeditor/MediaImageItem.java
@@ -1008,6 +1008,7 @@
                                               srcBitmap.getHeight()),
                                               new Rect(0, 0, (int)bitmapWidth,
                                               (int)bitmapHeight), sResizePaint);
+        canvas.setBitmap(null);
         /**
          *  Release the source bitmap
          */
diff --git a/media/java/android/media/videoeditor/OverlayFrame.java b/media/java/android/media/videoeditor/OverlayFrame.java
index 131f5f0..d159df2 100755
--- a/media/java/android/media/videoeditor/OverlayFrame.java
+++ b/media/java/android/media/videoeditor/OverlayFrame.java
@@ -420,6 +420,7 @@
             }
 
             overlayCanvas.drawBitmap(overlayBitmap, srcRect, destRect, sResizePaint);
+            overlayCanvas.setBitmap(null);
 
             /*
              * Write to the dest file
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index ac73351..a4f3922 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -976,11 +976,6 @@
     video_def->nFrameWidth = width;
     video_def->nFrameHeight = height;
     video_def->xFramerate = 0;      // No need for output port
-    // FIXME:
-    // Revmoe this workaround after work is done.
-    if (!strncmp(mComponentName, "OMX.TI.DUCATI1", 14)) {
-        video_def->xFramerate = (frameRate << 16);
-    }
     video_def->nBitrate = bitRate;  // Q16 format
     video_def->eCompressionFormat = compressionFormat;
     video_def->eColorFormat = OMX_COLOR_FormatUnused;
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 3f547fd..4c7f84e 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -1073,6 +1073,15 @@
             return gl;
         }
 
+        public void purgeBuffers() {
+            mEgl.eglMakeCurrent(mEglDisplay,
+                    EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE,
+                    EGL10.EGL_NO_CONTEXT);
+            mEgl.eglMakeCurrent(mEglDisplay,
+                    mEglSurface, mEglSurface,
+                    mEglContext);
+        }
+
         /**
          * Display the current render surface.
          * @return false if the context has been lost.
@@ -1415,6 +1424,7 @@
                         if (LOG_RENDERER) {
                             Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
                         }
+                        mEglHelper.purgeBuffers();
                         mRenderer.onSurfaceChanged(gl, w, h);
                         sizeChanged = false;
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index c896046..d74b548 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -501,6 +501,7 @@
                     new RectF(mGlowBitmapPaddingLeftPx, mGlowBitmapPaddingTopPx,
                             outBitmap.getWidth() - mGlowBitmapPaddingRightPx,
                             outBitmap.getHeight() - mGlowBitmapPaddingBottomPx), paint);
+            canvas.setBitmap(null);
         }
         return outBitmap;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 3a47e6e..fc21929 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -294,6 +294,7 @@
             c.rotate(360f - degrees);
             c.translate(-dims[0] / 2, -dims[1] / 2);
             c.drawBitmap(mScreenBitmap, 0, 0, null);
+            c.setBitmap(null);
             mScreenBitmap = ss;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandedView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandedView.java
index 51fc7c2..3276e1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandedView.java
@@ -16,16 +16,9 @@
 
 package com.android.systemui.statusbar.phone;
 
-import android.animation.LayoutTransition;
 import android.content.Context;
 import android.util.AttributeSet;
-import android.view.Display;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.WindowManager;
 import android.widget.LinearLayout;
-import android.util.Slog;
-
 
 public class ExpandedView extends LinearLayout {
     PhoneStatusBar mService;
@@ -38,8 +31,6 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-
-        setLayerType(LAYER_TYPE_HARDWARE, null);
     }
 
     /** We want to shrink down to 0, and ignore the background. */
@@ -49,7 +40,7 @@
     }
 
     @Override
-     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
          super.onLayout(changed, left, top, right, bottom);
          int height = bottom - top;
          if (height != mPrevHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 18026f1..d25a827 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -16,12 +16,10 @@
 
 package com.android.systemui.statusbar.phone;
 
-import android.app.Service;
 import android.app.ActivityManagerNative;
 import android.app.Dialog;
 import android.app.Notification;
 import android.app.PendingIntent;
-import android.app.Service;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -30,6 +28,7 @@
 import android.content.res.Resources;
 import android.content.res.Configuration;
 import android.graphics.PixelFormat;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
@@ -65,16 +64,12 @@
 import android.widget.RemoteViews;
 import android.widget.ScrollView;
 import android.widget.TextView;
-import android.widget.FrameLayout;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Set;
 
 import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarIconList;
 import com.android.internal.statusbar.StatusBarNotification;
 
 import com.android.systemui.R;
@@ -186,7 +181,6 @@
 
     boolean mAnimating;
     long mCurAnimationTime;
-    float mDisplayHeight;
     float mAnimY;
     float mAnimVel;
     float mAnimAccel;
@@ -201,6 +195,8 @@
     // tracking calls to View.setSystemUiVisibility()
     int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
 
+    final Point mDisplaySize = new Point();
+
     private class ExpandedDialog extends Dialog {
         ExpandedDialog(Context context) {
             super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
@@ -246,8 +242,11 @@
 
         Resources res = context.getResources();
 
+        mDisplay.getSize(mDisplaySize);
         loadDimens();
 
+        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
+
         ExpandedView expanded = (ExpandedView)View.inflate(context,
                 R.layout.status_bar_expanded, null);
         expanded.mService = this;
@@ -1149,7 +1148,7 @@
         if (mAnimating) {
             y = (int)mAnimY;
         } else {
-            y = mDisplay.getHeight()-1;
+            y = mDisplaySize.y-1;
         }
         // Let the fling think that we're open so it goes in the right direction
         // and doesn't try to re-open the windowshade.
@@ -1205,7 +1204,7 @@
             if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
             incrementAnim();
             if (SPEW) Slog.d(TAG, "doAnimation after  mAnimY=" + mAnimY);
-            if (mAnimY >= mDisplay.getHeight()-1) {
+            if (mAnimY >= mDisplaySize.y-1) {
                 if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
                 mAnimating = false;
                 updateExpandedViewPos(EXPANDED_FULL_OPEN);
@@ -1291,7 +1290,6 @@
 
     void performFling(int y, float vel, boolean always) {
         mAnimatingReveal = false;
-        mDisplayHeight = mDisplay.getHeight();
 
         mAnimY = y;
         mAnimVel = vel;
@@ -1301,7 +1299,7 @@
         if (mExpanded) {
             if (!always && (
                     vel > 200.0f
-                    || (y > (mDisplayHeight-25) && vel > -200.0f))) {
+                    || (y > (mDisplaySize.y-25) && vel > -200.0f))) {
                 // We are expanded, but they didn't move sufficiently to cause
                 // us to retract.  Animate back to the expanded position.
                 mAnimAccel = 2000.0f;
@@ -1319,7 +1317,7 @@
         } else {
             if (always || (
                     vel > 200.0f
-                    || (y > (mDisplayHeight/2) && vel > -200.0f))) {
+                    || (y > (mDisplaySize.y/2) && vel > -200.0f))) {
                 // We are collapsed, and they moved enough to allow us to
                 // expand.  Animate in the notifications.
                 mAnimAccel = 2000.0f;
@@ -1371,14 +1369,14 @@
                 mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
             }
             if ((!mExpanded && y < hitSize) ||
-                    (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
+                    (mExpanded && y > (mDisplaySize.y-hitSize))) {
 
                 // We drop events at the edge of the screen to make the windowshade come
                 // down by accident less, especially when pushing open a device with a keyboard
                 // that rotates (like g1 and droid)
                 int x = (int)event.getRawX();
                 final int edgeBorder = mEdgeBorder;
-                if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
+                if (x >= edgeBorder && x < mDisplaySize.x - edgeBorder) {
                     prepareTracking(y, !mExpanded);// opening if we're not already fully visible
                     mVelocityTracker.addMovement(event);
                 }
@@ -1626,10 +1624,9 @@
                     + ", mAnimAccel=" + mAnimAccel);
             pw.println("  mCurAnimationTime=" + mCurAnimationTime
                     + " mAnimLastTime=" + mAnimLastTime);
-            pw.println("  mDisplayHeight=" + mDisplayHeight
-                    + " mAnimatingReveal=" + mAnimatingReveal
+            pw.println("  mAnimatingReveal=" + mAnimatingReveal
                     + " mViewDelta=" + mViewDelta);
-            pw.println("  mDisplayHeight=" + mDisplayHeight);
+            pw.println("  mDisplaySize=" + mDisplaySize);
             pw.println("  mExpandedParams: " + mExpandedParams);
             pw.println("  mExpandedView: " + viewInfo(mExpandedView));
             pw.println("  mExpandedDialog: " + mExpandedDialog);
@@ -1721,17 +1718,13 @@
     void onTrackingViewAttached() {
         WindowManager.LayoutParams lp;
         int pixelFormat;
-        Drawable bg;
 
         /// ---------- Expanded View --------------
         pixelFormat = PixelFormat.TRANSLUCENT;
 
-        final int disph = mDisplay.getHeight();
         lp = mExpandedDialog.getWindow().getAttributes();
-        lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
-        lp.height = getExpandedHeight();
         lp.x = 0;
-        mTrackingPosition = lp.y = -disph; // sufficiently large negative
+        mTrackingPosition = lp.y = mDisplaySize.y; // sufficiently large negative
         lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
         lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
@@ -1741,9 +1734,9 @@
         lp.format = pixelFormat;
         lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
         lp.setTitle("StatusBarExpanded");
-        mExpandedDialog.getWindow().setAttributes(lp);
-        mExpandedDialog.getWindow().setFormat(pixelFormat);
         mExpandedParams = lp;
+        updateExpandedSize();
+        mExpandedDialog.getWindow().setFormat(pixelFormat);
 
         mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
         mExpandedDialog.setContentView(mExpandedView,
@@ -1751,7 +1744,6 @@
                                            ViewGroup.LayoutParams.MATCH_PARENT));
         mExpandedDialog.getWindow().setBackgroundDrawable(null);
         mExpandedDialog.show();
-        FrameLayout hack = (FrameLayout)mExpandedView.getParent();
     }
 
     void setDateViewVisibility(boolean visible, int anim) {
@@ -1768,6 +1760,20 @@
         }
     }
 
+    void updateExpandedInvisiblePosition() {
+        if (mTrackingView != null) {
+            mTrackingPosition = -mDisplaySize.y;
+            if (mTrackingParams != null) {
+                mTrackingParams.y = mTrackingPosition;
+                WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+            }
+        }
+        if (mExpandedParams != null) {
+            mExpandedParams.y = -mDisplaySize.y;
+            mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+        }
+    }
+
     void updateExpandedViewPos(int expandedPosition) {
         if (SPEW) {
             Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
@@ -1776,22 +1782,12 @@
         }
 
         int h = mStatusBarView.getHeight();
-        int disph = mDisplay.getHeight();
+        int disph = mDisplaySize.y;
 
         // If the expanded view is not visible, make sure they're still off screen.
         // Maybe the view was resized.
         if (!mExpandedVisible) {
-            if (mTrackingView != null) {
-                mTrackingPosition = -disph;
-                if (mTrackingParams != null) {
-                    mTrackingParams.y = mTrackingPosition;
-                    WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
-                }
-            }
-            if (mExpandedParams != null) {
-                mExpandedParams.y = -disph;
-                mExpandedDialog.getWindow().setAttributes(mExpandedParams);
-            }
+            updateExpandedInvisiblePosition();
             return;
         }
 
@@ -1816,14 +1812,21 @@
         WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
 
         if (mExpandedParams != null) {
-            mCloseView.getLocationInWindow(mPositionTmp);
-            final int closePos = mPositionTmp[1];
+            if (mCloseView.getWindowVisibility() == View.VISIBLE) {
+                mCloseView.getLocationInWindow(mPositionTmp);
+                final int closePos = mPositionTmp[1];
 
-            mExpandedContents.getLocationInWindow(mPositionTmp);
-            final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
+                mExpandedContents.getLocationInWindow(mPositionTmp);
+                final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
 
-            mExpandedParams.y = pos + mTrackingView.getHeight()
-                    - (mTrackingParams.height-closePos) - contentsBottom;
+                mExpandedParams.y = pos + mTrackingView.getHeight()
+                        - (mTrackingParams.height-closePos) - contentsBottom;
+            } else {
+                // If the tracking view is not yet visible, then we can't have
+                // a good value of the close view location.  We need to wait for
+                // it to be visible to do a layout.
+                mExpandedParams.y = -mDisplaySize.y;
+            }
             int max = h;
             if (mExpandedParams.y > max) {
                 mExpandedParams.y = max;
@@ -1859,14 +1862,24 @@
         }
     }
 
-    int getExpandedHeight() {
-        return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
+    int getExpandedHeight(int disph) {
+        return disph - mStatusBarView.getHeight() - mCloseView.getHeight();
     }
 
-    void updateExpandedHeight() {
-        if (mExpandedView != null) {
-            mExpandedParams.height = getExpandedHeight();
-            mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+    void updateDisplaySize() {
+        mDisplay.getSize(mDisplaySize);
+        updateExpandedSize();
+    }
+
+    void updateExpandedSize() {
+        if (mExpandedDialog != null) {
+            mExpandedParams.width = mDisplaySize.x;
+            mExpandedParams.height = getExpandedHeight(mDisplaySize.y);
+            if (!mExpandedVisible) {
+                updateExpandedInvisiblePosition();
+            } else {
+                mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index c2390e8..db6907c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -77,6 +77,7 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
+        mService.updateDisplaySize();
         boolean nightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
                 == Configuration.UI_MODE_NIGHT_YES;
         if (mNightMode != nightMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrackingView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrackingView.java
index fd32a3d..fc0f332 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrackingView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrackingView.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.os.Handler;
 import android.util.AttributeSet;
 import android.view.Display;
 import android.view.KeyEvent;
@@ -29,6 +30,7 @@
     PhoneStatusBar mService;
     boolean mTracking;
     int mStartX, mStartY;
+    Handler mHandler = new Handler();
 
     public TrackingView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -39,7 +41,6 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-        mService.updateExpandedHeight();
     }
 
     @Override
@@ -60,4 +61,16 @@
         super.onAttachedToWindow();
         mService.onTrackingViewAttached();
     }
+
+    @Override
+    protected void onWindowVisibilityChanged(int visibility) {
+        super.onWindowVisibilityChanged(visibility);
+        if (visibility == VISIBLE) {
+            mHandler.post(new Runnable() {
+                @Override public void run() {
+                    mService.updateExpandedViewPos(PhoneStatusBar.EXPANDED_LEAVE_ALONE);
+                }
+            });
+        }
+    }
 }
diff --git a/policy/src/com/android/internal/policy/impl/IconUtilities.java b/policy/src/com/android/internal/policy/impl/IconUtilities.java
index 99055cf..4564f90 100644
--- a/policy/src/com/android/internal/policy/impl/IconUtilities.java
+++ b/policy/src/com/android/internal/policy/impl/IconUtilities.java
@@ -186,6 +186,7 @@
         mask.recycle();
 
         dest.drawBitmap(src, 0, 0, mPaint);
+        dest.setBitmap(null);
 
         return result;
     }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 174f733..9c19da2 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -35,13 +35,13 @@
 import com.android.internal.view.menu.MenuDialogHelper;
 import com.android.internal.view.menu.MenuPresenter;
 import com.android.internal.view.menu.MenuView;
-import com.android.internal.view.menu.SubMenuBuilder;
 import com.android.internal.widget.ActionBarContainer;
 import com.android.internal.widget.ActionBarContextView;
 import com.android.internal.widget.ActionBarView;
 
 import android.app.KeyguardManager;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -175,6 +175,8 @@
     private AudioManager mAudioManager;
     private KeyguardManager mKeyguardManager;
 
+    private int mUiOptions = 0;
+
     public PhoneWindow(Context context) {
         super(context);
         mLayoutInflater = LayoutInflater.from(context);
@@ -213,6 +215,11 @@
     }
 
     @Override
+    public void setUiOptions(int uiOptions) {
+        mUiOptions = uiOptions;
+    }
+
+    @Override
     public void setContentView(int layoutResID) {
         if (mContentParent == null) {
             installDecor();
@@ -2634,8 +2641,14 @@
                         mActionBar.initIndeterminateProgress();
                     }
 
-                    final boolean splitActionBar = getWindowStyle().getBoolean(
-                            com.android.internal.R.styleable.Window_windowSplitActionBar, false);
+                    boolean splitActionBar = false;
+                    if ((mUiOptions & ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW) != 0) {
+                        splitActionBar = getContext().getResources().getBoolean(
+                                com.android.internal.R.bool.split_action_bar_is_narrow);
+                    } else {
+                        splitActionBar = getWindowStyle().getBoolean(
+                                com.android.internal.R.styleable.Window_windowSplitActionBar, false);
+                    }
                     if (splitActionBar) {
                         final ActionBarContainer splitView = (ActionBarContainer) findViewById(
                                 com.android.internal.R.id.split_action_bar);
@@ -2648,7 +2661,7 @@
                                     com.android.internal.R.id.action_context_bar);
                             cab.setSplitView(splitView);
                         } else {
-                            Log.e(TAG, "Window style requested split action bar with " +
+                            Log.e(TAG, "Requested split action bar with " +
                                     "incompatible window decor! Ignoring request.");
                         }
                     }
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 541a8cf..1bbe934 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -344,7 +344,7 @@
             mPolicyManager.registerListener(mPolicyListener);
         } catch (RemoteException e) {
             // ouch, no rules updates means some processes may never get network
-            Slog.e(TAG, "unable to register INetworkPolicyListener", e);
+            loge("unable to register INetworkPolicyListener" + e.toString());
         }
 
         final PowerManager powerManager = (PowerManager) context.getSystemService(
@@ -798,9 +798,11 @@
         }
 
         public void expire() {
-            log("ConnectivityService FeatureUser expire(" +
-                    mNetworkType + ", " + mFeature + ", " + mBinder +"), created " +
-                    (System.currentTimeMillis() - mCreateTime) + " mSec ago");
+            if (VDBG) {
+                log("ConnectivityService FeatureUser expire(" +
+                        mNetworkType + ", " + mFeature + ", " + mBinder +"), created " +
+                        (System.currentTimeMillis() - mCreateTime) + " mSec ago");
+            }
             stopUsingNetworkFeature(this, false);
         }
 
@@ -843,7 +845,7 @@
         if(networkType == ConnectivityManager.TYPE_MOBILE) {
             usedNetworkType = convertFeatureToNetworkType(feature);
             if (usedNetworkType < 0) {
-                Slog.e(TAG, "Can't match any netTracker!");
+                loge("Can't match any netTracker!");
                 usedNetworkType = networkType;
             }
         }
@@ -953,7 +955,7 @@
             return stopUsingNetworkFeature(u, true);
         } else {
             // none found!
-            if (DBG) log("ignoring stopUsingNetworkFeature - not a live request");
+            if (VDBG) log("ignoring stopUsingNetworkFeature - not a live request");
             return 1;
         }
     }
@@ -1081,7 +1083,7 @@
 
         if (tracker == null || !tracker.getNetworkInfo().isConnected() ||
                 tracker.isTeardownRequested()) {
-            if (DBG) {
+            if (VDBG) {
                 log("requestRouteToHostAddress on down network " +
                            "(" + networkType + ") - dropped");
             }
@@ -1152,13 +1154,13 @@
             }
         }
         if (doAdd) {
-            if (DBG) log("Adding " + r + " for interface " + ifaceName);
+            if (VDBG) log("Adding " + r + " for interface " + ifaceName);
             mAddedRoutes.add(r);
             try {
                 mNetd.addRoute(ifaceName, r);
             } catch (Exception e) {
                 // never crash - catch them all
-                loge("Exception trying to add a route: " + e);
+                if (VDBG) loge("Exception trying to add a route: " + e);
                 return false;
             }
         } else {
@@ -1166,16 +1168,16 @@
             // we can remove it from the table
             mAddedRoutes.remove(r);
             if (mAddedRoutes.contains(r) == false) {
-                if (DBG) log("Removing " + r + " for interface " + ifaceName);
+                if (VDBG) log("Removing " + r + " for interface " + ifaceName);
                 try {
                     mNetd.removeRoute(ifaceName, r);
                 } catch (Exception e) {
                     // never crash - catch them all
-                    loge("Exception trying to remove a route: " + e);
+                    if (VDBG) loge("Exception trying to remove a route: " + e);
                     return false;
                 }
             } else {
-                if (DBG) log("not removing " + r + " as it's still in use");
+                if (VDBG) log("not removing " + r + " as it's still in use");
             }
         }
         return true;
@@ -1220,7 +1222,7 @@
         enforceAccessPermission();
         boolean retVal = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.MOBILE_DATA, 1) == 1;
-        if (DBG) log("getMobileDataEnabled returning " + retVal);
+        if (VDBG) log("getMobileDataEnabled returning " + retVal);
         return retVal;
     }
 
@@ -1247,7 +1249,7 @@
             mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
 
             if (LOGD_RULES) {
-                Slog.d(TAG, "onUidRulesChanged(uid=" + uid + ", uidRules=" + uidRules + ")");
+                log("onUidRulesChanged(uid=" + uid + ", uidRules=" + uidRules + ")");
             }
 
             synchronized (mRulesLock) {
@@ -1268,8 +1270,7 @@
             mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
 
             if (LOGD_RULES) {
-                Slog.d(TAG,
-                        "onMeteredIfacesChanged(ifaces=" + Arrays.toString(meteredIfaces) + ")");
+                log("onMeteredIfacesChanged(ifaces=" + Arrays.toString(meteredIfaces) + ")");
             }
 
             synchronized (mRulesLock) {
@@ -1294,8 +1295,8 @@
 
     private void handleSetMobileData(boolean enabled) {
         if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
-            if (DBG) {
-                Slog.d(TAG, mNetTrackers[ConnectivityManager.TYPE_MOBILE].toString() + enabled);
+            if (VDBG) {
+                log(mNetTrackers[ConnectivityManager.TYPE_MOBILE].toString() + enabled);
             }
             mNetTrackers[ConnectivityManager.TYPE_MOBILE].setDataEnable(enabled);
         }
@@ -1587,7 +1588,7 @@
                         mNetConfigs[type].priority) ||
                         mNetworkPreference == mActiveDefaultNetwork) {
                         // don't accept this one
-                        if (DBG) {
+                        if (VDBG) {
                             log("Not broadcasting CONNECT_ACTION " +
                                 "to torn down network " + info.getTypeName());
                         }
@@ -1688,9 +1689,11 @@
                     }
                 } else {
                     resetMask = NetworkUtils.RESET_ALL_ADDRESSES;
-                    log("handleConnectivityChange: interface not not equivalent reset both" +
-                            " linkProperty[" + netType + "]:" +
-                            " resetMask=" + resetMask);
+                    if (DBG) {
+                        log("handleConnectivityChange: interface not not equivalent reset both" +
+                                " linkProperty[" + netType + "]:" +
+                                " resetMask=" + resetMask);
+                    }
                 }
             }
             if (mNetConfigs[netType].isDefault()) {
@@ -1798,7 +1801,7 @@
         String bufferSizes = SystemProperties.get(key);
 
         if (bufferSizes.length() == 0) {
-            loge(key + " not found in system properties. Using defaults");
+            if (VDBG) log(key + " not found in system properties. Using defaults");
 
             // Setting to default values so we won't be stuck to previous values
             key = "net.tcp.buffersize.default";
@@ -1807,7 +1810,7 @@
 
         // Set values in kernel
         if (bufferSizes.length() != 0) {
-            if (DBG) {
+            if (VDBG) {
                 log("Setting TCP values: [" + bufferSizes
                         + "] which comes from [" + key + "]");
             }
@@ -1849,7 +1852,7 @@
      */
     private void reassessPidDns(int myPid, boolean doBump)
     {
-        if (DBG) log("reassessPidDns for pid " + myPid);
+        if (VDBG) log("reassessPidDns for pid " + myPid);
         for(int i : mPriorityList) {
             if (mNetConfigs[i].isDefault()) {
                 continue;
@@ -1935,7 +1938,7 @@
             String value = mDefaultDns.getHostAddress();
             if (!value.equals(SystemProperties.get("net.dns1"))) {
                 if (DBG) {
-                    log("no dns provided for " + network + " - using " + value);
+                    loge("no dns provided for " + network + " - using " + value);
                 }
                 changed = true;
                 SystemProperties.set("net.dns1", value);
@@ -1948,7 +1951,7 @@
                 if (!changed && value.equals(SystemProperties.get(key))) {
                     continue;
                 }
-                if (DBG) {
+                if (VDBG) {
                     log("adding dns " + value + " for " + network);
                 }
                 changed = true;
@@ -1957,7 +1960,7 @@
         }
         for (int i = last + 1; i <= mNumDnsEntries; ++i) {
             String key = "net.dns" + i;
-            if (DBG) log("erasing " + key);
+            if (VDBG) log("erasing " + key);
             changed = true;
             SystemProperties.set(key, "");
         }
@@ -1968,7 +1971,7 @@
                 mNetd.setDnsServersForInterface(iface, NetworkUtils.makeStrings(dnses));
                 mNetd.setDefaultInterfaceForDns(iface);
             } catch (Exception e) {
-                Slog.e(TAG, "exception setting default dns interface: " + e);
+                loge("exception setting default dns interface: " + e);
             }
         }
         if (!domains.equals(SystemProperties.get("net.dns.search"))) {
@@ -1998,7 +2001,7 @@
                     mNetd.setDnsServersForInterface(p.getInterfaceName(),
                             NetworkUtils.makeStrings(dnses));
                 } catch (Exception e) {
-                    Slog.e(TAG, "exception setting dns servers: " + e);
+                    loge("exception setting dns servers: " + e);
                 }
                 // set per-pid dns for attached secondary nets
                 List pids = mNetRequestersPids[netType];
@@ -2335,7 +2338,7 @@
 
     // 100 percent is full good, 0 is full bad.
     public void reportInetCondition(int networkType, int percentage) {
-        if (DBG) log("reportNetworkCondition(" + networkType + ", " + percentage + ")");
+        if (VDBG) log("reportNetworkCondition(" + networkType + ", " + percentage + ")");
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.STATUS_BAR,
                 "ConnectivityService");
@@ -2372,7 +2375,7 @@
         mDefaultInetCondition = condition;
         int delay;
         if (mInetConditionChangeInFlight == false) {
-            if (DBG) log("starting a change hold");
+            if (VDBG) log("starting a change hold");
             // setup a new hold to debounce this
             if (mDefaultInetCondition > 50) {
                 delay = Settings.Secure.getInt(mContext.getContentResolver(),
@@ -2387,12 +2390,12 @@
         } else {
             // we've set the new condition, when this hold ends that will get
             // picked up
-            if (DBG) log("currently in hold - not setting new end evt");
+            if (VDBG) log("currently in hold - not setting new end evt");
         }
     }
 
     private void handleInetConditionHoldEnd(int netType, int sequence) {
-        if (DBG) {
+        if (VDBG) {
             log("Inet hold end, net=" + netType +
                     ", condition =" + mDefaultInetCondition +
                     ", published condition =" + mDefaultInetConditionPublished);
@@ -2490,7 +2493,7 @@
                 mDefaultProxy = null;
             }
         }
-        if (DBG) log("changing default proxy to " + proxy);
+        if (VDBG) log("changing default proxy to " + proxy);
         if ((proxy == null && mGlobalProxy == null) || proxy.equals(mGlobalProxy)) return;
         if (mGlobalProxy != null) return;
         sendProxyBroadcast(proxy);
@@ -2517,7 +2520,7 @@
 
     private void sendProxyBroadcast(ProxyProperties proxy) {
         if (proxy == null) proxy = new ProxyProperties("", 0, "");
-        log("sending Proxy Broadcast for " + proxy);
+        if (DBG) log("sending Proxy Broadcast for " + proxy);
         Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
             Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index 9cb772e..ecbad099 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -183,7 +183,9 @@
                     android.R.dimen.notification_large_icon_height);
             icon.setBounds(0, 0, width, height);
             bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-            icon.draw(new Canvas(bitmap));
+            Canvas c = new Canvas(bitmap);
+            icon.draw(c);
+            c.setBitmap(null);
         }
 
         // Configure the interface. Abort if any of these steps fails.
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 88e0fa8..3d977d0 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -3326,7 +3326,7 @@
         if (pkg.applicationInfo.nativeLibraryDir != null) {
             try {
                 final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
-                final String dataPathString = dataPath.getCanonicalFile().getPath();
+                final String dataPathString = dataPath.getCanonicalPath();
 
                 if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
                     /*
@@ -3340,7 +3340,7 @@
                         Log.i(TAG, "removed obsolete native libraries for system package "
                                 + path);
                     }
-                } else if (nativeLibraryDir.getCanonicalFile().getParent()
+                } else if (nativeLibraryDir.getParentFile().getCanonicalPath()
                         .equals(dataPathString)) {
                     /*
                      * Make sure the native library dir isn't a symlink to
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index f8059f5..e0b5e17 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -4919,6 +4919,7 @@
         matrix.postTranslate(-(int)(frame.left*scale), -(int)(frame.top*scale));
         Canvas canvas = new Canvas(bm);
         canvas.drawBitmap(rawss, matrix, null);
+        canvas.setBitmap(null);
 
         rawss.recycle();
         return bm;
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 7bf3e0a..f4be168 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -40,6 +40,7 @@
 
 #include "GLExtensions.h"
 #include "HWComposer.h"
+#include "SurfaceFlinger.h"
 
 using namespace android;
 
@@ -75,7 +76,7 @@
         const sp<SurfaceFlinger>& flinger,
         uint32_t dpy)
     : DisplayHardwareBase(flinger, dpy),
-      mFlags(0), mHwc(0)
+      mFlinger(flinger), mFlags(0), mHwc(0)
 {
     init(dpy);
 }
@@ -310,7 +311,7 @@
 
 
     // initialize the H/W composer
-    mHwc = new HWComposer();
+    mHwc = new HWComposer(mFlinger);
     if (mHwc->initCheck() == NO_ERROR) {
         mHwc->setFrameBuffer(mDisplay, mSurface);
     }
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index cdf89fd..40a6f1e 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -95,6 +95,7 @@
     void init(uint32_t displayIndex) __attribute__((noinline));
     void fini() __attribute__((noinline));
 
+    sp<SurfaceFlinger> mFlinger;
     EGLDisplay      mDisplay;
     EGLSurface      mSurface;
     EGLContext      mContext;
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
index 30eb258..3ebc7b6 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
@@ -37,7 +37,7 @@
 
                 ~DisplayHardwareBase();
 
-    // console managment
+    // console management
     void releaseScreen() const;
     void acquireScreen() const;
     bool isScreenAcquired() const;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 4a3b20d..7d1bdf0 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -30,12 +30,14 @@
 #include <EGL/egl.h>
 
 #include "HWComposer.h"
+#include "SurfaceFlinger.h"
 
 namespace android {
 // ---------------------------------------------------------------------------
 
-HWComposer::HWComposer()
-    : mModule(0), mHwc(0), mList(0), mCapacity(0),
+HWComposer::HWComposer(const sp<SurfaceFlinger>& flinger)
+    : mFlinger(flinger),
+      mModule(0), mHwc(0), mList(0), mCapacity(0),
       mDpy(EGL_NO_DISPLAY), mSur(EGL_NO_SURFACE)
 {
     int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
@@ -44,6 +46,13 @@
         err = hwc_open(mModule, &mHwc);
         LOGE_IF(err, "%s device failed to initialize (%s)",
                 HWC_HARDWARE_COMPOSER, strerror(-err));
+        if (err == 0) {
+            if (mHwc->registerProcs) {
+                mCBContext.hwc = this;
+                mCBContext.procs.invalidate = &hook_invalidate;
+                mHwc->registerProcs(mHwc, &mCBContext.procs);
+            }
+        }
     }
 }
 
@@ -58,6 +67,14 @@
     return mHwc ? NO_ERROR : NO_INIT;
 }
 
+void HWComposer::hook_invalidate(struct hwc_procs* procs) {
+    reinterpret_cast<cb_context *>(procs)->hwc->invalidate();
+}
+
+void HWComposer::invalidate() {
+    mFlinger->signalEvent();
+}
+
 void HWComposer::setFrameBuffer(EGLDisplay dpy, EGLSurface sur) {
     mDpy = (hwc_display_t)dpy;
     mSur = (hwc_surface_t)sur;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 5a9e9eb..983898a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -24,16 +24,19 @@
 
 #include <hardware/hwcomposer.h>
 
+#include <utils/StrongPointer.h>
+
 namespace android {
 // ---------------------------------------------------------------------------
 
 class String8;
+class SurfaceFlinger;
 
 class HWComposer
 {
 public:
 
-    HWComposer();
+    HWComposer(const sp<SurfaceFlinger>& flinger);
     ~HWComposer();
 
     status_t initCheck() const;
@@ -60,12 +63,21 @@
     void dump(String8& out, char* scratch, size_t SIZE) const;
 
 private:
+    struct cb_context {
+        hwc_procs_t procs;
+        HWComposer* hwc;
+    };
+    static void hook_invalidate(struct hwc_procs* procs);
+    void invalidate();
+
+    sp<SurfaceFlinger>      mFlinger;
     hw_module_t const*      mModule;
     hwc_composer_device_t*  mHwc;
     hwc_layer_list_t*       mList;
     size_t                  mCapacity;
     hwc_display_t           mDpy;
     hwc_surface_t           mSur;
+    cb_context              mCBContext;
 };
 
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 886bb2a..383c045 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -172,17 +172,14 @@
 
 void Layer::setGeometry(hwc_layer_t* hwcl)
 {
-    hwcl->compositionType = HWC_FRAMEBUFFER;
-    hwcl->hints = 0;
-    hwcl->flags = 0;
-    hwcl->transform = 0;
-    hwcl->blending = HWC_BLENDING_NONE;
+    LayerBaseClient::setGeometry(hwcl);
+
+    hwcl->flags &= ~HWC_SKIP_LAYER;
 
     // we can't do alpha-fade with the hwc HAL
     const State& s(drawingState());
     if (s.alpha < 0xFF) {
         hwcl->flags = HWC_SKIP_LAYER;
-        return;
     }
 
     /*
@@ -205,26 +202,9 @@
     // we can only handle simple transformation
     if (finalTransform & Transform::ROT_INVALID) {
         hwcl->flags = HWC_SKIP_LAYER;
-        return;
+    } else {
+        hwcl->transform = finalTransform;
     }
-
-    hwcl->transform = finalTransform;
-
-    if (!isOpaque()) {
-        hwcl->blending = mPremultipliedAlpha ?
-                HWC_BLENDING_PREMULT : HWC_BLENDING_COVERAGE;
-    }
-
-    // scaling is already applied in mTransformedBounds
-    hwcl->displayFrame.left   = mTransformedBounds.left;
-    hwcl->displayFrame.top    = mTransformedBounds.top;
-    hwcl->displayFrame.right  = mTransformedBounds.right;
-    hwcl->displayFrame.bottom = mTransformedBounds.bottom;
-
-    hwcl->visibleRegionScreen.rects =
-            reinterpret_cast<hwc_rect_t const *>(
-                    visibleRegionScreen.getArray(
-                            &hwcl->visibleRegionScreen.numRects));
 }
 
 void Layer::setPerFrameData(hwc_layer_t* hwcl) {
@@ -235,9 +215,9 @@
         // HWC handle it.
         hwcl->flags |= HWC_SKIP_LAYER;
         hwcl->handle = NULL;
-        return;
+    } else {
+        hwcl->handle = buffer->handle;
     }
-    hwcl->handle = buffer->handle;
 
     if (isCropped()) {
         hwcl->sourceCrop.left   = mCurrentCrop.left;
@@ -247,8 +227,13 @@
     } else {
         hwcl->sourceCrop.left   = 0;
         hwcl->sourceCrop.top    = 0;
-        hwcl->sourceCrop.right  = buffer->width;
-        hwcl->sourceCrop.bottom = buffer->height;
+        if (buffer != NULL) {
+            hwcl->sourceCrop.right  = buffer->width;
+            hwcl->sourceCrop.bottom = buffer->height;
+        } else {
+            hwcl->sourceCrop.right  = mTransformedBounds.width();
+            hwcl->sourceCrop.bottom = mTransformedBounds.height();
+        }
     }
 }
 
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index c86c659..e04c533 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -302,13 +302,47 @@
     }
 }
 
-void LayerBase::setGeometry(hwc_layer_t* hwcl) {
-    hwcl->flags |= HWC_SKIP_LAYER;
+void LayerBase::setGeometry(hwc_layer_t* hwcl)
+{
+    hwcl->compositionType = HWC_FRAMEBUFFER;
+    hwcl->hints = 0;
+    hwcl->flags = HWC_SKIP_LAYER;
+    hwcl->transform = 0;
+    hwcl->blending = HWC_BLENDING_NONE;
+
+    // this gives us only the "orientation" component of the transform
+    const State& s(drawingState());
+    const uint32_t finalTransform = s.transform.getOrientation();
+    // we can only handle simple transformation
+    if (finalTransform & Transform::ROT_INVALID) {
+        hwcl->flags = HWC_SKIP_LAYER;
+    } else {
+        hwcl->transform = finalTransform;
+    }
+
+    if (!isOpaque()) {
+        hwcl->blending = mPremultipliedAlpha ?
+                HWC_BLENDING_PREMULT : HWC_BLENDING_COVERAGE;
+    }
+
+    // scaling is already applied in mTransformedBounds
+    hwcl->displayFrame.left   = mTransformedBounds.left;
+    hwcl->displayFrame.top    = mTransformedBounds.top;
+    hwcl->displayFrame.right  = mTransformedBounds.right;
+    hwcl->displayFrame.bottom = mTransformedBounds.bottom;
+    hwcl->visibleRegionScreen.rects =
+            reinterpret_cast<hwc_rect_t const *>(
+                    visibleRegionScreen.getArray(
+                            &hwcl->visibleRegionScreen.numRects));
 }
 
 void LayerBase::setPerFrameData(hwc_layer_t* hwcl) {
     hwcl->compositionType = HWC_FRAMEBUFFER;
     hwcl->handle = NULL;
+    hwcl->sourceCrop.left   = 0;
+    hwcl->sourceCrop.top    = 0;
+    hwcl->sourceCrop.right  = mTransformedBounds.width();
+    hwcl->sourceCrop.bottom = mTransformedBounds.height();
 }
 
 void LayerBase::setFiltering(bool filtering)
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java
index 7bf25cf..4037a69 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java
@@ -77,13 +77,23 @@
             super.onDraw(canvas);
             canvas.drawRGB(255, 255, 255);
 
+            mMediumPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+            mMediumPaint.setStrokeWidth(2.0f);
             canvas.drawText("Hello OpenGL renderer!", 100, 20, mMediumPaint);
+
+            mMediumPaint.setStyle(Paint.Style.FILL);
             mMediumPaint.setTextAlign(Paint.Align.CENTER);
             canvas.drawText("Hello OpenGL renderer!", 100, 40, mMediumPaint);
+
+            mMediumPaint.setStyle(Paint.Style.STROKE);
+            mMediumPaint.setStrokeWidth(2.0f);
             mMediumPaint.setTextAlign(Paint.Align.RIGHT);
             canvas.drawText("Hello OpenGL renderer!", 100, 60, mMediumPaint);
+
+            mMediumPaint.setStyle(Paint.Style.FILL);
             mMediumPaint.setTextAlign(Paint.Align.LEFT);
             canvas.drawText("Hello OpenGL renderer!", 100, 100, mMediumPaint);
+
             mMediumPaint.setShadowLayer(2.5f, 0.0f, 0.0f, 0xff000000);
             canvas.drawText("Hello OpenGL renderer!", 100, 150, mMediumPaint);
             mMediumPaint.clearShadowLayer();
diff --git a/tests/TileBenchmark/res/values/strings.xml b/tests/TileBenchmark/res/values/strings.xml
index 66972ac..c4fd189 100644
--- a/tests/TileBenchmark/res/values/strings.xml
+++ b/tests/TileBenchmark/res/values/strings.xml
@@ -71,8 +71,16 @@
     <string name="frames_per_second">Frames/sec</string>
     <!-- Portion of viewport covered by good tiles [CHAR LIMIT=15] -->
     <string name="viewport_coverage">Coverage</string>
+    <!-- Milliseconds taken to inval, and re-render the page [CHAR LIMIT=15] -->
+    <string name="render_millis">RenderMillis</string>
     <!-- Format string for stat value overlay [CHAR LIMIT=15] -->
     <string name="format_stat">%4.4f</string>
+
+    <!-- Format string for viewport position value overlay [CHAR LIMIT=25] -->
+    <string name="format_view_pos">View:(%1$d,%2$d)-(%3$d,%4$d)</string>
+    <!-- Format string for viewport position value overlay [CHAR LIMIT=25] -->
+    <string name="format_inval_pos">Inval:(%1$d,%2$d)-(%3$d,%4$d)</string>
+
     <!-- Format string for displaying aggregate stats+values (nr of valid tiles,
     etc.) [CHAR LIMIT=20] -->
     <string name="format_stat_name">%1$-20s %2$3d</string>
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackActivity.java b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackActivity.java
index 36694a7..1eb1c00 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackActivity.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackActivity.java
@@ -83,14 +83,14 @@
         }
     };
 
-    private class LoadFileTask extends AsyncTask<String, Void, TileData[][]> {
+    private class LoadFileTask extends AsyncTask<String, Void, RunData> {
         @Override
-        protected TileData[][] doInBackground(String... params) {
-            TileData[][] data = null;
+        protected RunData doInBackground(String... params) {
+            RunData data = null;
             try {
                 FileInputStream fis = openFileInput(params[0]);
                 ObjectInputStream in = new ObjectInputStream(fis);
-                data = (TileData[][]) in.readObject();
+                data = (RunData) in.readObject();
                 in.close();
             } catch (IOException ex) {
                 ex.printStackTrace();
@@ -101,7 +101,7 @@
         }
 
         @Override
-        protected void onPostExecute(TileData data[][]) {
+        protected void onPostExecute(RunData data) {
             if (data == null) {
                 Toast.makeText(getApplicationContext(),
                         getResources().getString(R.string.error_no_data),
@@ -110,7 +110,7 @@
             }
             mPlaybackView.setData(data);
 
-            mFrameMax = data.length - 1;
+            mFrameMax = data.frames.length - 1;
             mSeekBar.setMax(mFrameMax);
 
             setFrame(null, 0);
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java
index 35b1563..aad138c 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java
@@ -22,10 +22,12 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.drawable.ShapeDrawable;
-import android.os.Bundle;
+
+import com.test.tilebenchmark.RunData.TileData;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 
 public class PlaybackGraphs {
     private static final int BAR_WIDTH = PlaybackView.TILE_SCALE * 3;
@@ -44,7 +46,7 @@
         return 0.0f;
     }
 
-    private interface MetricGen {
+    protected interface MetricGen {
         public double getValue(TileData[] frame);
 
         public double getMax();
@@ -52,7 +54,7 @@
         public int getLabelId();
     };
 
-    private static MetricGen[] Metrics = new MetricGen[] {
+    protected static MetricGen[] Metrics = new MetricGen[] {
             new MetricGen() {
                 // framerate graph
                 @Override
@@ -99,7 +101,7 @@
             }
     };
 
-    private interface StatGen {
+    protected interface StatGen {
         public double getValue(double sortedValues[]);
 
         public int getLabelId();
@@ -116,7 +118,7 @@
                 + sortedValues[intIndex + 1] * (alpha);
     }
 
-    private static StatGen[] Stats = new StatGen[] {
+    protected static StatGen[] Stats = new StatGen[] {
             new StatGen() {
                 @Override
                 public double getValue(double[] sortedValues) {
@@ -157,21 +159,22 @@
     }
 
     private ArrayList<ShapeDrawable> mShapes = new ArrayList<ShapeDrawable>();
-    private double[][] mStats = new double[Metrics.length][Stats.length];
+    protected double[][] mStats = new double[Metrics.length][Stats.length];
+    protected HashMap<String, Double> mSingleStats;
 
-    public void setData(TileData[][] tileProfilingData) {
+    public void setData(RunData data) {
         mShapes.clear();
-        double metricValues[] = new double[tileProfilingData.length];
+        double metricValues[] = new double[data.frames.length];
 
-        if (tileProfilingData.length == 0) {
+        if (data.frames.length == 0) {
             return;
         }
 
         for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) {
             // create graph out of rectangles, one per frame
             int lastBar = 0;
-            for (int frameIndex = 0; frameIndex < tileProfilingData.length; frameIndex++) {
-                TileData frame[] = tileProfilingData[frameIndex];
+            for (int frameIndex = 0; frameIndex < data.frames.length; frameIndex++) {
+                TileData frame[] = data.frames[frameIndex];
                 int newBar = (frame[0].top + frame[0].bottom) / 2;
 
                 MetricGen s = Metrics[metricIndex];
@@ -194,9 +197,11 @@
             // store aggregate statistics per metric (median, and similar)
             Arrays.sort(metricValues);
             for (int statIndex = 0; statIndex < Stats.length; statIndex++) {
-                mStats[metricIndex][statIndex] = Stats[statIndex]
-                        .getValue(metricValues);
+                mStats[metricIndex][statIndex] =
+                        Stats[statIndex].getValue(metricValues);
             }
+
+            mSingleStats = data.singleStats;
         }
     }
 
@@ -215,7 +220,7 @@
     }
 
     public void draw(Canvas canvas, ArrayList<ShapeDrawable> shapes,
-            String[] strings, Resources resources) {
+            ArrayList<String> strings, Resources resources) {
         canvas.scale(CANVAS_SCALE, CANVAS_SCALE);
 
         canvas.translate(BAR_WIDTH * Metrics.length, 0);
@@ -238,26 +243,9 @@
                 canvas.drawText(label, xPos, yPos, whiteLabels);
             }
         }
-        for (int stringIndex = 0; stringIndex < strings.length; stringIndex++) {
+        for (int stringIndex = 0; stringIndex < strings.size(); stringIndex++) {
             int yPos = LABELOFFSET + stringIndex * PlaybackView.TILE_SCALE / 2;
-            canvas.drawText(strings[stringIndex], 0, yPos, whiteLabels);
+            canvas.drawText(strings.get(stringIndex), 0, yPos, whiteLabels);
         }
     }
-
-    public Bundle getStatBundle(Resources resources) {
-        Bundle b = new Bundle();
-
-        for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) {
-            for (int statIndex = 0; statIndex < Stats.length; statIndex++) {
-                String metricLabel = resources.getString(
-                        Metrics[metricIndex].getLabelId());
-                String statLabel = resources.getString(
-                        Stats[statIndex].getLabelId());
-                double value = mStats[metricIndex][statIndex];
-                b.putDouble(metricLabel + " " + statLabel, value);
-            }
-        }
-
-        return b;
-    }
 }
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackView.java b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackView.java
index edc8643..5459c1f 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackView.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackView.java
@@ -30,10 +30,12 @@
 import android.view.MotionEvent;
 import android.view.View;
 
+import com.test.tilebenchmark.RunData.TileData;
+
 import java.util.ArrayList;
 
 public class PlaybackView extends View {
-    public static final int TILE_SCALE = 300;
+    public static final int TILE_SCALE = 256;
     private static final int INVAL_FLAG = -2;
     private static final int INVAL_CYCLE = 250;
 
@@ -41,9 +43,9 @@
     private PlaybackGraphs mGraphs;
 
     private ArrayList<ShapeDrawable> mTempShapes = new ArrayList<ShapeDrawable>();
-    private TileData mProfData[][] = null;
+    private RunData mProfData = null;
     private GestureDetector mGestureDetector = null;
-    private String mRenderStrings[] = new String[4];
+    private ArrayList<String> mRenderStrings = new ArrayList<String>();
 
     private class TileDrawable extends ShapeDrawable {
         TileData tile;
@@ -135,17 +137,30 @@
         invalidate(); // may have animations, force redraw
     }
 
+    private String statString(int labelId, int value) {
+        return getResources().getString(R.string.format_stat_name,
+                getResources().getString(labelId), value);
+    }
+    private String tileString(int formatStringId, TileData t) {
+        return getResources().getString(formatStringId,
+                t.left, t.top, t.right, t.bottom);
+    }
+
     public int setFrame(int frame) {
-        if (mProfData == null || mProfData.length == 0) {
+        if (mProfData == null || mProfData.frames.length == 0) {
             return 0;
         }
 
         int readyTiles = 0, unreadyTiles = 0, unplacedTiles = 0, numInvals = 0;
         mTempShapes.clear();
+        mRenderStrings.clear();
 
         // create tile shapes (as they're drawn on bottom)
-        for (TileData t : mProfData[frame]) {
-            if (t.level != INVAL_FLAG && t != mProfData[frame][0]) {
+        for (TileData t : mProfData.frames[frame]) {
+            if (t == mProfData.frames[frame][0]){
+                // viewport 'tile', add coords to render strings
+                mRenderStrings.add(tileString(R.string.format_view_pos, t));
+            } else  if (t.level != INVAL_FLAG) {
                 int colorId;
                 if (t.isReady) {
                     readyTiles++;
@@ -159,14 +174,16 @@
                 }
                 mTempShapes.add(new TileDrawable(t, colorId));
             } else {
+                // inval 'tile', count and add coords to render strings
                 numInvals++;
+                mRenderStrings.add(tileString(R.string.format_inval_pos, t));
             }
         }
 
         // create invalidate shapes (drawn above tiles)
         int invalId = 0;
-        for (TileData t : mProfData[frame]) {
-            if (t.level == INVAL_FLAG && t != mProfData[frame][0]) {
+        for (TileData t : mProfData.frames[frame]) {
+            if (t.level == INVAL_FLAG && t != mProfData.frames[frame][0]) {
                 TileDrawable invalShape = new TileDrawable(t,
                         R.color.inval_region_start);
                 ValueAnimator tileAnimator = ObjectAnimator.ofInt(invalShape,
@@ -186,26 +203,20 @@
             }
         }
 
-        mRenderStrings[0] = getResources().getString(R.string.format_stat_name,
-                getResources().getString(R.string.ready_tiles), readyTiles);
-        mRenderStrings[1] = getResources().getString(R.string.format_stat_name,
-                getResources().getString(R.string.unready_tiles), unreadyTiles);
-        mRenderStrings[2] = getResources().getString(R.string.format_stat_name,
-                getResources().getString(R.string.unplaced_tiles),
-                unplacedTiles);
-        mRenderStrings[3] = getResources().getString(R.string.format_stat_name,
-                getResources().getString(R.string.number_invalidates),
-                numInvals);
+        mRenderStrings.add(statString(R.string.ready_tiles, readyTiles));
+        mRenderStrings.add(statString(R.string.unready_tiles, unreadyTiles));
+        mRenderStrings.add(statString(R.string.unplaced_tiles, unplacedTiles));
+        mRenderStrings.add(statString(R.string.number_invalidates, numInvals));
 
         // draw view rect (using first TileData object, on top)
-        TileDrawable viewShape = new TileDrawable(mProfData[frame][0],
+        TileDrawable viewShape = new TileDrawable(mProfData.frames[frame][0],
                 R.color.view);
         mTempShapes.add(viewShape);
         this.invalidate();
         return frame;
     }
 
-    public void setData(TileData[][] tileProfilingData) {
+    public void setData(RunData tileProfilingData) {
         mProfData = tileProfilingData;
 
         mGraphs.setData(mProfData);
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java
index 1521807..a63a2f0 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java
@@ -51,11 +51,11 @@
 public class ProfileActivity extends Activity {
 
     public interface ProfileCallback {
-        public void profileCallback(TileData data[][]);
+        public void profileCallback(RunData data);
     }
 
     public static final String TEMP_FILENAME = "profile.tiles";
-    private static final int LOAD_TEST_DELAY = 2000; // nr of millis after load,
+    private static final int LOAD_TEST_DELAY = 1000; // nr of millis after load,
                                                      // before test
 
     Button mInspectButton;
@@ -135,6 +135,7 @@
         public void onPageFinished(WebView view, String url) {
             super.onPageFinished(view, url);
             view.requestFocus();
+
             new CountDownTimer(LOAD_TEST_DELAY, LOAD_TEST_DELAY) {
                 @Override
                 public void onTick(long millisUntilFinished) {
@@ -155,10 +156,10 @@
     }
 
     private class StoreFileTask extends
-            AsyncTask<Pair<String, TileData[][]>, Void, Void> {
+            AsyncTask<Pair<String, RunData>, Void, Void> {
 
         @Override
-        protected Void doInBackground(Pair<String, TileData[][]>... params) {
+        protected Void doInBackground(Pair<String, RunData>... params) {
             try {
                 FileOutputStream fos = openFileOutput(params[0].first,
                         Context.MODE_PRIVATE);
@@ -205,10 +206,8 @@
 
     /** auto - automatically scroll. */
     private void startViewProfiling(boolean auto) {
-        if (!auto) {
-            // manual, toggle capture button to indicate capture state to user
-            mCaptureButton.setChecked(true);
-        }
+        // toggle capture button to indicate capture state to user
+        mCaptureButton.setChecked(true);
         mWeb.startScrollTest(mCallback, auto);
         setTestingState(TestingState.START_TESTING);
     }
@@ -227,8 +226,8 @@
         mCallback = new ProfileCallback() {
             @SuppressWarnings("unchecked")
             @Override
-            public void profileCallback(TileData[][] data) {
-                new StoreFileTask().execute(new Pair<String, TileData[][]>(
+            public void profileCallback(RunData data) {
+                new StoreFileTask().execute(new Pair<String, RunData>(
                         TEMP_FILENAME, data));
                 mCaptureButton.setChecked(false);
                 setTestingState(TestingState.STOP_TESTING);
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
index d3941be..3fc4665 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
@@ -18,15 +18,19 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.webkit.WebView;
 
 import com.test.tilebenchmark.ProfileActivity.ProfileCallback;
+import com.test.tilebenchmark.RunData.TileData;
 
 public class ProfiledWebView extends WebView {
     private int mSpeed;
 
+    private boolean isTesting = false;
     private boolean isScrolling = false;
     private ProfileCallback mCallback;
+    private long mContentInvalMillis;
 
     public ProfiledWebView(Context context) {
         super(context);
@@ -47,7 +51,7 @@
 
     @Override
     protected void onDraw(android.graphics.Canvas canvas) {
-        if (isScrolling) {
+        if (isTesting && isScrolling) {
             if (canScrollVertically(1)) {
                 scrollBy(0, mSpeed);
             } else {
@@ -60,31 +64,53 @@
 
     /*
      * Called once the page is loaded to start scrolling for evaluating tiles.
-     * If autoScrolling isn't set, stop must be called manually.
+     * If autoScrolling isn't set, stop must be called manually. Before
+     * scrolling, invalidate all content and redraw it, measuring time taken.
      */
     public void startScrollTest(ProfileCallback callback, boolean autoScrolling) {
         isScrolling = autoScrolling;
         mCallback = callback;
-        tileProfilingStart();
+        isTesting = false;
+        mContentInvalMillis = System.currentTimeMillis();
+        registerPageSwapCallback();
+        contentInvalidateAll();
         invalidate();
     }
 
     /*
+     * Called after the manual contentInvalidateAll, after the tiles have all
+     * been redrawn.
+     */
+    @Override
+    protected void pageSwapCallback() {
+        mContentInvalMillis = System.currentTimeMillis() - mContentInvalMillis;
+        super.pageSwapCallback();
+        Log.d("ProfiledWebView", "REDRAW TOOK " + mContentInvalMillis
+                + "millis");
+        isTesting = true;
+        invalidate(); // ensure a redraw so that auto-scrolling can occur
+        tileProfilingStart();
+    }
+
+    /*
      * Called once the page has stopped scrolling
      */
     public void stopScrollTest() {
-        super.tileProfilingStop();
+        tileProfilingStop();
+        isTesting = false;
 
         if (mCallback == null) {
             tileProfilingClear();
             return;
         }
 
-        TileData data[][] = new TileData[super.tileProfilingNumFrames()][];
-        for (int frame = 0; frame < data.length; frame++) {
-            data[frame] = new TileData[
+        RunData data = new RunData(super.tileProfilingNumFrames());
+        data.singleStats.put(getResources().getString(R.string.render_millis),
+                (double)mContentInvalMillis);
+        for (int frame = 0; frame < data.frames.length; frame++) {
+            data.frames[frame] = new TileData[
                     tileProfilingNumTilesInFrame(frame)];
-            for (int tile = 0; tile < data[frame].length; tile++) {
+            for (int tile = 0; tile < data.frames[frame].length; tile++) {
                 int left = tileProfilingGetInt(frame, tile, "left");
                 int top = tileProfilingGetInt(frame, tile, "top");
                 int right = tileProfilingGetInt(frame, tile, "right");
@@ -96,18 +122,18 @@
 
                 float scale = tileProfilingGetFloat(frame, tile, "scale");
 
-                data[frame][tile] = new TileData(left, top, right, bottom,
+                data.frames[frame][tile] = data.new TileData(left, top, right, bottom,
                         isReady, level, scale);
             }
         }
-        super.tileProfilingClear();
+        tileProfilingClear();
 
         mCallback.profileCallback(data);
     }
 
     @Override
     public void loadUrl(String url) {
-        if (!url.startsWith("http://")) {
+        if (!url.startsWith("http://") && !url.startsWith("file://")) {
             url = "http://" + url;
         }
         super.loadUrl(url);
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/RunData.java b/tests/TileBenchmark/src/com/test/tilebenchmark/RunData.java
new file mode 100644
index 0000000..2da61cc
--- /dev/null
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/RunData.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 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.test.tilebenchmark;
+
+import java.io.Serializable;
+import java.util.HashMap;
+
+public class RunData implements Serializable {
+    public TileData[][] frames;
+    public HashMap<String, Double> singleStats = new HashMap<String, Double>();
+
+    public RunData(int frames) {
+        this.frames = new TileData[frames][];
+    }
+
+    public class TileData implements Serializable {
+        public int left, top, right, bottom;
+        public boolean isReady;
+        public int level;
+        public float scale;
+
+        public TileData(int left, int top, int right, int bottom,
+                boolean isReady, int level, float scale) {
+            this.left = left;
+            this.right = right;
+            this.top = top;
+            this.bottom = bottom;
+            this.isReady = isReady;
+            this.level = level;
+            this.scale = scale;
+        }
+
+        public String toString() {
+            return "Tile (" + left + "," + top + ")->("
+                    + right + "," + bottom + ")";
+        }
+    }
+
+}
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/TileData.java b/tests/TileBenchmark/src/com/test/tilebenchmark/TileData.java
deleted file mode 100644
index 3e729a6..0000000
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/TileData.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2011 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.test.tilebenchmark;
-
-import java.io.Serializable;
-
-public class TileData implements Serializable {
-    int left, top, right, bottom;
-    public boolean isReady;
-    public int level;
-    public float scale;
-
-    public TileData(int left, int top, int right, int bottom, boolean isReady,
-            int level, float scale) {
-        this.left = left;
-        this.right = right;
-        this.top = top;
-        this.bottom = bottom;
-        this.isReady = isReady;
-        this.level = level;
-        this.scale = scale;
-    }
-
-    public String toString() {
-        return "Tile (" + left + "," + top + ")->("
-                + right + "," + bottom + ")";
-    }
-}
diff --git a/tests/TileBenchmark/tests/Android.mk b/tests/TileBenchmark/tests/Android.mk
new file mode 100644
index 0000000..8b235ec
--- /dev/null
+++ b/tests/TileBenchmark/tests/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := TileBenchmarkTests
+
+LOCAL_INSTRUMENTATION_FOR := TileBenchmark
+
+include $(BUILD_PACKAGE)
diff --git a/tests/TileBenchmark/tests/AndroidManifest.xml b/tests/TileBenchmark/tests/AndroidManifest.xml
new file mode 100644
index 0000000..703b152
--- /dev/null
+++ b/tests/TileBenchmark/tests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.test.tilebenchmark.tests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+                     android:targetPackage="com.test.tilebenchmark"
+                     android:label="Tests for WebView Tiles."/>
+</manifest>
diff --git a/tests/TileBenchmark/tests/src/com/test/tilebenchmark/PerformanceTest.java b/tests/TileBenchmark/tests/src/com/test/tilebenchmark/PerformanceTest.java
new file mode 100644
index 0000000..0f02239
--- /dev/null
+++ b/tests/TileBenchmark/tests/src/com/test/tilebenchmark/PerformanceTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2011 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.test.tilebenchmark;
+
+import com.test.tilebenchmark.ProfileActivity.ProfileCallback;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+public class PerformanceTest extends
+        ActivityInstrumentationTestCase2<ProfileActivity> {
+
+    private class StatAggregator extends PlaybackGraphs {
+        private HashMap<String, Double> mDataMap = new HashMap<String, Double>();
+        private int mCount = 0;
+
+        public void aggregate() {
+            mCount++;
+            Resources resources = mView.getResources();
+            for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) {
+                for (int statIndex = 0; statIndex < Stats.length; statIndex++) {
+                    String metricLabel = resources.getString(
+                            Metrics[metricIndex].getLabelId());
+                    String statLabel = resources.getString(
+                            Stats[statIndex].getLabelId());
+
+                    String label = metricLabel + " " + statLabel;
+                    double aggVal = mDataMap.containsKey(label) ? mDataMap
+                            .get(label) : 0;
+
+                    aggVal += mStats[metricIndex][statIndex];
+                    mDataMap.put(label, aggVal);
+                }
+            }
+            for (Map.Entry<String, Double> e : mSingleStats.entrySet()) {
+                double aggVal = mDataMap.containsKey(e.getKey())
+                        ? mDataMap.get(e.getKey()) : 0;
+                mDataMap.put(e.getKey(), aggVal + e.getValue());
+            }
+        }
+
+        public Bundle getBundle() {
+            Bundle b = new Bundle();
+            int count = 0 == mCount ? Integer.MAX_VALUE : mCount;
+            for (Map.Entry<String, Double> e : mDataMap.entrySet()) {
+                b.putDouble(e.getKey(), e.getValue() / count);
+            }
+            return b;
+        }
+    }
+
+    ProfileActivity mActivity;
+    ProfiledWebView mView;
+    StatAggregator mStats = new StatAggregator();
+
+    private static final String LOGTAG = "PerformanceTest";
+    private static final String TEST_LOCATION = "webkit/page_cycler";
+    private static final String URL_PREFIX = "file://";
+    private static final String URL_POSTFIX = "/index.html?skip=true";
+    private static final int MAX_ITERATIONS = 4;
+    private static final String TEST_DIRS[] = {
+            "alexa_us"//, "android", "dom", "intl1", "intl2", "moz", "moz2"
+    };
+
+    public PerformanceTest() {
+        super(ProfileActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mActivity = getActivity();
+        mView = (ProfiledWebView) mActivity.findViewById(R.id.web);
+    }
+
+    private boolean loadUrl(final String url) {
+        try {
+            Log.d(LOGTAG, "test starting for url " + url);
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mView.loadUrl(url);
+                }
+            });
+            synchronized (mStats) {
+                mStats.wait();
+            }
+            mStats.aggregate();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+            return false;
+        }
+        return true;
+    }
+
+    private boolean runIteration() {
+        File sdFile = Environment.getExternalStorageDirectory();
+        for (String testDirName : TEST_DIRS) {
+            File testDir = new File(sdFile, TEST_LOCATION + "/" + testDirName);
+            Log.d(LOGTAG, "Testing dir: '" + testDir.getAbsolutePath()
+                    + "', exists=" + testDir.exists());
+            for (File siteDir : testDir.listFiles()) {
+                if (!siteDir.isDirectory())
+                    continue;
+
+                if (!loadUrl(URL_PREFIX + siteDir.getAbsolutePath()
+                        + URL_POSTFIX)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    public void testMetrics() {
+        String state = Environment.getExternalStorageState();
+
+        if (!Environment.MEDIA_MOUNTED.equals(state)
+                && !Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
+            Log.d(LOGTAG, "ARG Can't access sd card!");
+            // Can't read the SD card, fail and die!
+            getInstrumentation().sendStatus(1, null);
+            return;
+        }
+
+        // use mGraphs as a condition variable between the UI thread and
+        // this(the testing) thread
+        mActivity.setCallback(new ProfileCallback() {
+            @Override
+            public void profileCallback(RunData data) {
+                Log.d(LOGTAG, "test completion callback");
+                mStats.setData(data);
+                synchronized (mStats) {
+                    mStats.notify();
+                }
+            }
+        });
+
+        for (int i = 0; i < MAX_ITERATIONS; i++)
+            if (!runIteration()) {
+                getInstrumentation().sendStatus(1, null);
+                return;
+            }
+        getInstrumentation().sendStatus(0, mStats.getBundle());
+    }
+}