Merge "Support BTN_TOOL_*TAP as synonyms for BTN_TOOL_FINGER. Bug: 5205301"
diff --git a/Android.mk b/Android.mk
index fdf0933..d4dc088 100644
--- a/Android.mk
+++ b/Android.mk
@@ -183,7 +183,8 @@
 	media/java/android/media/IAudioFocusDispatcher.aidl \
 	media/java/android/media/IMediaScannerListener.aidl \
 	media/java/android/media/IMediaScannerService.aidl \
-	media/java/android/media/IRemoteControlClientDispatcher.aidl \
+	media/java/android/media/IRemoteControlClient.aidl \
+	media/java/android/media/IRemoteControlDisplay.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/CleanSpec.mk b/CleanSpec.mk
index 2593065..3cec66f 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -105,6 +105,7 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/SystemUI_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/R/com/android/systemui/R.java)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IAudioService.P)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IAudioService.P)
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/api/current.txt b/api/current.txt
index fb16830..033cccb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4226,6 +4226,7 @@
     method public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
     method public static synchronized android.bluetooth.BluetoothAdapter getDefaultAdapter();
     method public java.lang.String getName();
+    method public int getProfileConnectionState(int);
     method public boolean getProfileProxy(android.content.Context, android.bluetooth.BluetoothProfile.ServiceListener, int);
     method public android.bluetooth.BluetoothDevice getRemoteDevice(java.lang.String);
     method public int getScanMode();
diff --git a/core/java/android/app/ProgressDialog.java b/core/java/android/app/ProgressDialog.java
index d421173..f1a04f8 100644
--- a/core/java/android/app/ProgressDialog.java
+++ b/core/java/android/app/ProgressDialog.java
@@ -343,7 +343,7 @@
     
     private void onProgressChanged() {
         if (mProgressStyle == STYLE_HORIZONTAL) {
-            if (!mViewUpdateHandler.hasMessages(0)) {
+            if (mViewUpdateHandler != null && !mViewUpdateHandler.hasMessages(0)) {
                 mViewUpdateHandler.sendEmptyMessage(0);
             }
         }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 264db19..2236928 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -777,23 +777,24 @@
      * Get the current connection state of a profile.
      * This function can be used to check whether the local Bluetooth adapter
      * is connected to any remote device for a specific profile.
-     * Profile can be one of {@link BluetoothProfile.HEADSET},
-     * {@link BluetoothProfile.A2DP}.
+     * Profile can be one of {@link BluetoothProfile#HEADSET},
+     * {@link BluetoothProfile#A2DP}.
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
      *
      * <p> Return value can be one of
-     * {@link * BluetoothProfile.STATE_DISCONNECTED},
-     * {@link * BluetoothProfile.STATE_CONNECTING},
-     * {@link * BluetoothProfile.STATE_CONNECTED},
-     * {@link * BluetoothProfile.STATE_DISCONNECTING}
-     * @hide
+     * {@link BluetoothProfile#STATE_DISCONNECTED},
+     * {@link BluetoothProfile#STATE_CONNECTING},
+     * {@link BluetoothProfile#STATE_CONNECTED},
+     * {@link BluetoothProfile#STATE_DISCONNECTING}
      */
     public int getProfileConnectionState(int profile) {
         if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED;
         try {
             return mService.getProfileConnectionState(profile);
-        } catch (RemoteException e) {Log.e(TAG, "getProfileConnectionState:", e);}
+        } catch (RemoteException e) {
+            Log.e(TAG, "getProfileConnectionState:", e);
+        }
         return BluetoothProfile.STATE_DISCONNECTED;
     }
 
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index 79ade260..d78bbbf 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -97,8 +97,18 @@
      * Refreshes the "searchables" list when packages are added/removed.
      */
     class MyPackageMonitor extends PackageMonitor {
+
         @Override
         public void onSomePackagesChanged() {
+            updateSearchables();
+        }
+
+        @Override
+        public void onPackageModified(String pkg) {
+            updateSearchables();
+        }
+
+        private void updateSearchables() {
             // Update list of searchable activities
             getSearchables().buildSearchableList();
             // Inform all listeners that the list of searchables has been updated.
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c7bf8e3..0dc781f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -218,4 +218,9 @@
      * Called by the settings application to temporarily set the pointer speed.
      */
     void setPointerSpeed(int speed);
+
+    /**
+     * Block until all windows the window manager knows about have been drawn.
+     */
+    void waitForAllDrawn();
 }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 8fa1922..7c826ca9 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2628,7 +2628,8 @@
 
                     final int left = cl + (int) region.left;
                     final int top = ct + (int) region.top;
-                    invalidate(left, top, left + (int) region.width(), top + (int) region.height());
+                    invalidate(left, top, left + (int) (region.width() + .5f),
+                            top + (int) (region.height() + .5f));
                 }
             }
         } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index e1aa9a4..fb87e23 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -259,8 +259,9 @@
         mStreamControls = new HashMap<Integer,StreamControl>(STREAM_TYPES.length);
         Resources res = mContext.getResources();
         for (int i = 0; i < STREAM_TYPES.length; i++) {
+            final int streamType = STREAM_TYPES[i];
             StreamControl sc = new StreamControl();
-            sc.streamType = STREAM_TYPES[i];
+            sc.streamType = streamType;
             sc.group = (ViewGroup) inflater.inflate(R.layout.volume_adjust_item, null);
             sc.group.setTag(sc);
             sc.icon = (ImageView) sc.group.findViewById(R.id.stream_icon);
@@ -273,10 +274,12 @@
             sc.iconMuteRes = STREAM_ICONS_MUTED[i];
             sc.icon.setImageResource(sc.iconRes);
             sc.seekbarView = (SeekBar) sc.group.findViewById(R.id.seekbar);
-            sc.seekbarView.setMax(mAudioManager.getStreamMaxVolume(STREAM_TYPES[i]));
+            int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
+                    streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
+            sc.seekbarView.setMax(mAudioManager.getStreamMaxVolume(streamType) + plusOne);
             sc.seekbarView.setOnSeekBarChangeListener(this);
             sc.seekbarView.setTag(sc);
-            mStreamControls.put(STREAM_TYPES[i], sc);
+            mStreamControls.put(streamType, sc);
         }
     }
 
@@ -476,6 +479,9 @@
 
         StreamControl sc = mStreamControls.get(streamType);
         if (sc != null) {
+            if (sc.seekbarView.getMax() != max) {
+                sc.seekbarView.setMax(max);
+            }
             sc.seekbarView.setProgress(index);
         }
 
@@ -557,28 +563,6 @@
         }
     }
 
-//    /**
-//     * Makes the ringer icon visible with an icon that is chosen
-//     * based on the current ringer mode.
-//     */
-//    private void setRingerIcon() {
-//        mSmallStreamIcon.setVisibility(View.GONE);
-//        mLargeStreamIcon.setVisibility(View.VISIBLE);
-//
-//        int ringerMode = mAudioService.getRingerMode();
-//        int icon;
-//
-//        if (LOGD) Log.d(TAG, "setRingerIcon(), ringerMode: " + ringerMode);
-//
-//        if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
-//            icon = com.android.internal.R.drawable.ic_volume_off;
-//        } else if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
-//            icon = com.android.internal.R.drawable.ic_vibrate;
-//        } else {
-//            icon = com.android.internal.R.drawable.ic_volume;
-//        }
-//        mLargeStreamIcon.setImageResource(icon);
-//    }
 
     /**
      * Switch between icons because Bluetooth music is same as music volume, but with
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 6b09049..fdd9b2c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -177,7 +177,8 @@
             @ViewDebug.IntToString(from = TYPE_SYSTEM_ERROR, to = "TYPE_SYSTEM_ERROR"),
             @ViewDebug.IntToString(from = TYPE_INPUT_METHOD, to = "TYPE_INPUT_METHOD"),
             @ViewDebug.IntToString(from = TYPE_INPUT_METHOD_DIALOG, to = "TYPE_INPUT_METHOD_DIALOG"),
-            @ViewDebug.IntToString(from = TYPE_SECURE_SYSTEM_OVERLAY, to = "TYPE_SECURE_SYSTEM_OVERLAY")
+            @ViewDebug.IntToString(from = TYPE_SECURE_SYSTEM_OVERLAY, to = "TYPE_SECURE_SYSTEM_OVERLAY"),
+            @ViewDebug.IntToString(from = TYPE_BOOT_PROGRESS, to = "TYPE_BOOT_PROGRESS")
         })
         public int type;
     
@@ -401,6 +402,13 @@
         public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;
 
         /**
+         * Window type: The boot progress dialog, goes on top of everything
+         * in the world.
+         * @hide
+         */
+        public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;
+
+        /**
          * End of types of system windows.
          */
         public static final int LAST_SYSTEM_WINDOW      = 2999;
diff --git a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
index d3baa2b..aa9fa45 100644
--- a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
+++ b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
@@ -25,6 +25,8 @@
 
 public class TargetDrawable {
     private static final String TAG = "TargetDrawable";
+    private static final boolean DEBUG = false;
+
     public static final int[] STATE_ACTIVE =
             { android.R.attr.state_enabled, android.R.attr.state_active };
     public static final int[] STATE_INACTIVE =
@@ -139,11 +141,13 @@
                 maxWidth = Math.max(maxWidth, childDrawable.getIntrinsicWidth());
                 maxHeight = Math.max(maxHeight, childDrawable.getIntrinsicHeight());
             }
-            Log.v(TAG, "union of childDrawable rects " + d + " to: " + maxWidth + "x" + maxHeight);
+            if (DEBUG) Log.v(TAG, "union of childDrawable rects " + d + " to: "
+                        + maxWidth + "x" + maxHeight);
             d.setBounds(0, 0, maxWidth, maxHeight);
             for (int i = 0; i < d.getStateCount(); i++) {
                 Drawable childDrawable = d.getStateDrawable(i);
-                Log.v(TAG, "sizing drawable " + childDrawable + " to: " + maxWidth + "x" + maxHeight);
+                if (DEBUG) Log.v(TAG, "sizing drawable " + childDrawable + " to: "
+                            + maxWidth + "x" + maxHeight);
                 childDrawable.setBounds(0, 0, maxWidth, maxHeight);
             }
         } else if (mDrawable != null) {
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index b8f2d6f..6b5ca50 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -39,6 +39,8 @@
 #include <binder/IServiceManager.h>
 #include <utils/threads.h>
 
+#include <ScopedUtfChars.h>
+
 #include <android_runtime/AndroidRuntime.h>
 
 //#undef LOGV
@@ -444,6 +446,25 @@
         return result;
     }
 
+    void warnIfStillLive() {
+        JNIEnv* env = javavm_to_jnienv(mVM);
+        if (mObject != NULL) {
+            // Okay, something is wrong -- we have a hard reference to a live death
+            // recipient on the VM side, but the list is being torn down.
+            jclass clazz = env->GetObjectClass(mObject);
+            jmethodID getnameMethod = env->GetMethodID(clazz, "getName", NULL);
+            jstring nameString = (jstring) env->CallObjectMethod(clazz, getnameMethod);
+            if (nameString) {
+                ScopedUtfChars nameUtf(env, nameString);
+                LOGW("BinderProxy is being destroyed but the application did not call "
+                        "unlinkToDeath to unlink all of its death recipients beforehand.  "
+                        "Releasing leaked death recipient: %s", nameUtf.c_str());
+                env->DeleteLocalRef(nameString);
+            }
+            env->DeleteLocalRef(clazz);
+        }
+    }
+
 protected:
     virtual ~JavaDeathRecipient()
     {
@@ -478,7 +499,10 @@
     // to the list are holding references on the list object.  Only when they are torn
     // down can the list header be destroyed.
     if (mList.size() > 0) {
-        LOGE("Retiring DRL %p with extant death recipients\n", this);
+        List< sp<JavaDeathRecipient> >::iterator iter;
+        for (iter = mList.begin(); iter != mList.end(); iter++) {
+            (*iter)->warnIfStillLive();
+        }
     }
 }
 
diff --git a/core/res/res/drawable-hdpi/popup_inline_error_above_holo_dark.9.png b/core/res/res/drawable-hdpi/popup_inline_error_above_holo_dark.9.png
index 61ea2b0..83b2bce 100644
--- a/core/res/res/drawable-hdpi/popup_inline_error_above_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/popup_inline_error_above_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_inline_error_above_holo_light.9.png b/core/res/res/drawable-hdpi/popup_inline_error_above_holo_light.9.png
index 83b2bce..61ea2b0 100644
--- a/core/res/res/drawable-hdpi/popup_inline_error_above_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/popup_inline_error_above_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_16_inner_holo.png b/core/res/res/drawable-hdpi/spinner_16_inner_holo.png
index 01f4278..2af7e81 100644
--- a/core/res/res/drawable-hdpi/spinner_16_inner_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_16_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_20_inner_holo.png b/core/res/res/drawable-hdpi/spinner_20_inner_holo.png
index 4c9849f..74ce1ee 100644
--- a/core/res/res/drawable-hdpi/spinner_20_inner_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_20_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_48_inner_holo.png b/core/res/res/drawable-hdpi/spinner_48_inner_holo.png
index 5d15e74..7220c2f 100644
--- a/core/res/res/drawable-hdpi/spinner_48_inner_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_48_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_76_inner_holo.png b/core/res/res/drawable-hdpi/spinner_76_inner_holo.png
index cc8affe..633df1b 100644
--- a/core/res/res/drawable-hdpi/spinner_76_inner_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_76_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_inline_error_above_holo_dark.9.png b/core/res/res/drawable-mdpi/popup_inline_error_above_holo_dark.9.png
index c03e658..19b153b 100644
--- a/core/res/res/drawable-mdpi/popup_inline_error_above_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/popup_inline_error_above_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_inline_error_above_holo_light.9.png b/core/res/res/drawable-mdpi/popup_inline_error_above_holo_light.9.png
index 19b153b..c03e658 100644
--- a/core/res/res/drawable-mdpi/popup_inline_error_above_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/popup_inline_error_above_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_16_inner_holo.png b/core/res/res/drawable-mdpi/spinner_16_inner_holo.png
index a322a02..4f6f981 100644
--- a/core/res/res/drawable-mdpi/spinner_16_inner_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_16_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_20_inner_holo.png b/core/res/res/drawable-mdpi/spinner_20_inner_holo.png
index 538788a..fab8766 100644
--- a/core/res/res/drawable-mdpi/spinner_20_inner_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_20_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_48_inner_holo.png b/core/res/res/drawable-mdpi/spinner_48_inner_holo.png
index b59dc64..91a29fd 100644
--- a/core/res/res/drawable-mdpi/spinner_48_inner_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_48_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_76_inner_holo.png b/core/res/res/drawable-mdpi/spinner_76_inner_holo.png
index e7d654c..a388434 100644
--- a/core/res/res/drawable-mdpi/spinner_76_inner_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_76_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_dark.9.png b/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_dark.9.png
index d69b772..a210f3c 100644
--- a/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_light.9.png b/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_light.9.png
index a210f3c..d69b772 100644
--- a/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_16_inner_holo.png b/core/res/res/drawable-xhdpi/spinner_16_inner_holo.png
index d49d67a..8f2b8ae 100644
--- a/core/res/res/drawable-xhdpi/spinner_16_inner_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_16_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_20_inner_holo.png b/core/res/res/drawable-xhdpi/spinner_20_inner_holo.png
index 5eec6f3..b202d4e 100644
--- a/core/res/res/drawable-xhdpi/spinner_20_inner_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_20_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_48_inner_holo.png b/core/res/res/drawable-xhdpi/spinner_48_inner_holo.png
index 6f824026..c2df6b4 100644
--- a/core/res/res/drawable-xhdpi/spinner_48_inner_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_48_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_76_inner_holo.png b/core/res/res/drawable-xhdpi/spinner_76_inner_holo.png
index af88495..9f2044d 100644
--- a/core/res/res/drawable-xhdpi/spinner_76_inner_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_76_inner_holo.png
Binary files differ
diff --git a/core/res/res/values-sw600dp/bools.xml b/core/res/res/values-sw600dp/bools.xml
index a453ac1..2097049 100644
--- a/core/res/res/values-sw600dp/bools.xml
+++ b/core/res/res/values-sw600dp/bools.xml
@@ -18,4 +18,5 @@
     <bool name="preferences_prefer_dual_pane">true</bool>
     <bool name="show_ongoing_ime_switcher">false</bool>
     <bool name="action_bar_expanded_action_views_exclusive">false</bool>
+    <bool name="target_honeycomb_needs_options_menu">false</bool>
 </resources>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 6e4db5e..87a98e2 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -20,4 +20,5 @@
     <bool name="preferences_prefer_dual_pane">false</bool>
     <bool name="show_ongoing_ime_switcher">true</bool>
     <bool name="action_bar_expanded_action_views_exclusive">true</bool>
+    <bool name="target_honeycomb_needs_options_menu">true</bool>
 </resources>
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index f9efd3c..e3ef717 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1715,161 +1715,54 @@
         }
     }
 
-    /**
-     * Acts as a proxy between AudioService and the RemoteControlClient
-     */
-    private IRemoteControlClientDispatcher mRcClientDispatcher =
-            new IRemoteControlClientDispatcher.Stub() {
-
-        public String getMetadataStringForClient(String clientName, int field) {
-            RemoteControlClient realClient;
-            synchronized(mRcClientMap) {
-                realClient = mRcClientMap.get(clientName);
-            }
-            if (realClient != null) {
-                return realClient.getMetadataString(field);
-            } else {
-                return null;
-            }
-        }
-
-        public int getPlaybackStateForClient(String clientName) {
-            RemoteControlClient realClient;
-            synchronized(mRcClientMap) {
-                realClient = mRcClientMap.get(clientName);
-            }
-            if (realClient != null) {
-                return realClient.getPlaybackState();
-            } else {
-                return 0;
-            }
-        }
-
-        public int getTransportControlFlagsForClient(String clientName) {
-            RemoteControlClient realClient;
-            synchronized(mRcClientMap) {
-                realClient = mRcClientMap.get(clientName);
-            }
-            if (realClient != null) {
-                return realClient.getTransportControlFlags();
-            } else {
-                return 0;
-            }
-        }
-
-        public Bitmap getAlbumArtForClient(String clientName, int maxWidth, int maxHeight) {
-            RemoteControlClient realClient;
-            synchronized(mRcClientMap) {
-                realClient = mRcClientMap.get(clientName);
-            }
-            if (realClient != null) {
-                return realClient.getAlbumArt(maxWidth, maxHeight);
-            } else {
-                return null;
-            }
-        }
-    };
-
-    private HashMap<String, RemoteControlClient> mRcClientMap =
-            new HashMap<String, RemoteControlClient>();
-
-    private String getIdForRcClient(RemoteControlClient client) {
-        // client is guaranteed to be non-null
-        return client.toString();
-    }
 
     /**
      * @hide
+     * CANDIDATE FOR SDK
      * Registers the remote control client for providing information to display on the remote
      * controls.
-     * @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 remote control client associated with the event receiver, responsible
+     * @param rcClient the remote control client associated responsible
      *      for providing the information to display on the remote control.
      */
-    public void registerRemoteControlClient(ComponentName eventReceiver,
-            RemoteControlClient rcClient) {
-        if ((eventReceiver == null) || (rcClient == null)) {
+    public void registerRemoteControlClient(RemoteControlClient rcClient) {
+        if ((rcClient == null) || (rcClient.getRcEventReceiver() == null)) {
             return;
         }
-        String clientKey = getIdForRcClient(rcClient);
-        synchronized(mRcClientMap) {
-            if (mRcClientMap.containsKey(clientKey)) {
-                return;
-            }
-            mRcClientMap.put(clientKey, rcClient);
-        }
         IAudioService service = getService();
         try {
-            service.registerRemoteControlClient(eventReceiver, mRcClientDispatcher, clientKey,
+            service.registerRemoteControlClient(rcClient.getRcEventReceiver(), /* eventReceiver */
+                    rcClient.getIRemoteControlClient(),                        /* rcClient      */
+                    rcClient.toString(),                                       /* clientName    */
                     // used to match media button event receiver and audio focus
-                    mContext.getPackageName());
+                    mContext.getPackageName());                                /* packageName   */
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in registerRemoteControlClient"+e);
-            synchronized(mRcClientMap) {
-                mRcClientMap.remove(clientKey);
-            }
         }
     }
 
     /**
      * @hide
+     * CANDIDATE FOR SDK
      * Unregisters the remote control client that was providing information to display on the
      * remotes.
-     * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
-     *      that receives the media button intent, and associated with the remote control
-     *      client.
      * @param rcClient the remote control client to unregister
-     * @see #registerRemoteControlClient(ComponentName, RemoteControlClient)
+     * @see #registerRemoteControlClient(RemoteControlClient)
      */
-    public void unregisterRemoteControlClient(ComponentName eventReceiver,
-            RemoteControlClient rcClient) {
-        if ((eventReceiver == null) || (rcClient == null)) {
+    public void unregisterRemoteControlClient(RemoteControlClient rcClient) {
+        if ((rcClient == null) || (rcClient.getRcEventReceiver() == null)) {
             return;
         }
         IAudioService service = getService();
         try {
-            // remove locally
-            boolean unregister = true;
-            synchronized(mRcClientMap) {
-                if (mRcClientMap.remove(getIdForRcClient(rcClient)) == null) {
-                    unregister = false;
-                }
-            }
-            if (unregister) {
-                // unregistering a RemoteControlClient is equivalent to setting it to null
-                service.registerRemoteControlClient(eventReceiver, null, null,
-                        mContext.getPackageName());
-            }
+            service.unregisterRemoteControlClient(rcClient.getRcEventReceiver(), /* eventReceiver */
+                    rcClient.getIRemoteControlClient());                         /* rcClient      */
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in unregisterRemoteControlClient"+e);
         }
     }
 
-    /**
-     * @hide
-     * Returns the current remote control client.
-     * @param rcClientId the generation counter that matches the extra
-     *     {@link AudioManager#EXTRA_REMOTE_CONTROL_CLIENT_GENERATION} in the
-     *     {@link AudioManager#REMOTE_CONTROL_CLIENT_CHANGED} event
-     * @return the current RemoteControlClient from which information to display on the remote
-     *     control can be retrieved, or null if rcClientId doesn't match the current generation
-     *     counter.
-     */
-    public IRemoteControlClientDispatcher getRemoteControlClientDispatcher(int rcClientId) {
-        IAudioService service = getService();
-        try {
-            return service.getRemoteControlClientDispatcher(rcClientId);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in getRemoteControlClient "+e);
-            return null;
-        }
-    }
 
+    // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
     /**
      * @hide
      * Broadcast intent action indicating that the displays on the remote controls
@@ -1882,6 +1775,7 @@
     public static final String REMOTE_CONTROL_CLIENT_CHANGED =
             "android.media.REMOTE_CONTROL_CLIENT_CHANGED";
 
+    // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
     /**
      * @hide
      * The IRemoteControlClientDispatcher monotonically increasing generation counter.
@@ -1891,6 +1785,7 @@
     public static final String EXTRA_REMOTE_CONTROL_CLIENT_GENERATION =
             "android.media.EXTRA_REMOTE_CONTROL_CLIENT_GENERATION";
 
+    // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
     /**
      * @hide
      * The name of the RemoteControlClient.
@@ -1902,6 +1797,7 @@
     public static final String EXTRA_REMOTE_CONTROL_CLIENT_NAME =
             "android.media.EXTRA_REMOTE_CONTROL_CLIENT_NAME";
 
+    // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
     /**
      * @hide
      * The media button event receiver associated with the RemoteControlClient.
@@ -1913,6 +1809,7 @@
     public static final String EXTRA_REMOTE_CONTROL_EVENT_RECEIVER =
             "android.media.EXTRA_REMOTE_CONTROL_EVENT_RECEIVER";
 
+    // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
     /**
      * @hide
      * The flags describing what information has changed in the current remote control client.
@@ -1923,33 +1820,6 @@
             "android.media.EXTRA_REMOTE_CONTROL_CLIENT_INFO_CHANGED";
 
     /**
-     * @hide
-     * Notifies the users of the associated remote control client that the information to display
-     * has changed.
-     @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 infoFlag the type of information that has changed since this method was last called,
-     *      or the event receiver was registered. Use one or multiple of the following flags to
-     *      describe what changed:
-     *      {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_METADATA},
-     *      {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_KEY_MEDIA},
-     *      {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_PLAYSTATE},
-     *      {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_ALBUM_ART}.
-     */
-    public void notifyRemoteControlInformationChanged(ComponentName eventReceiver, int infoFlag) {
-        IAudioService service = getService();
-        try {
-            service.notifyRemoteControlInformationChanged(eventReceiver, infoFlag);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in refreshRemoteControlDisplay"+e);
-        }
-    }
-
-    /**
      *  @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 46f45a0..acc2b23 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -34,7 +34,6 @@
 import android.database.ContentObserver;
 import android.media.MediaPlayer.OnCompletionListener;
 import android.media.MediaPlayer.OnErrorListener;
-import android.media.IRemoteControlClientDispatcher;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
@@ -2167,45 +2166,12 @@
                     break;
 
                 case MSG_RCDISPLAY_CLEAR:
-                    // TODO remove log before release
-                    Log.i(TAG, "Clear remote control display");
-                    Intent clearIntent = new Intent(AudioManager.REMOTE_CONTROL_CLIENT_CHANGED);
-                    // no extra means no IRemoteControlClientDispatcher, which is a request to clear
-                    clearIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-                    mContext.sendBroadcast(clearIntent);
+                    onRcDisplayClear();
                     break;
 
                 case MSG_RCDISPLAY_UPDATE:
-                    synchronized(mCurrentRcLock) {
-                        // msg.obj is guaranteed to be non null
-                        RemoteControlStackEntry rcse = (RemoteControlStackEntry)msg.obj;
-                        if ((mCurrentRcClient == null) ||
-                                (!mCurrentRcClient.equals(rcse.mRcClient))) {
-                            // 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++;
-                            // TODO remove log before release
-                            Log.i(TAG, "Display/update remote control ");
-                            Intent rcClientIntent = new Intent(
-                                    AudioManager.REMOTE_CONTROL_CLIENT_CHANGED);
-                            rcClientIntent.putExtra(
-                                    AudioManager.EXTRA_REMOTE_CONTROL_CLIENT_GENERATION,
-                                    mCurrentRcClientGen);
-                            rcClientIntent.putExtra(
-                                    AudioManager.EXTRA_REMOTE_CONTROL_CLIENT_INFO_CHANGED,
-                                    msg.arg1);
-                            rcClientIntent.putExtra(
-                                    AudioManager.EXTRA_REMOTE_CONTROL_EVENT_RECEIVER,
-                                    rcse.mReceiverComponent.flattenToString());
-                            rcClientIntent.putExtra(
-                                    AudioManager.EXTRA_REMOTE_CONTROL_CLIENT_NAME,
-                                    rcse.mRcClientName);
-                            rcClientIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-                            mContext.sendBroadcast(rcClientIntent);
-                        }
-                    }
+                    // msg.obj is guaranteed to be non null
+                    onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
                     break;
 
                 case MSG_BT_HEADSET_CNCT_FAILED:
@@ -2893,18 +2859,18 @@
 
     private final Object mCurrentRcLock = new Object();
     /**
-     * The one remote control client to be polled for display information.
+     * The one remote control client which will receive a request for display information.
      * This object may be null.
      * Access protected by mCurrentRcLock.
      */
-    private IRemoteControlClientDispatcher mCurrentRcClient = null;
+    private IRemoteControlClient mCurrentRcClient = null;
 
     private final static int RC_INFO_NONE = 0;
     private final static int RC_INFO_ALL =
-        RemoteControlClient.FLAG_INFORMATION_CHANGED_ALBUM_ART |
-        RemoteControlClient.FLAG_INFORMATION_CHANGED_KEY_MEDIA |
-        RemoteControlClient.FLAG_INFORMATION_CHANGED_METADATA |
-        RemoteControlClient.FLAG_INFORMATION_CHANGED_PLAYSTATE;
+        RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
+        RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
+        RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
+        RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
 
     /**
      * A monotonically increasing generation counter for mCurrentRcClient.
@@ -2914,25 +2880,6 @@
     private int mCurrentRcClientGen = 0;
 
     /**
-     * Returns the current remote control client.
-     * @param rcClientId the counter value that matches the extra
-     *     {@link AudioManager#EXTRA_REMOTE_CONTROL_CLIENT_GENERATION} in the
-     *     {@link AudioManager#REMOTE_CONTROL_CLIENT_CHANGED} event
-     * @return the current IRemoteControlClientDispatcher from which information to display on the
-     *     remote control can be retrieved, or null if rcClientId doesn't match the current
-     *     generation counter.
-     */
-    public IRemoteControlClientDispatcher getRemoteControlClientDispatcher(int rcClientId) {
-        synchronized(mCurrentRcLock) {
-            if (rcClientId == mCurrentRcClientGen) {
-                return mCurrentRcClient;
-            } else {
-                return null;
-            }
-        }
-    }
-
-    /**
      * Inner class to monitor remote control client deaths, and remove the client for the
      * remote control stack if necessary.
      */
@@ -2965,7 +2912,7 @@
         public int mCallingUid;
 
         /** provides access to the information to display on the remote control */
-        public IRemoteControlClientDispatcher mRcClient;
+        public IRemoteControlClient mRcClient;
         public RcClientDeathHandler mRcClientDeathHandler;
 
         public RemoteControlStackEntry(ComponentName r) {
@@ -3119,6 +3066,103 @@
         return false;
     }
 
+    //==========================================================================================
+    // Remote control display / client
+    //==========================================================================================
+    /**
+     * Update the remote control displays with the new "focused" client generation
+     */
+    private void setNewRcClientGenerationOnDisplays_syncRcStack(int newClientGeneration) {
+        // NOTE: Only one IRemoteControlDisplay supported in this implementation
+        if (mRcDisplay != null) {
+            try {
+                mRcDisplay.setCurrentClientGenerationId(newClientGeneration);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead display in onRcDisplayUpdate() "+e);
+                // if we had a display before, stop monitoring its death
+                rcDisplay_stopDeathMonitor_syncRcStack();
+                mRcDisplay = null;
+            }
+        }
+    }
+
+    /**
+     * Update the remote control clients with the new "focused" client generation
+     */
+    private void setNewRcClientGenerationOnClients_syncRcStack(int newClientGeneration) {
+        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+        while(stackIterator.hasNext()) {
+            RemoteControlStackEntry se = stackIterator.next();
+            if ((se != null) && (se.mRcClient != null)) {
+                try {
+                    se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Dead client in onRcDisplayUpdate()"+e);
+                    stackIterator.remove();
+                    se.unlinkToRcClientDeath();
+                }
+            }
+        }
+    }
+
+    /**
+     * Update the displays and clients with the new "focused" client generation
+     */
+    private void setNewRcClientGeneration(int newClientGeneration) {
+        synchronized(mRCStack) {
+            // send the new valid client generation ID to all displays
+            setNewRcClientGenerationOnDisplays_syncRcStack(newClientGeneration);
+            // send the new valid client generation ID to all clients
+            setNewRcClientGenerationOnClients_syncRcStack(newClientGeneration);
+        }
+    }
+
+    /**
+     * Called when processing MSG_RCDISPLAY_CLEAR event
+     */
+    private void onRcDisplayClear() {
+        // TODO remove log before release
+        Log.i(TAG, "Clear remote control display");
+
+        synchronized(mCurrentRcLock) {
+            mCurrentRcClientGen++;
+
+            // synchronously update the displays and clients with the new client generation
+            setNewRcClientGeneration(mCurrentRcClientGen);
+        }
+    }
+
+    /**
+     * Called when processing MSG_RCDISPLAY_UPDATE event
+     */
+    private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
+        synchronized(mCurrentRcLock) {
+            if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
+                // TODO remove log before release
+                Log.i(TAG, "Display/update remote control ");
+
+                mCurrentRcClientGen++;
+
+                // synchronously update the displays and clients with the new client generation
+                setNewRcClientGeneration(mCurrentRcClientGen);
+
+                // ask the current client that it needs to send info
+                try {
+                    mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
+                            flags, mArtworkExpectedWidth, mArtworkExpectedHeight);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Current valid remote client is dead: "+e);
+                    mCurrentRcClient = null;
+                }
+            } else {
+                // 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)
+            }
+        }
+    }
+
+
     /**
      * Helper function:
      * Called synchronized on mRCStack
@@ -3127,6 +3171,7 @@
         synchronized(mCurrentRcLock) {
             mCurrentRcClient = null;
         }
+        // will cause onRcDisplayClear() to be called in AudioService's handler thread
         mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
     }
 
@@ -3152,6 +3197,7 @@
             }
             mCurrentRcClient = rcse.mRcClient;
         }
+        // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
         mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
                 infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
     }
@@ -3220,7 +3266,7 @@
 
     /** see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...) */
     public void registerRemoteControlClient(ComponentName eventReceiver,
-            IRemoteControlClientDispatcher rcClient, String clientName, String callingPackageName) {
+            IRemoteControlClient rcClient, String clientName, String callingPackageName) {
         synchronized(mAudioFocusLock) {
             synchronized(mRCStack) {
                 // store the new display information
@@ -3235,6 +3281,14 @@
                         }
                         // save the new remote control client
                         rcse.mRcClient = rcClient;
+                        if (mRcDisplay != null) {
+                            try {
+                                rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
+                            } catch (RemoteException e) {
+                                Log.e(TAG, "Error connecting remote control display to client: "+e);
+                                e.printStackTrace();
+                            }
+                        }
                         rcse.mCallingPackageName = callingPackageName;
                         rcse.mRcClientName = clientName;
                         rcse.mCallingUid = Binder.getCallingUid();
@@ -3266,18 +3320,121 @@
         }
     }
 
-    /** see AudioManager.notifyRemoteControlInformationChanged(ComponentName er, int infoFlag) */
-    public void notifyRemoteControlInformationChanged(ComponentName eventReceiver, int infoFlag) {
-        synchronized(mAudioFocusLock) {
+    /** see AudioManager.unregisterRemoteControlClient(ComponentName eventReceiver, ...) */
+    public void unregisterRemoteControlClient(ComponentName eventReceiver,
+            IRemoteControlClient rcClient) {
+        //FIXME implement
+    }
+
+    /**
+     * The remote control displays.
+     * Access synchronized on mRCStack
+     * NOTE: Only one IRemoteControlDisplay supported in this implementation
+     */
+    private IRemoteControlDisplay mRcDisplay;
+    private RcDisplayDeathHandler mRcDisplayDeathHandler;
+    private int mArtworkExpectedWidth = -1;
+    private int mArtworkExpectedHeight = -1;
+    /**
+     * Inner class to monitor remote control display deaths, and unregister them from the list
+     * of displays if necessary.
+     */
+    private class RcDisplayDeathHandler implements IBinder.DeathRecipient {
+        public void binderDied() {
             synchronized(mRCStack) {
-                // only refresh if the eventReceiver is at the top of the stack
-                if (isCurrentRcController(eventReceiver)) {
-                    checkUpdateRemoteControlDisplay(infoFlag);
+                Log.w(TAG, "  RemoteControl: display died");
+                mRcDisplay = null;
+            }
+        }
+
+    }
+
+    private void rcDisplay_stopDeathMonitor_syncRcStack() {
+        if (mRcDisplay != null) {
+            // we had a display before, stop monitoring its death
+            IBinder b = mRcDisplay.asBinder();
+            try {
+                b.unlinkToDeath(mRcDisplayDeathHandler, 0);
+            } catch (java.util.NoSuchElementException e) {
+             // being conservative here
+                Log.e(TAG, "Error while trying to unlink display death handler " + e);
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private void rcDisplay_startDeathMonitor_syncRcStack() {
+        if (mRcDisplay != null) {
+            // new non-null display, monitor its death
+            IBinder b = mRcDisplay.asBinder();
+            mRcDisplayDeathHandler = new RcDisplayDeathHandler();
+            try {
+                b.linkToDeath(mRcDisplayDeathHandler, 0);
+            } catch (RemoteException e) {
+                // remote control display is DOA, disqualify it
+                Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b);
+                mRcDisplay = null;
+            }
+        }
+    }
+
+    public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
+        synchronized(mRCStack) {
+            if (mRcDisplay == rcd) {
+                return;
+            }
+            // if we had a display before, stop monitoring its death
+            rcDisplay_stopDeathMonitor_syncRcStack();
+            mRcDisplay = rcd;
+            // new display, start monitoring its death
+            rcDisplay_startDeathMonitor_syncRcStack();
+
+            // let all the remote control clients there is a new display
+            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+            while(stackIterator.hasNext()) {
+                RemoteControlStackEntry rcse = stackIterator.next();
+                if(rcse.mRcClient != null) {
+                    try {
+                        rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error connecting remote control display to client: " + e);
+                        e.printStackTrace();
+                    }
                 }
             }
         }
     }
 
+    public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
+        synchronized(mRCStack) {
+            // if we had a display before, stop monitoring its death
+            rcDisplay_stopDeathMonitor_syncRcStack();
+            mRcDisplay = null;
+
+            // disconnect this remote control display from all the clients
+            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+            while(stackIterator.hasNext()) {
+                RemoteControlStackEntry rcse = stackIterator.next();
+                if(rcse.mRcClient != null) {
+                    try {
+                        rcse.mRcClient.unplugRemoteControlDisplay(rcd);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error disconnecting remote control display to client: " + e);
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+    }
+
+    public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
+        synchronized(mRCStack) {
+            // NOTE: Only one IRemoteControlDisplay supported in this implementation
+            mArtworkExpectedWidth = w;
+            mArtworkExpectedHeight = h;
+        }
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         // TODO probably a lot more to do here than just the audio focus and remote control stacks
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7f9ced9..7bf9814 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -18,7 +18,8 @@
 
 import android.content.ComponentName;
 import android.media.IAudioFocusDispatcher;
-import android.media.IRemoteControlClientDispatcher;
+import android.media.IRemoteControlClient;
+import android.media.IRemoteControlDisplay;
 
 /**
  * {@hide}
@@ -88,13 +89,14 @@
 
     void unregisterMediaButtonEventReceiver(in ComponentName eventReceiver);
 
-    void registerRemoteControlClient(in ComponentName eventReceiver,
-           in IRemoteControlClientDispatcher rcClient, in String clientName,
-           in String callingPackageName);
+    oneway void registerRemoteControlClient(in ComponentName eventReceiver,
+           in IRemoteControlClient rcClient, in String clientName, in String callingPackageName);
+    oneway void unregisterRemoteControlClient(in ComponentName eventReceiver,
+           in IRemoteControlClient rcClient);
 
-    IRemoteControlClientDispatcher getRemoteControlClientDispatcher(in int rcClientId);
-
-    void notifyRemoteControlInformationChanged(in ComponentName eventReceiver, int infoFlag);
+    oneway void   registerRemoteControlDisplay(in IRemoteControlDisplay rcd);
+    oneway void unregisterRemoteControlDisplay(in IRemoteControlDisplay rcd);
+    oneway void remoteControlDisplayUsesBitmapSize(in IRemoteControlDisplay rcd, int w, int h);
 
     void startBluetoothSco(IBinder cb);
 
diff --git a/media/java/android/media/IRemoteControlClient.aidl b/media/java/android/media/IRemoteControlClient.aidl
new file mode 100644
index 0000000..0fbba20
--- /dev/null
+++ b/media/java/android/media/IRemoteControlClient.aidl
@@ -0,0 +1,51 @@
+/* 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;
+import android.media.IRemoteControlDisplay;
+
+/**
+ * @hide
+ * Interface registered by AudioManager to notify a source of remote control information
+ * that information is requested to be displayed on the remote control (through
+ * IRemoteControlDisplay).
+ * {@see AudioManager#registerRemoteControlClient(RemoteControlClient)}.
+ */
+oneway interface IRemoteControlClient
+{
+    /**
+     * Notifies a remote control client that information for the given generation ID is
+     * requested. If the flags contains
+     * {@link RemoteControlClient#FLAG_INFORMATION_REQUESTED_ALBUM_ART} then the width and height
+     *   parameters are valid.
+     * @param generationId
+     * @param infoFlags
+     * @param artWidth if > 0, artHeight must be > 0 too.
+     * @param artHeight
+     * FIXME: is infoFlags required? since the RCC pushes info, this might always be called
+     *        with RC_INFO_ALL
+     */
+    void onInformationRequested(int generationId, int infoFlags, int artWidth, int artHeight);
+
+    /**
+     * Sets the generation counter of the current client that is displayed on the remote control.
+     */
+    void setCurrentClientGenerationId(int clientGeneration);
+
+    void   plugRemoteControlDisplay(IRemoteControlDisplay rcd);
+    void unplugRemoteControlDisplay(IRemoteControlDisplay rcd);
+}
\ No newline at end of file
diff --git a/media/java/android/media/IRemoteControlClientDispatcher.aidl b/media/java/android/media/IRemoteControlClientDispatcher.aidl
deleted file mode 100644
index 98142cc..0000000
--- a/media/java/android/media/IRemoteControlClientDispatcher.aidl
+++ /dev/null
@@ -1,92 +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 android.media;
-
-import android.graphics.Bitmap;
-
-/**
- * @hide
- * Interface registered by AudioManager to dispatch remote control information requests
- * to the RemoteControlClient implementation. This is used by AudioService.
- * {@see AudioManager#registerRemoteControlClient(ComponentName, RemoteControlClient)}.
- */
-interface IRemoteControlClientDispatcher
-{
-    /**
-     * 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 requested field is not supported, or the String matching the
-     *       metadata field.
-     */
-    String getMetadataStringForClient(String clientName, int field);
-
-    /**
-     * Called by a remote control to retrieve the current playback state.
-     * @return one of the following values:
-     *       {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_STOPPED},
-     *       {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_PAUSED},
-     *       {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_PLAYING},
-     *       {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_FAST_FORWARDING},
-     *       {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_REWINDING},
-     *       {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_SKIPPING_FORWARDS},
-     *       {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_SKIPPING_BACKWARDS},
-     *       {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_BUFFERING},
-     *       {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_ERROR}.
-     */
-    int getPlaybackStateForClient(String clientName);
-
-    /**
-     * Called by a remote control to retrieve the flags for the media transport control buttons
-     * that this client supports.
-     * @see {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_PREVIOUS},
-     *      {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_REWIND},
-     *      {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_PLAY},
-     *      {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_PLAY_PAUSE},
-     *      {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_PAUSE},
-     *      {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_STOP},
-     *      {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_FAST_FORWARD},
-     *      {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_NEXT}
-     */
-    int getTransportControlFlagsForClient(String clientName);
-
-    /**
-     * Called by a remote control to retrieve the album art picture at the requested size.
-     * Note that returning a bitmap smaller than the maximum requested dimension is accepted
-     * and it will be scaled as needed, but exceeding the maximum dimensions may produce
-     * unspecified results, such as the image being cropped or simply not being displayed.
-     * @param maxWidth the maximum width of the requested bitmap expressed in pixels.
-     * @param maxHeight the maximum height of the requested bitmap expressed in pixels.
-     * @return the bitmap for the album art, or null if there isn't any.
-     * @see android.graphics.Bitmap
-     */
-    Bitmap getAlbumArtForClient(String clientName, int maxWidth, int maxHeight);
-}
diff --git a/media/java/android/media/IRemoteControlDisplay.aidl b/media/java/android/media/IRemoteControlDisplay.aidl
new file mode 100644
index 0000000..19ea202
--- /dev/null
+++ b/media/java/android/media/IRemoteControlDisplay.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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;
+import android.os.Bundle;
+
+/**
+ * @hide
+ * Interface registered through AudioManager of an object that displays information
+ * received from a remote control client.
+ * {@see AudioManager#registerRemoteControlDisplay(IRemoteControlDisplay)}.
+ */
+oneway interface IRemoteControlDisplay
+{
+    /**
+     * Sets the generation counter of the current client that is displayed on the remote control.
+     */
+    void setCurrentClientGenerationId(int clientGeneration);
+
+    void setPlaybackState(int generationId, int state);
+
+    void setMetadata(int generationId, in Bundle metadata);
+
+    void setTransportControlFlags(int generationId, int transportControlFlags);
+
+    void setArtwork(int generationId, in Bitmap artwork);
+}
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index c384636..bfe08b9 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -17,69 +17,84 @@
 package android.media;
 
 import android.content.ComponentName;
+import android.content.SharedPreferences.Editor;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
 
 /**
  * @hide
- * Interface for an object that exposes information meant to be consumed by remote controls
+ * CANDIDATE FOR SDK
+ * RemoteControlClient enables exposing information meant to be consumed by remote controls
  * capable of displaying metadata, album art and media transport control buttons.
- * Such a remote control client object is associated with a media button event receiver
+ * A remote control client object is associated with a media button event receiver
  * when registered through
- * {@link AudioManager#registerRemoteControlClient(ComponentName, RemoteControlClient)}.
+ * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.
  */
-public interface RemoteControlClient
+public class RemoteControlClient
 {
+    private final static String TAG = "RemoteControlClient";
+
     /**
      * Playback state of a RemoteControlClient which is stopped.
      *
-     * @see android.media.RemoteControlClient#getPlaybackState()
+     * @see #setPlaybackState(int)
      */
     public final static int PLAYSTATE_STOPPED            = 1;
     /**
      * Playback state of a RemoteControlClient which is paused.
      *
-     * @see android.media.RemoteControlClient#getPlaybackState()
+     * @see #setPlaybackState(int)
      */
     public final static int PLAYSTATE_PAUSED             = 2;
     /**
      * Playback state of a RemoteControlClient which is playing media.
      *
-     * @see android.media.RemoteControlClient#getPlaybackState()
+     * @see #setPlaybackState(int)
      */
     public final static int PLAYSTATE_PLAYING            = 3;
     /**
      * Playback state of a RemoteControlClient which is fast forwarding in the media
      *    it is currently playing.
      *
-     * @see android.media.RemoteControlClient#getPlaybackState()
+     * @see #setPlaybackState(int)
      */
     public final static int PLAYSTATE_FAST_FORWARDING    = 4;
     /**
      * Playback state of a RemoteControlClient which is fast rewinding in the media
      *    it is currently playing.
      *
-     * @see android.media.RemoteControlClient#getPlaybackState()
+     * @see #setPlaybackState(int)
      */
     public final static int PLAYSTATE_REWINDING          = 5;
     /**
      * Playback state of a RemoteControlClient which is skipping to the next
      *    logical chapter (such as a song in a playlist) in the media it is currently playing.
      *
-     * @see android.media.RemoteControlClient#getPlaybackState()
+     * @see #setPlaybackState(int)
      */
     public final static int PLAYSTATE_SKIPPING_FORWARDS  = 6;
     /**
      * Playback state of a RemoteControlClient which is skipping back to the previous
      *    logical chapter (such as a song in a playlist) in the media it is currently playing.
      *
-     * @see android.media.RemoteControlClient#getPlaybackState()
+     * @see #setPlaybackState(int)
      */
     public final static int PLAYSTATE_SKIPPING_BACKWARDS = 7;
     /**
      * Playback state of a RemoteControlClient which is buffering data to play before it can
      *    start or resume playback.
      *
-     * @see android.media.RemoteControlClient#getPlaybackState()
+     * @see #setPlaybackState(int)
      */
     public final static int PLAYSTATE_BUFFERING          = 8;
     /**
@@ -88,98 +103,188 @@
      *    connectivity when attempting to stream data from a server, or expired user credentials
      *    when trying to play subscription-based content.
      *
-     * @see android.media.RemoteControlClient#getPlaybackState()
+     * @see #setPlaybackState(int)
      */
     public final static int PLAYSTATE_ERROR              = 9;
+    /**
+     * @hide
+     * The value of a playback state when none has been declared
+     */
+    public final static int PLAYSTATE_NONE               = 0;
 
     /**
      * Flag indicating a RemoteControlClient makes use of the "previous" media key.
      *
-     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see #setTransportControlFlags(int)
      * @see android.view.KeyEvent#KEYCODE_MEDIA_PREVIOUS
      */
     public final static int FLAG_KEY_MEDIA_PREVIOUS = 1 << 0;
     /**
      * Flag indicating a RemoteControlClient makes use of the "rewing" media key.
      *
-     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see #setTransportControlFlags(int)
      * @see android.view.KeyEvent#KEYCODE_MEDIA_REWIND
      */
     public final static int FLAG_KEY_MEDIA_REWIND = 1 << 1;
     /**
      * Flag indicating a RemoteControlClient makes use of the "play" media key.
      *
-     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see #setTransportControlFlags(int)
      * @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY
      */
     public final static int FLAG_KEY_MEDIA_PLAY = 1 << 2;
     /**
      * Flag indicating a RemoteControlClient makes use of the "play/pause" media key.
      *
-     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see #setTransportControlFlags(int)
      * @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE
      */
     public final static int FLAG_KEY_MEDIA_PLAY_PAUSE = 1 << 3;
     /**
      * Flag indicating a RemoteControlClient makes use of the "pause" media key.
      *
-     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see #setTransportControlFlags(int)
      * @see android.view.KeyEvent#KEYCODE_MEDIA_PAUSE
      */
     public final static int FLAG_KEY_MEDIA_PAUSE = 1 << 4;
     /**
      * Flag indicating a RemoteControlClient makes use of the "stop" media key.
      *
-     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see #setTransportControlFlags(int)
      * @see android.view.KeyEvent#KEYCODE_MEDIA_STOP
      */
     public final static int FLAG_KEY_MEDIA_STOP = 1 << 5;
     /**
      * Flag indicating a RemoteControlClient makes use of the "fast forward" media key.
      *
-     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see #setTransportControlFlags(int)
      * @see android.view.KeyEvent#KEYCODE_MEDIA_FAST_FORWARD
      */
     public final static int FLAG_KEY_MEDIA_FAST_FORWARD = 1 << 6;
     /**
      * Flag indicating a RemoteControlClient makes use of the "next" media key.
      *
-     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see #setTransportControlFlags(int)
      * @see android.view.KeyEvent#KEYCODE_MEDIA_NEXT
      */
     public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7;
 
     /**
-     * Flag used to signal that the metadata exposed by the RemoteControlClient has changed.
-     *
-     * @see #notifyRemoteControlInformationChanged(ComponentName, int)
+     * @hide
+     * The flags for when no media keys are declared supported
      */
-    public final static int FLAG_INFORMATION_CHANGED_METADATA = 1 << 0;
+    public final static int FLAGS_KEY_MEDIA_NONE = 0;
+
     /**
+     * @hide
+     * Flag used to signal some type of metadata exposed by the RemoteControlClient is requested.
+     */
+    public final static int FLAG_INFORMATION_REQUEST_METADATA = 1 << 0;
+    /**
+     * @hide
+     * FIXME doc not valid
      * Flag used to signal that the transport control buttons supported by the
      * RemoteControlClient have changed.
      * This can for instance happen when playback is at the end of a playlist, and the "next"
      * operation is not supported anymore.
-     *
-     * @see #notifyRemoteControlInformationChanged(ComponentName, int)
      */
-    public final static int FLAG_INFORMATION_CHANGED_KEY_MEDIA = 1 << 1;
+    public final static int FLAG_INFORMATION_REQUEST_KEY_MEDIA = 1 << 1;
     /**
+     * @hide
+     * FIXME doc not valid
      * Flag used to signal that the playback state of the RemoteControlClient has changed.
-     *
-     * @see #notifyRemoteControlInformationChanged(ComponentName, int)
      */
-    public final static int FLAG_INFORMATION_CHANGED_PLAYSTATE = 1 << 2;
+    public final static int FLAG_INFORMATION_REQUEST_PLAYSTATE = 1 << 2;
     /**
+     * @hide
+     * FIXME doc not valid
      * Flag used to signal that the album art for the RemoteControlClient has changed.
-     *
-     * @see #notifyRemoteControlInformationChanged(ComponentName, int)
      */
-    public final static int FLAG_INFORMATION_CHANGED_ALBUM_ART = 1 << 3;
+    public final static int FLAG_INFORMATION_REQUEST_ALBUM_ART = 1 << 3;
 
     /**
-     * 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
+     * Class constructor.
+     * @param mediaButtonEventReceiver the receiver for the media button events.
+     * @see AudioManager#registerMediaButtonEventReceiver(ComponentName)
+     * @see AudioManager#registerRemoteControlClient(RemoteControlClient)
+     */
+    public RemoteControlClient(ComponentName mediaButtonEventReceiver) {
+        mRcEventReceiver = mediaButtonEventReceiver;
+
+        Looper looper;
+        if ((looper = Looper.myLooper()) != null) {
+            mEventHandler = new EventHandler(this, looper);
+        } else if ((looper = Looper.getMainLooper()) != null) {
+            mEventHandler = new EventHandler(this, looper);
+        } else {
+            mEventHandler = null;
+            Log.e(TAG, "RemoteControlClient() couldn't find main application thread");
+        }
+    }
+
+    /**
+     * Class constructor for a remote control client whose internal event handling
+     * happens on a user-provided Looper.
+     * @param mediaButtonEventReceiver the receiver for the media button events.
+     * @param looper the Looper running the event loop.
+     * @see AudioManager#registerMediaButtonEventReceiver(ComponentName)
+     * @see AudioManager#registerRemoteControlClient(RemoteControlClient)
+     */
+    public RemoteControlClient(ComponentName mediaButtonEventReceiver, Looper looper) {
+        mRcEventReceiver = mediaButtonEventReceiver;
+
+        mEventHandler = new EventHandler(this, looper);
+    }
+
+    /**
+     * Class used to modify metadata in a {@link RemoteControlClient} object.
+     */
+    public class MetadataEditor {
+
+        private MetadataEditor() { /* only use factory */ }
+
+        public MetadataEditor putString(int key, String value) {
+            return this;
+        }
+
+        public MetadataEditor putBitmap(int key, Bitmap bitmap) {
+            return this;
+        }
+
+        public void clear() {
+
+        }
+
+        public void apply() {
+
+        }
+    }
+
+    public MetadataEditor editMetadata(boolean startEmpty) {
+        return (new MetadataEditor());
+    }
+
+
+    /**
+     * @hide
+     * FIXME migrate this functionality under MetadataEditor
+     * Start collecting information to be displayed.
+     * Use {@link #commitMetadata()} to signal the end of the collection which has been created
+     *  through one or multiple calls to {@link #addMetadataString(int, int, String)}.
+     */
+    public void startMetadata() {
+        synchronized(mCacheLock) {
+            mMetadata.clear();
+        }
+    }
+
+    /**
+     * @hide
+     * FIXME migrate this functionality under MetadataEditor
+     * Adds textual information to be displayed.
+     * Note that none of the information added before {@link #startMetadata()},
+     * and after {@link #commitMetadata()} has been called, will be displayed.
+     * @param key the identifier of a the metadata field to set. Valid values are
      *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM},
      *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST},
      *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
@@ -195,14 +300,54 @@
      *      {@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 requested field is not supported, or the String matching the
-     *       metadata field.
+     * @param value the String for the field value, or null to signify there is no valid
+     *      information for the field.
      */
-    String getMetadataString(int field);
+    public void addMetadataString(int key, String value) {
+        synchronized(mCacheLock) {
+            // store locally
+            mMetadata.putString(String.valueOf(key), value);
+        }
+    }
 
     /**
-     * Called by a remote control to retrieve the current playback state.
-     * @return one of the following values:
+     * @hide
+     * FIXME migrate this functionality under MetadataEditor
+     * Marks all the metadata previously set with {@link #addMetadataString(int, int, String)} as
+     * eligible to be displayed.
+     */
+    public void commitMetadata() {
+        synchronized(mCacheLock) {
+            // send to remote control display if conditions are met
+            sendMetadata_syncCacheLock();
+        }
+    }
+
+    /**
+     * @hide
+     * FIXME migrate this functionality under MetadataEditor
+     * Sets the album / artwork picture to be displayed on the remote control.
+     * @param artwork the bitmap for the artwork, or null if there isn't any.
+     * @see android.graphics.Bitmap
+     */
+    public void setArtwork(Bitmap artwork) {
+        synchronized(mCacheLock) {
+            // resize and store locally
+            if (mArtworkExpectedWidth > 0) {
+                mArtwork = scaleBitmapIfTooBig(artwork,
+                        mArtworkExpectedWidth, mArtworkExpectedHeight);
+            } else {
+                // no valid resize dimensions, store as is
+                mArtwork = artwork;
+            }
+            // send to remote control display if conditions are met
+            sendArtwork_syncCacheLock();
+        }
+    }
+
+    /**
+     * Sets the current playback state.
+     * @param state the current playback state, one of the following values:
      *       {@link #PLAYSTATE_STOPPED},
      *       {@link #PLAYSTATE_PAUSED},
      *       {@link #PLAYSTATE_PLAYING},
@@ -213,12 +358,20 @@
      *       {@link #PLAYSTATE_BUFFERING},
      *       {@link #PLAYSTATE_ERROR}.
      */
-    int getPlaybackState();
+    public void setPlaybackState(int state) {
+        synchronized(mCacheLock) {
+            // store locally
+            mPlaybackState = state;
+
+            // send to remote control display if conditions are met
+            sendPlaybackState_syncCacheLock();
+        }
+    }
 
     /**
-     * Called by a remote control to retrieve the flags for the media transport control buttons
-     * that this client supports.
-     * @see {@link #FLAG_KEY_MEDIA_PREVIOUS},
+     * Sets the flags for the media transport control buttons that this client supports.
+     * @param a combination of the following flags:
+     *      {@link #FLAG_KEY_MEDIA_PREVIOUS},
      *      {@link #FLAG_KEY_MEDIA_REWIND},
      *      {@link #FLAG_KEY_MEDIA_PLAY},
      *      {@link #FLAG_KEY_MEDIA_PLAY_PAUSE},
@@ -227,17 +380,311 @@
      *      {@link #FLAG_KEY_MEDIA_FAST_FORWARD},
      *      {@link #FLAG_KEY_MEDIA_NEXT}
      */
-    int getTransportControlFlags();
+    public void setTransportControlFlags(int transportControlFlags) {
+        synchronized(mCacheLock) {
+            // store locally
+            mTransportControlFlags = transportControlFlags;
+
+            // send to remote control display if conditions are met
+            sendTransportControlFlags_syncCacheLock();
+        }
+    }
 
     /**
-     * Called by a remote control to retrieve the album art picture at the requested size.
-     * Note that returning a bitmap smaller than the maximum requested dimension is accepted
-     * and it will be scaled as needed, but exceeding the maximum dimensions may produce
-     * unspecified results, such as the image being cropped or simply not being displayed.
-     * @param maxWidth the maximum width of the requested bitmap expressed in pixels.
-     * @param maxHeight the maximum height of the requested bitmap expressed in pixels.
-     * @return the bitmap for the album art, or null if there isn't any.
-     * @see android.graphics.Bitmap
+     * Lock for all cached data
      */
-    Bitmap getAlbumArt(int maxWidth, int maxHeight);
+    private final Object mCacheLock = new Object();
+    /**
+     * Cache for the playback state.
+     * Access synchronized on mCacheLock
+     */
+    private int mPlaybackState = PLAYSTATE_NONE;
+    /**
+     * Cache for the artwork bitmap.
+     * Access synchronized on mCacheLock
+     */
+    private Bitmap mArtwork;
+    private final int ARTWORK_DEFAULT_SIZE = 256;
+    private int mArtworkExpectedWidth = ARTWORK_DEFAULT_SIZE;
+    private int mArtworkExpectedHeight = ARTWORK_DEFAULT_SIZE;
+    /**
+     * Cache for the transport control mask.
+     * Access synchronized on mCacheLock
+     */
+    private int mTransportControlFlags = FLAGS_KEY_MEDIA_NONE;
+    /**
+     * Cache for the metadata strings.
+     * Access synchronized on mCacheLock
+     */
+    private Bundle mMetadata = new Bundle();
+    /**
+     * The current remote control client generation ID across the system
+     */
+    private int mCurrentClientGenId = -1;
+    /**
+     * The remote control client generation ID, the last time it was told it was the current RC.
+     * If (mCurrentClientGenId == mInternalClientGenId) is true, it means that this remote control
+     * client is the "focused" one, and that whenever this client's info is updated, it needs to
+     * send it to the known IRemoteControlDisplay interfaces.
+     */
+    private int mInternalClientGenId = -2;
+
+    /**
+     * The media button event receiver associated with this remote control client
+     */
+    private final ComponentName mRcEventReceiver;
+
+    /**
+     * The remote control display to which this client will send information.
+     * NOTE: Only one IRemoteControlDisplay supported in this implementation
+     */
+    private IRemoteControlDisplay mRcDisplay;
+
+    /**
+     * @hide
+     * Accessor to media button event receiver
+     */
+    public ComponentName getRcEventReceiver() {
+        return mRcEventReceiver;
+    }
+    /**
+     * @hide
+     * Accessor to IRemoteControlClient
+     */
+    public IRemoteControlClient getIRemoteControlClient() {
+        return mIRCC;
+    }
+
+    /**
+     * The IRemoteControlClient implementation
+     */
+    private IRemoteControlClient mIRCC = new IRemoteControlClient.Stub() {
+
+        public void onInformationRequested(int clientGeneration, int infoFlags,
+                int artWidth, int artHeight) {
+            // only post messages, we can't block here
+            if (mEventHandler != null) {
+                // signal new client
+                mEventHandler.removeMessages(MSG_NEW_INTERNAL_CLIENT_GEN);
+                mEventHandler.dispatchMessage(
+                        mEventHandler.obtainMessage(
+                                MSG_NEW_INTERNAL_CLIENT_GEN,
+                                artWidth, artHeight,
+                                new Integer(clientGeneration)));
+                // send the information
+                mEventHandler.removeMessages(MSG_REQUEST_PLAYBACK_STATE);
+                mEventHandler.removeMessages(MSG_REQUEST_METADATA);
+                mEventHandler.removeMessages(MSG_REQUEST_TRANSPORTCONTROL);
+                mEventHandler.removeMessages(MSG_REQUEST_ARTWORK);
+                mEventHandler.dispatchMessage(
+                        mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE));
+                mEventHandler.dispatchMessage(
+                        mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL));
+                mEventHandler.dispatchMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA));
+                mEventHandler.dispatchMessage(mEventHandler.obtainMessage(MSG_REQUEST_ARTWORK));
+            }
+        }
+
+        public void setCurrentClientGenerationId(int clientGeneration) {
+            // only post messages, we can't block here
+            if (mEventHandler != null) {
+                mEventHandler.removeMessages(MSG_NEW_CURRENT_CLIENT_GEN);
+                mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+                        MSG_NEW_CURRENT_CLIENT_GEN, clientGeneration, 0/*ignored*/));
+            }
+        }
+
+        public void plugRemoteControlDisplay(IRemoteControlDisplay rcd) {
+            // only post messages, we can't block here
+            if (mEventHandler != null) {
+                mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+                        MSG_PLUG_DISPLAY, rcd));
+            }
+        }
+
+        public void unplugRemoteControlDisplay(IRemoteControlDisplay rcd) {
+            // only post messages, we can't block here
+            if (mEventHandler != null) {
+                mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+                        MSG_UNPLUG_DISPLAY, rcd));
+            }
+        }
+    };
+
+    private EventHandler mEventHandler;
+    private final static int MSG_REQUEST_PLAYBACK_STATE = 1;
+    private final static int MSG_REQUEST_METADATA = 2;
+    private final static int MSG_REQUEST_TRANSPORTCONTROL = 3;
+    private final static int MSG_REQUEST_ARTWORK = 4;
+    private final static int MSG_NEW_INTERNAL_CLIENT_GEN = 5;
+    private final static int MSG_NEW_CURRENT_CLIENT_GEN = 6;
+    private final static int MSG_PLUG_DISPLAY = 7;
+    private final static int MSG_UNPLUG_DISPLAY = 8;
+
+    private class EventHandler extends Handler {
+        public EventHandler(RemoteControlClient rcc, Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case MSG_REQUEST_PLAYBACK_STATE:
+                    synchronized (mCacheLock) {
+                        sendPlaybackState_syncCacheLock();
+                    }
+                    break;
+                case MSG_REQUEST_METADATA:
+                    synchronized (mCacheLock) {
+                        sendMetadata_syncCacheLock();
+                    }
+                    break;
+                case MSG_REQUEST_TRANSPORTCONTROL:
+                    synchronized (mCacheLock) {
+                        sendTransportControlFlags_syncCacheLock();
+                    }
+                    break;
+                case MSG_REQUEST_ARTWORK:
+                    synchronized (mCacheLock) {
+                        sendArtwork_syncCacheLock();
+                    }
+                    break;
+                case MSG_NEW_INTERNAL_CLIENT_GEN:
+                    onNewInternalClientGen((Integer)msg.obj, msg.arg1, msg.arg2);
+                    break;
+                case MSG_NEW_CURRENT_CLIENT_GEN:
+                    onNewCurrentClientGen(msg.arg1);
+                    break;
+                case MSG_PLUG_DISPLAY:
+                    onPlugDisplay((IRemoteControlDisplay)msg.obj);
+                    break;
+                case MSG_UNPLUG_DISPLAY:
+                    onUnplugDisplay((IRemoteControlDisplay)msg.obj);
+                    break;
+                default:
+                    Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler");
+            }
+        }
+    }
+
+    private void sendPlaybackState_syncCacheLock() {
+        if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
+            try {
+                mRcDisplay.setPlaybackState(mInternalClientGenId, mPlaybackState);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error in setPlaybackState(), dead display "+e);
+                mRcDisplay = null;
+                mArtworkExpectedWidth = -1;
+                mArtworkExpectedHeight = -1;
+            }
+        }
+    }
+
+    private void sendMetadata_syncCacheLock() {
+        if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
+            try {
+                mRcDisplay.setMetadata(mInternalClientGenId, mMetadata);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error in sendPlaybackState(), dead display "+e);
+                mRcDisplay = null;
+                mArtworkExpectedWidth = -1;
+                mArtworkExpectedHeight = -1;
+            }
+        }
+    }
+
+    private void sendTransportControlFlags_syncCacheLock() {
+        if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
+            try {
+                mRcDisplay.setTransportControlFlags(mInternalClientGenId,
+                        mTransportControlFlags);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error in sendTransportControlFlags(), dead display "+e);
+                mRcDisplay = null;
+                mArtworkExpectedWidth = -1;
+                mArtworkExpectedHeight = -1;
+            }
+        }
+    }
+
+    private void sendArtwork_syncCacheLock() {
+        if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
+            // even though we have already scaled in setArtwork(), when this client needs to
+            // send the bitmap, there might be newer and smaller expected dimensions, so we have
+            // to check again.
+            mArtwork = scaleBitmapIfTooBig(mArtwork, mArtworkExpectedWidth, mArtworkExpectedHeight);
+            try {
+                mRcDisplay.setArtwork(mInternalClientGenId, mArtwork);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error in sendArtwork(), dead display "+e);
+                mRcDisplay = null;
+                mArtworkExpectedWidth = -1;
+                mArtworkExpectedHeight = -1;
+            }
+        }
+    }
+
+    private void onNewInternalClientGen(Integer clientGeneration, int artWidth, int artHeight) {
+        synchronized (mCacheLock) {
+            // this remote control client is told it is the "focused" one:
+            // it implies that now (mCurrentClientGenId == mInternalClientGenId) is true
+            mInternalClientGenId = clientGeneration.intValue();
+            if (artWidth > 0) {
+                mArtworkExpectedWidth = artWidth;
+                mArtworkExpectedHeight = artHeight;
+            }
+        }
+    }
+
+    private void onNewCurrentClientGen(int clientGeneration) {
+        synchronized (mCacheLock) {
+            mCurrentClientGenId = clientGeneration;
+        }
+    }
+
+    private void onPlugDisplay(IRemoteControlDisplay rcd) {
+        synchronized(mCacheLock) {
+            mRcDisplay = rcd;
+        }
+    }
+
+    private void onUnplugDisplay(IRemoteControlDisplay rcd) {
+        synchronized(mCacheLock) {
+            if ((mRcDisplay != null) && (mRcDisplay.equals(rcd))) {
+                mRcDisplay = null;
+                mArtworkExpectedWidth = ARTWORK_DEFAULT_SIZE;
+                mArtworkExpectedHeight = ARTWORK_DEFAULT_SIZE;
+            }
+        }
+    }
+
+    /**
+     * Scale a bitmap to fit the smallest dimension by uniformly scaling the incoming bitmap.
+     * If the bitmap fits, then do nothing and return the original.
+     *
+     * @param bitmap
+     * @param maxWidth
+     * @param maxHeight
+     * @return
+     */
+
+    private Bitmap scaleBitmapIfTooBig(Bitmap bitmap, int maxWidth, int maxHeight) {
+        final int width = bitmap.getWidth();
+        final int height = bitmap.getHeight();
+        if (width > maxWidth || height > maxHeight) {
+            float scale = Math.min((float) maxWidth / width, (float) maxHeight / height);
+            int newWidth = Math.round(scale * width);
+            int newHeight = Math.round(scale * height);
+            Bitmap outBitmap = Bitmap.createBitmap(newWidth, newHeight, bitmap.getConfig());
+            Canvas canvas = new Canvas(outBitmap);
+            Paint paint = new Paint();
+            paint.setAntiAlias(true);
+            paint.setFilterBitmap(true);
+            canvas.drawBitmap(bitmap, null,
+                    new RectF(0, 0, outBitmap.getWidth(), outBitmap.getHeight()), paint);
+            bitmap = outBitmap;
+        }
+        return bitmap;
+
+    }
 }
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 92e84c2..09f91f5 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -52,7 +52,10 @@
         *post_id3_pos = 0;
     }
 
+    bool resync_from_head = false;
     if (*inout_pos == 0) {
+        resync_from_head = true;
+
         // Skip an optional ID3 header if syncing at the very beginning
         // of the datasource.
 
@@ -137,22 +140,20 @@
 
         uint32_t header = U32_AT(tmp);
 
-        if (match_header != 0 && (header & kMask) != (match_header & kMask)) {
-            ++pos;
-            ++tmp;
-            --remainingBytes;
-            continue;
-        }
-
         size_t frame_size;
-        int sample_rate, num_channels, bitrate;
-        if (!GetMPEGAudioFrameSize(
-                    header, &frame_size,
-                    &sample_rate, &num_channels, &bitrate)) {
-            ++pos;
-            ++tmp;
-            --remainingBytes;
-            continue;
+        if ((match_header != 0 && (header & kMask) != (match_header & kMask))
+                || !GetMPEGAudioFrameSize(header, &frame_size)) {
+            if (resync_from_head) {
+                // This isn't a valid mp3 file because it failed to detect
+                // a header while a valid mp3 file should have a valid
+                // header here.
+                break;
+            } else {
+                ++pos;
+                ++tmp;
+                --remainingBytes;
+                continue;
+            }
         }
 
         LOGV("found possible 1st frame at %lld (header = 0x%08x)", pos, header);
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
index afa92f1..9629702 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
@@ -43,7 +43,7 @@
  *
  */
 class KeyguardStatusViewManager implements OnClickListener {
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
     private static final String TAG = "KeyguardStatusView";
 
     public static final int LOCK_ICON = 0; // R.drawable.ic_lock_idle_lock;
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 431f8e0..5661116 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -116,6 +116,7 @@
     private static final int KEYGUARD_DONE_AUTHENTICATING = 11;
     private static final int SET_HIDDEN = 12;
     private static final int KEYGUARD_TIMEOUT = 13;
+    private static final int REPORT_SHOW_DONE = 14;
 
     /**
      * The default amount of time we stay awake (used for all key input)
@@ -238,6 +239,8 @@
 
     private boolean mScreenOn = false;
 
+    private boolean mShowPending = false;
+
     // last known state of the cellular connection
     private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE;
 
@@ -306,7 +309,7 @@
         synchronized (this) {
             if (DEBUG) Log.d(TAG, "onSystemReady");
             mSystemReady = true;
-            doKeyguard();
+            doKeyguardLocked();
         }
     }
 
@@ -363,7 +366,7 @@
                 if (timeout <= 0) {
                     // Lock now
                     mSuppressNextLockSound = true;
-                    doKeyguard();
+                    doKeyguardLocked();
                 } else {
                     // Lock in the future
                     long when = SystemClock.elapsedRealtime() + timeout;
@@ -379,7 +382,19 @@
             } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) {
                 // Do not enable the keyguard if the prox sensor forced the screen off.
             } else {
-                doKeyguard();
+                if (!doKeyguardLocked() && why == WindowManagerPolicy.OFF_BECAUSE_OF_USER) {
+                    // The user has explicitly turned off the screen, causing it
+                    // to lock.  We want to block here until the keyguard window
+                    // has shown, so the power manager won't complete the screen
+                    // off flow until that point, so we know it won't turn *on*
+                    // the screen until this is done.
+                    while (mShowPending) {
+                        try {
+                            wait();
+                        } catch (InterruptedException e) {
+                        }
+                    }
+                }
             }
         }
     }
@@ -553,56 +568,58 @@
     }
 
     /**
-     * Enable the keyguard if the settings are appropriate.
+     * Enable the keyguard if the settings are appropriate.  Return true if all
+     * work that will happen is done; returns false if the caller can wait for
+     * the keyguard to be shown.
      */
-    private void doKeyguard() {
-        synchronized (this) {
-            // if another app is disabling us, don't show
-            if (!mExternallyEnabled) {
-                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
+    private boolean doKeyguardLocked() {
+        // if another app is disabling us, don't show
+        if (!mExternallyEnabled) {
+            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
 
-                // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
-                // for an occasional ugly flicker in this situation:
-                // 1) receive a call with the screen on (no keyguard) or make a call
-                // 2) screen times out
-                // 3) user hits key to turn screen back on
-                // instead, we reenable the keyguard when we know the screen is off and the call
-                // ends (see the broadcast receiver below)
-                // TODO: clean this up when we have better support at the window manager level
-                // for apps that wish to be on top of the keyguard
-                return;
-            }
-
-            // if the keyguard is already showing, don't bother
-            if (mKeyguardViewManager.isShowing()) {
-                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
-                return;
-            }
-
-            // if the setup wizard hasn't run yet, don't show
-            final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",
-                    false);
-            final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();
-            final IccCard.State state = mUpdateMonitor.getSimState();
-            final boolean lockedOrMissing = state.isPinLocked()
-                    || ((state == IccCard.State.ABSENT
-                            || state == IccCard.State.PERM_DISABLED)
-                            && requireSim);
-
-            if (!lockedOrMissing && !provisioned) {
-                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
-                        + " and the sim is not locked or missing");
-                return;
-            }
-
-            if (mLockPatternUtils.isLockScreenDisabled()) {
-                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
-                return;
-            }
-
-            if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
-            showLocked();
+            // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
+            // for an occasional ugly flicker in this situation:
+            // 1) receive a call with the screen on (no keyguard) or make a call
+            // 2) screen times out
+            // 3) user hits key to turn screen back on
+            // instead, we reenable the keyguard when we know the screen is off and the call
+            // ends (see the broadcast receiver below)
+            // TODO: clean this up when we have better support at the window manager level
+            // for apps that wish to be on top of the keyguard
+            return true;
         }
+
+        // if the keyguard is already showing, don't bother
+        if (mKeyguardViewManager.isShowing()) {
+            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
+            return true;
+        }
+
+        // if the setup wizard hasn't run yet, don't show
+        final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",
+                false);
+        final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();
+        final IccCard.State state = mUpdateMonitor.getSimState();
+        final boolean lockedOrMissing = state.isPinLocked()
+                || ((state == IccCard.State.ABSENT
+                        || state == IccCard.State.PERM_DISABLED)
+                        && requireSim);
+
+        if (!lockedOrMissing && !provisioned) {
+            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
+                    + " and the sim is not locked or missing");
+            return true;
+        }
+
+        if (mLockPatternUtils.isLockScreenDisabled()) {
+            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
+            return true;
+        }
+
+        if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
+        mShowPending = true;
+        showLocked();
+        return false;
     }
 
     /**
@@ -696,41 +713,49 @@
             case ABSENT:
                 // only force lock screen in case of missing sim if user hasn't
                 // gone through setup wizard
-                if (!mUpdateMonitor.isDeviceProvisioned()) {
-                    if (!isShowing()) {
-                        if (DEBUG) Log.d(TAG, "ICC_ABSENT isn't showing,"
-                                + " we need to show the keyguard since the "
-                                + "device isn't provisioned yet.");
-                        doKeyguard();
-                    } else {
-                        resetStateLocked();
+                synchronized (this) {
+                    if (!mUpdateMonitor.isDeviceProvisioned()) {
+                        if (!isShowing()) {
+                            if (DEBUG) Log.d(TAG, "ICC_ABSENT isn't showing,"
+                                    + " we need to show the keyguard since the "
+                                    + "device isn't provisioned yet.");
+                            doKeyguardLocked();
+                        } else {
+                            resetStateLocked();
+                        }
                     }
                 }
                 break;
             case PIN_REQUIRED:
             case PUK_REQUIRED:
-                if (!isShowing()) {
-                    if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't showing, we need "
-                            + "to show the keyguard so the user can enter their sim pin");
-                    doKeyguard();
-                } else {
-                    resetStateLocked();
+                synchronized (this) {
+                    if (!isShowing()) {
+                        if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't showing, we need "
+                                + "to show the keyguard so the user can enter their sim pin");
+                        doKeyguardLocked();
+                    } else {
+                        resetStateLocked();
+                    }
                 }
                 break;
             case PERM_DISABLED:
-                if (!isShowing()) {
-                    if (DEBUG) Log.d(TAG, "PERM_DISABLED and "
-                          + "keygaurd isn't showing.");
-                    doKeyguard();
-                } else {
-                    if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
-                          + "show permanently disabled message in lockscreen.");
-                    resetStateLocked();
+                synchronized (this) {
+                    if (!isShowing()) {
+                        if (DEBUG) Log.d(TAG, "PERM_DISABLED and "
+                              + "keygaurd isn't showing.");
+                        doKeyguardLocked();
+                    } else {
+                        if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
+                              + "show permanently disabled message in lockscreen.");
+                        resetStateLocked();
+                    }
                 }
                 break;
             case READY:
-                if (isShowing()) {
-                    resetStateLocked();
+                synchronized (this) {
+                    if (isShowing()) {
+                        resetStateLocked();
+                    }
                 }
                 break;
         }
@@ -751,27 +776,31 @@
                 if (DEBUG) Log.d(TAG, "received DELAYED_KEYGUARD_ACTION with seq = "
                         + sequence + ", mDelayedShowingSequence = " + mDelayedShowingSequence);
 
-                if (mDelayedShowingSequence == sequence) {
-                    // Don't play lockscreen SFX if the screen went off due to
-                    // timeout.
-                    mSuppressNextLockSound = true;
-
-                    doKeyguard();
+                synchronized (KeyguardViewMediator.this) {
+                    if (mDelayedShowingSequence == sequence) {
+                        // Don't play lockscreen SFX if the screen went off due to
+                        // timeout.
+                        mSuppressNextLockSound = true;
+    
+                        doKeyguardLocked();
+                    }
                 }
             } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
                 mPhoneState = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
 
-                if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)  // call ending
-                        && !mScreenOn                           // screen off
-                        && mExternallyEnabled) {                // not disabled by any app
-
-                    // note: this is a way to gracefully reenable the keyguard when the call
-                    // ends and the screen is off without always reenabling the keyguard
-                    // each time the screen turns off while in call (and having an occasional ugly
-                    // flicker while turning back on the screen and disabling the keyguard again).
-                    if (DEBUG) Log.d(TAG, "screen is off and call ended, let's make sure the "
-                            + "keyguard is showing");
-                    doKeyguard();
+                synchronized (KeyguardViewMediator.this) {
+                    if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)  // call ending
+                            && !mScreenOn                           // screen off
+                            && mExternallyEnabled) {                // not disabled by any app
+    
+                        // note: this is a way to gracefully reenable the keyguard when the call
+                        // ends and the screen is off without always reenabling the keyguard
+                        // each time the screen turns off while in call (and having an occasional ugly
+                        // flicker while turning back on the screen and disabling the keyguard again).
+                        if (DEBUG) Log.d(TAG, "screen is off and call ended, let's make sure the "
+                                + "keyguard is showing");
+                        doKeyguardLocked();
+                    }
                 }
             }
         }
@@ -962,7 +991,15 @@
                     handleSetHidden(msg.arg1 != 0);
                     break;
                 case KEYGUARD_TIMEOUT:
-                    doKeyguard();
+                    synchronized (KeyguardViewMediator.this) {
+                        doKeyguardLocked();
+                    }
+                    break;
+                case REPORT_SHOW_DONE:
+                    synchronized (KeyguardViewMediator.this) {
+                        mShowPending = false;
+                        KeyguardViewMediator.this.notifyAll();
+                    }
                     break;
             }
         }
@@ -1062,8 +1099,6 @@
             if (DEBUG) Log.d(TAG, "handleShow");
             if (!mSystemReady) return;
 
-            playSounds(true);
-
             mKeyguardViewManager.show();
             mShowing = true;
             adjustUserActivityLocked();
@@ -1072,7 +1107,17 @@
                 ActivityManagerNative.getDefault().closeSystemDialogs("lock");
             } catch (RemoteException e) {
             }
+
+            // Do this at the end to not slow down display of the keyguard.
+            playSounds(true);
+
             mShowKeyguardWakeLock.release();
+
+            // We won't say the show is done yet because the view hierarchy
+            // still needs to do the traversal.  Posting this message allows
+            // us to hold off until that is done.
+            Message msg = mHandler.obtainMessage(REPORT_SHOW_DONE);
+            mHandler.sendMessage(msg);
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 431a6bb..e2d6c5f 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2512,8 +2512,15 @@
         a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
         a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
 
-        if (getContext().getApplicationInfo().targetSdkVersion
-                < android.os.Build.VERSION_CODES.HONEYCOMB) {
+        final Context context = getContext();
+        final int targetSdk = context.getApplicationInfo().targetSdkVersion;
+        final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
+        final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
+        final boolean targetHcNeedsOptions = context.getResources().getBoolean(
+                com.android.internal.R.bool.target_honeycomb_needs_options_menu);
+        final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
+
+        if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
             addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
         }
         
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 1d5fbc0a..be129a8 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -120,6 +120,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
 import android.view.WindowManagerImpl;
 import android.view.WindowManagerPolicy;
 import android.view.KeyCharacterMap.FallbackAction;
@@ -197,8 +198,9 @@
     // things in here CAN NOT take focus, but are shown on top of everything else.
     static final int SYSTEM_OVERLAY_LAYER = 20;
     static final int SECURE_SYSTEM_OVERLAY_LAYER = 21;
+    static final int BOOT_PROGRESS_LAYER = 22;
     // the (mouse) pointer layer
-    static final int POINTER_LAYER = 22;
+    static final int POINTER_LAYER = 23;
 
     static final int APPLICATION_MEDIA_SUBLAYER = -2;
     static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
@@ -1095,6 +1097,8 @@
             return POINTER_LAYER;
         case TYPE_NAVIGATION_BAR:
             return NAVIGATION_BAR_LAYER;
+        case TYPE_BOOT_PROGRESS:
+            return BOOT_PROGRESS_LAYER;
         }
         Log.e(TAG, "Unknown window type: " + type);
         return APPLICATION_LAYER;
@@ -2797,13 +2801,26 @@
     /** {@inheritDoc} */
     public void screenTurnedOff(int why) {
         EventLog.writeEvent(70000, 0);
-        mKeyguardMediator.onScreenTurnedOff(why);
         synchronized (mLock) {
             mScreenOn = false;
+        }
+        mKeyguardMediator.onScreenTurnedOff(why);
+        synchronized (mLock) {
             updateOrientationListenerLp();
             updateLockScreenTimeout();
             updateScreenSaverTimeoutLocked();
         }
+        try {
+            mWindowManager.waitForAllDrawn();
+        } catch (RemoteException e) {
+        }
+        // Wait for one frame to give surface flinger time to do its
+        // compositing.  Yes this is a hack, but I am really not up right now for
+        // implementing some mechanism to block until SF is done. :p
+        try {
+            Thread.sleep(20);
+        } catch (InterruptedException e) {
+        }
     }
 
     /** {@inheritDoc} */
@@ -3092,7 +3109,7 @@
                     mBootMsgDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
                     mBootMsgDialog.setIndeterminate(true);
                     mBootMsgDialog.getWindow().setType(
-                            WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY);
+                            WindowManager.LayoutParams.TYPE_BOOT_PROGRESS);
                     mBootMsgDialog.getWindow().addFlags(
                             WindowManager.LayoutParams.FLAG_DIM_BEHIND
                             | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index d80a2cd..cbd986f 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -161,6 +161,7 @@
     private int mStayOnConditions = 0;
     private final int[] mBroadcastQueue = new int[] { -1, -1, -1 };
     private final int[] mBroadcastWhy = new int[3];
+    private boolean mBroadcastingScreenOff = false;
     private int mPartialCount = 0;
     private int mPowerState;
     // mScreenOffReason can be WindowManagerPolicy.OFF_BECAUSE_OF_USER,
@@ -1342,6 +1343,10 @@
             mBroadcastWakeLock.release();
         }
 
+        // The broadcast queue has changed; make sure the screen is on if it
+        // is now possible for it to be.
+        updateNativePowerStateLocked();
+
         // Now send the message.
         if (index >= 0) {
             // Acquire the broadcast wake lock before changing the power
@@ -1370,6 +1375,9 @@
                         mBroadcastWhy[i] = mBroadcastWhy[i+1];
                     }
                     policy = getPolicyLocked();
+                    if (value == 0) {
+                        mBroadcastingScreenOff = true;
+                    }
                 }
                 if (value == 1) {
                     mScreenOnStart = SystemClock.uptimeMillis();
@@ -1412,6 +1420,8 @@
                         synchronized (mLocks) {
                             EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3,
                                     mBroadcastWakeLock.mCount);
+                            mBroadcastingScreenOff = false;
+                            updateNativePowerStateLocked();
                             mBroadcastWakeLock.release();
                         }
                     }
@@ -1442,6 +1452,10 @@
             synchronized (mLocks) {
                 EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0,
                         SystemClock.uptimeMillis() - mScreenOffStart, mBroadcastWakeLock.mCount);
+                synchronized (mLocks) {
+                    mBroadcastingScreenOff = false;
+                    updateNativePowerStateLocked();
+                }
                 mBroadcastWakeLock.release();
             }
         }
@@ -1768,6 +1782,22 @@
     }
     
     private void updateNativePowerStateLocked() {
+        if ((mPowerState & SCREEN_ON_BIT) != 0) {
+            // Don't turn screen on if we are currently reporting a screen off.
+            // This is to avoid letting the screen go on before things like the
+            // lock screen have been displayed due to it going off.
+            if (mBroadcastingScreenOff) {
+                // Currently broadcasting that the screen is off.  Don't
+                // allow screen to go on until that is done.
+                return;
+            }
+            for (int i=0; i<mBroadcastQueue.length; i++) {
+                if (mBroadcastQueue[i] == 0) {
+                    // A screen off is currently enqueued.
+                    return;
+                }
+            }
+        }
         nativeSetPowerState(
                 (mPowerState & SCREEN_ON_BIT) != 0,
                 (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index b817598..c935733 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -6564,7 +6564,7 @@
                             i--;
                         }
                     }
-                    
+
                     for (int i=0; i<ris.size(); i++) {
                         ActivityInfo ai = ris.get(i).activityInfo;
                         ComponentName comp = new ComponentName(ai.packageName, ai.name);
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 6f0779f..4ad0f45 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -3163,7 +3163,7 @@
 
                 //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
                 if (mMainStack) {
-                    if (!mService.mBooted && !fromTimeout) {
+                    if (!mService.mBooted) {
                         mService.mBooted = true;
                         enableScreen = true;
                     }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index dc5555e..3df94a6 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -50,11 +50,9 @@
 import android.Manifest;
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
-import android.app.ProgressDialog;
 import android.app.StatusBarManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -164,6 +162,7 @@
     static final boolean DEBUG_REORDER = false;
     static final boolean DEBUG_WALLPAPER = false;
     static final boolean DEBUG_DRAG = false;
+    static final boolean DEBUG_SCREEN_ON = false;
     static final boolean SHOW_SURFACE_ALLOC = false;
     static final boolean SHOW_TRANSACTIONS = false;
     static final boolean HIDE_STACK_CRAWLS = true;
@@ -408,6 +407,7 @@
     boolean mSafeMode;
     boolean mDisplayEnabled = false;
     boolean mSystemBooted = false;
+    boolean mForceDisplayEnabled = false;
     boolean mShowingBootMessages = false;
     int mInitialDisplayWidth = 0;
     int mInitialDisplayHeight = 0;
@@ -2189,7 +2189,7 @@
         // to hold off on removing the window until the animation is done.
         // If the display is frozen, just remove immediately, since the
         // animation wouldn't be seen.
-        if (win.mSurface != null && !mDisplayFrozen && mPolicy.isScreenOn()) {
+        if (win.mSurface != null && !mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn()) {
             // If we are not currently running the exit animation, we
             // need to see about starting one.
             if (wasVisible=win.isWinVisibleLw()) {
@@ -2536,6 +2536,12 @@
             win.mRelayoutCalled = true;
             final int oldVisibility = win.mViewVisibility;
             win.mViewVisibility = viewVisibility;
+            if (DEBUG_SCREEN_ON) {
+                RuntimeException stack = new RuntimeException();
+                stack.fillInStackTrace();
+                Slog.i(TAG, "Relayout " + win + ": oldVis=" + oldVisibility
+                        + " newVis=" + viewVisibility, stack);
+            }
             if (viewVisibility == View.VISIBLE &&
                     (win.mAppToken == null || !win.mAppToken.clientHidden)) {
                 displayed = !win.isVisibleLw();
@@ -2556,7 +2562,7 @@
                 if (displayed) {
                     if (win.mSurface != null && !win.mDrawPending
                             && !win.mCommitDrawPending && !mDisplayFrozen
-                            && mPolicy.isScreenOn()) {
+                            && mDisplayEnabled && mPolicy.isScreenOn()) {
                         applyEnterAnimationLocked(win);
                     }
                     if ((win.mAttrs.flags
@@ -2849,7 +2855,7 @@
         // frozen, there is no reason to animate and it can cause strange
         // artifacts when we unfreeze the display if some different animation
         // is running.
-        if (!mDisplayFrozen && mPolicy.isScreenOn()) {
+        if (!mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn()) {
             int anim = mPolicy.selectAnimationLw(win, transit);
             int attr = -1;
             Animation a = null;
@@ -2935,7 +2941,7 @@
         // frozen, there is no reason to animate and it can cause strange
         // artifacts when we unfreeze the display if some different animation
         // is running.
-        if (!mDisplayFrozen && mPolicy.isScreenOn()) {
+        if (!mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn()) {
             Animation a;
             if (mNextAppTransitionPackage != null) {
                 a = loadAnimation(mNextAppTransitionPackage, enter ?
@@ -3501,7 +3507,7 @@
             if (DEBUG_APP_TRANSITIONS) Slog.v(
                     TAG, "Prepare app transition: transit=" + transit
                     + " mNextAppTransition=" + mNextAppTransition);
-            if (!mDisplayFrozen && mPolicy.isScreenOn()) {
+            if (!mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn()) {
                 if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET
                         || mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) {
                     mNextAppTransition = transit;
@@ -3585,7 +3591,7 @@
             // If the display is frozen, we won't do anything until the
             // actual window is displayed so there is no reason to put in
             // the starting window.
-            if (mDisplayFrozen || !mPolicy.isScreenOn()) {
+            if (mDisplayFrozen || !mDisplayEnabled || !mPolicy.isScreenOn()) {
                 return;
             }
 
@@ -3867,7 +3873,7 @@
 
             // If we are preparing an app transition, then delay changing
             // the visibility of this token until we execute that transition.
-            if (!mDisplayFrozen && mPolicy.isScreenOn()
+            if (!mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn()
                     && mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
                 // Already in requested state, don't do anything more.
                 if (wtoken.hiddenRequested != visible) {
@@ -4688,6 +4694,10 @@
             }
             mSystemBooted = true;
             hideBootMessagesLocked();
+            // If the screen still doesn't come up after 30 seconds, give
+            // up and turn it on.
+            Message msg = mH.obtainMessage(H.BOOT_TIMEOUT);
+            mH.sendMessageDelayed(msg, 30*1000);
         }
 
         performEnableScreen();
@@ -4703,6 +4713,17 @@
         mH.sendMessage(mH.obtainMessage(H.ENABLE_SCREEN));
     }
 
+    public void performBootTimeout() {
+        synchronized(mWindowMap) {
+            if (mDisplayEnabled) {
+                return;
+            }
+            Slog.w(TAG, "***** BOOT TIMEOUT: forcing display enabled");
+            mForceDisplayEnabled = true;
+        }
+        performEnableScreen();
+    }
+
     public void performEnableScreen() {
         synchronized(mWindowMap) {
             if (mDisplayEnabled) {
@@ -4712,19 +4733,55 @@
                 return;
             }
 
-            // Don't enable the screen until all existing windows
-            // have been drawn.
-            final int N = mWindows.size();
-            for (int i=0; i<N; i++) {
-                WindowState w = mWindows.get(i);
-                if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
+            if (!mForceDisplayEnabled) {
+                // Don't enable the screen until all existing windows
+                // have been drawn.
+                boolean haveBootMsg = false;
+                boolean haveApp = false;
+                boolean haveWallpaper = false;
+                boolean haveKeyguard = false;
+                final int N = mWindows.size();
+                for (int i=0; i<N; i++) {
+                    WindowState w = mWindows.get(i);
+                    if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
+                        return;
+                    }
+                    if (w.isDrawnLw()) {
+                        if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_BOOT_PROGRESS) {
+                            haveBootMsg = true;
+                        } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION) {
+                            haveApp = true;
+                        } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER) {
+                            haveWallpaper = true;
+                        } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD) {
+                            haveKeyguard = true;
+                        }
+                    }
+                }
+
+                if (DEBUG_SCREEN_ON) {
+                    Slog.i(TAG, "******** booted=" + mSystemBooted + " msg=" + mShowingBootMessages
+                            + " haveBoot=" + haveBootMsg + " haveApp=" + haveApp
+                            + " haveWall=" + haveWallpaper + " haveKeyguard=" + haveKeyguard);
+                }
+
+                // If we are turning on the screen to show the boot message,
+                // don't do it until the boot message is actually displayed.
+                if (!mSystemBooted && !haveBootMsg) {
+                    return;
+                }
+    
+                // If we are turning on the screen after the boot is completed
+                // normally, don't do so until we have the application and
+                // wallpaper.
+                if (mSystemBooted && ((!haveApp && !haveKeyguard) || !haveWallpaper)) {
                     return;
                 }
             }
 
             mDisplayEnabled = true;
+            if (DEBUG_SCREEN_ON) Slog.i(TAG, "******************** ENABLING SCREEN!");
             if (false) {
-                Slog.i(TAG, "ENABLING SCREEN!");
                 StringWriter sw = new StringWriter();
                 PrintWriter pw = new PrintWriter(sw);
                 this.dump(null, pw, null);
@@ -6239,6 +6296,7 @@
         public static final int DRAG_START_TIMEOUT = 20;
         public static final int DRAG_END_TIMEOUT = 21;
         public static final int REPORT_HARD_KEYBOARD_STATUS_CHANGE = 22;
+        public static final int BOOT_TIMEOUT = 23;
 
         private Session mLastReportedHold;
 
@@ -6549,6 +6607,11 @@
                     break;
                 }
 
+                case BOOT_TIMEOUT: {
+                    performBootTimeout();
+                    break;
+                }
+
                 case APP_FREEZE_TIMEOUT: {
                     synchronized (mWindowMap) {
                         Slog.w(TAG, "App freeze timeout expired.");
@@ -8300,7 +8363,7 @@
 
             if (mDimAnimator != null && mDimAnimator.mDimShown) {
                 animating |= mDimAnimator.updateSurface(dimming, currentTime,
-                        mDisplayFrozen || !mPolicy.isScreenOn());
+                        mDisplayFrozen || !mDisplayEnabled || !mPolicy.isScreenOn());
             }
 
             if (!blurring && mBlurShown) {
@@ -8498,12 +8561,44 @@
                 mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
             }
         }
-        
+
+        mWindowMap.notifyAll();
+
         // Check to see if we are now in a state where the screen should
         // be enabled, because the window obscured flags have changed.
         enableScreenIfNeededLocked();
     }
-    
+
+    public void waitForAllDrawn() {
+        synchronized (mWindowMap) {
+            while (true) {
+                final int N = mWindows.size();
+                boolean okay = true;
+                for (int i=0; i<N && okay; i++) {
+                    WindowState w = mWindows.get(i);
+                    if (DEBUG_SCREEN_ON) {
+                        Slog.i(TAG, "Window " + w + " vis=" + w.isVisibleLw()
+                                + " obscured=" + w.mObscured + " drawn=" + w.isDrawnLw());
+                    }
+                    if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
+                        if (DEBUG_SCREEN_ON) {
+                            Slog.i(TAG, "Window not yet drawn: " + w);
+                        }
+                        okay = false;
+                        break;
+                    }
+                }
+                if (okay) {
+                    return;
+                }
+                try {
+                    mWindowMap.wait();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+    }
+
     /**
      * Must be called with the main window manager lock held.
      */
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
index 7d83a9f..ed29a78 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
@@ -451,6 +451,10 @@
         return 0;
     }
 
+    public void waitForAllDrawn() {
+        // TODO Auto-generated method stub
+    }
+    
     public IBinder asBinder() {
         // TODO Auto-generated method stub
         return null;