Merge "Fix how we hide and show the nav bar."
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 99acb3f..e8ab227 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -416,6 +416,13 @@
         public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;
 
         /**
+         * Window type: Fake window to consume touch events when the navigation
+         * bar is hidden.
+         * @hide
+         */
+        public static final int TYPE_HIDDEN_NAV_CONSUMER = FIRST_SYSTEM_WINDOW+22;
+
+        /**
          * End of types of system windows.
          */
         public static final int LAST_SYSTEM_WINDOW      = 2999;
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index aaf45e5..bfd2959 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -23,6 +23,7 @@
 import android.graphics.RectF;
 import android.os.IBinder;
 import android.os.LocalPowerManager;
+import android.os.Looper;
 import android.view.animation.Animation;
 
 import java.io.FileDescriptor;
@@ -315,6 +316,36 @@
     }
 
     /**
+     * Representation of a "fake window" that the policy has added to the
+     * window manager to consume events.
+     */
+    public interface FakeWindow {
+        /**
+         * Remove the fake window from the window manager.
+         */
+        void dismiss();
+    }
+
+    /**
+     * Interface for calling back in to the window manager that is private
+     * between it and the policy.
+     */
+    public interface WindowManagerFuncs {
+        /**
+         * Ask the window manager to re-evaluate the system UI flags.
+         */
+        public void reevaluateStatusBarVisibility();
+
+        /**
+         * Add a fake window to the window manager.  This window sits
+         * at the top of the other windows and consumes events.
+         */
+        public FakeWindow addFakeWindow(Looper looper, InputHandler inputHandler,
+                String name, int windowType, int layoutParamsFlags, boolean canReceiveKeys,
+                boolean hasFocus, boolean touchFullscreen);
+    }
+
+    /**
      * Bit mask that is set for all enter transition.
      */
     public final int TRANSIT_ENTER_MASK = 0x1000;
@@ -395,6 +426,7 @@
      * @param powerManager 
      */
     public void init(Context context, IWindowManager windowManager,
+            WindowManagerFuncs windowManagerFuncs,
             LocalPowerManager powerManager);
 
     /**
@@ -762,7 +794,7 @@
     /**
      * A new window has been focused.
      */
-    public void focusChanged(WindowState lastFocus, WindowState newFocus);
+    public int focusChangedLw(WindowState lastFocus, WindowState newFocus);
     
     /**
      * Called after the screen turns off.
@@ -968,6 +1000,14 @@
     public void setUserRotationMode(int mode, int rotation);
 
     /**
+     * Called when a new system UI visibility is being reported, allowing
+     * the policy to adjust what is actually reported.
+     * @param visibility The raw visiblity reported by the status bar.
+     * @return The new desired visibility.
+     */
+    public int adjustSystemUiVisibilityLw(int visibility);
+
+    /**
      * Print the WindowManagerPolicy's state into the given stream.
      *
      * @param prefix Text to print at the front of each line.
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 3916e86..aca1fa2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -34,7 +34,6 @@
     void topAppWindowChanged(boolean menuVisible);
     void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
     void setHardKeyboardStatus(boolean available, boolean enabled);
-    void userActivity();
     void toggleRecentApps();
 }
 
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 07430e7..ecebfc0 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -46,6 +46,5 @@
     void onNotificationClear(String pkg, String tag, int id);
     void setSystemUiVisibility(int vis);
     void setHardKeyboardEnabled(boolean enabled);
-    void userActivity();
     void toggleRecentApps();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index c91f513..bf2d5e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -60,8 +60,7 @@
     private static final int MSG_SHOW_IME_BUTTON        = 9 << MSG_SHIFT;
     private static final int MSG_SET_HARD_KEYBOARD_STATUS = 10 << MSG_SHIFT;
     
-    private static final int MSG_USER_ACTIVITY          = 11 << MSG_SHIFT;
-    private static final int MSG_TOGGLE_RECENT_APPS       = 12 << MSG_SHIFT;
+    private static final int MSG_TOGGLE_RECENT_APPS       = 11 << MSG_SHIFT;
 
     private StatusBarIconList mList;
     private Callbacks mCallbacks;
@@ -90,7 +89,6 @@
         public void topAppWindowChanged(boolean visible);
         public void setImeWindowStatus(IBinder token, int vis, int backDisposition);
         public void setHardKeyboardStatus(boolean available, boolean enabled);
-        public void userActivity();
         public void toggleRecentApps();
     }
 
@@ -191,13 +189,6 @@
         }
     }
 
-    public void userActivity() {
-        synchronized (mList) {
-            mHandler.removeMessages(MSG_USER_ACTIVITY);
-            mHandler.obtainMessage(MSG_USER_ACTIVITY, 0, 0, null).sendToTarget();
-        }
-    }
-
     public void toggleRecentApps() {
         synchronized (mList) {
             mHandler.removeMessages(MSG_TOGGLE_RECENT_APPS);
@@ -271,9 +262,6 @@
                 case MSG_SET_HARD_KEYBOARD_STATUS:
                     mCallbacks.setHardKeyboardStatus(msg.arg1 != 0, msg.arg2 != 0);
                     break;
-                case MSG_USER_ACTIVITY:
-                    mCallbacks.userActivity();
-                    break;
                 case MSG_TOGGLE_RECENT_APPS:
                     mCallbacks.toggleRecentApps();
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index d260e6d..e3a64a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -17,9 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import android.animation.Animator;
-import android.animation.AnimatorSet;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.ServiceManager;
@@ -27,14 +25,12 @@
 import android.util.Slog;
 import android.view.animation.AccelerateInterpolator;
 import android.view.Display;
-import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Surface;
 import android.view.WindowManager;
 import android.widget.LinearLayout;
-import android.content.res.Configuration;
 
 import com.android.internal.statusbar.IStatusBarService;
 
@@ -54,7 +50,6 @@
     final Display mDisplay;
     View mCurrentView = null;
     View[] mRotatedViews = new View[4];
-    AnimatorSet mLastAnimator = null;
 
     int mBarSize;
     boolean mVertical;
@@ -204,43 +199,6 @@
 
         // bring up the lights no matter what
         setLowProfile(false);
-
-        if (!ANIMATE_HIDE_TRANSITION) {
-            setVisibility(hide ? View.GONE : View.VISIBLE);
-            return;
-        }
-
-        float oldAlpha = mCurrentView.getAlpha();
-        if (DEBUG) {
-            Slog.d(TAG, "animating alpha: " + oldAlpha + " -> "
-                + (!hide ? 1f : 0f));
-        }
-
-        if (mLastAnimator != null && mLastAnimator.isRunning()) mLastAnimator.cancel();
-
-        if (!hide) {
-            setVisibility(View.VISIBLE);
-        }
-
-        // play us off, animatorset
-        mLastAnimator = new AnimatorSet();
-        mLastAnimator.playTogether(
-                ObjectAnimator.ofFloat(mCurrentView, "alpha", hide ? 0f : 1f),
-                ObjectAnimator.ofFloat(mCurrentView,
-                                       mVertical ? "translationX" : "translationY",
-                                       hide ? mBarSize : 0)
-        );
-        mLastAnimator.setDuration(!hide ? 250 : 1000);
-        mLastAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator _a) {
-                mLastAnimator = null;
-                if (hide) {
-                    setVisibility(View.GONE);
-                }
-            }
-        });
-        mLastAnimator.start();
     }
 
     public void onFinishInflate() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index d6e4d1b..09ea6ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -31,12 +31,8 @@
 import android.content.res.Resources;
 import android.content.res.Configuration;
 import android.graphics.PixelFormat;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.net.Uri;
-import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.Handler;
@@ -471,6 +467,7 @@
                     0
                     | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                     | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                     | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                     | WindowManager.LayoutParams.FLAG_SLIPPERY,
                 PixelFormat.OPAQUE);
@@ -2028,19 +2025,6 @@
         }
     }
 
-    // The user is not allowed to get stuck without navigation UI. Upon the slightest user
-    // interaction we bring the navigation back.
-    public void userActivity() {
-        if (0 != (mSystemUiVisibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)) {
-            try {
-                mBarService.setSystemUiVisibility(
-                    mSystemUiVisibility & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
-            } catch (RemoteException ex) {
-                // weep softly
-            }
-        }
-    }
-
     public void toggleRecentApps() {
         int msg = (mRecentsPanel.getVisibility() == View.GONE)
                 ? MSG_OPEN_RECENTS_PANEL : MSG_CLOSE_RECENTS_PANEL;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 54b45a9..ba52fb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -1822,9 +1822,6 @@
         visibilityChanged(false);
     }
 
-    public void userActivity() {
-    }
-
     public void toggleRecentApps() {
         int msg = (mRecentsPanel.getVisibility() == View.GONE)
                 ? MSG_OPEN_RECENTS_PANEL : MSG_CLOSE_RECENTS_PANEL;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 9e1dec7..487063d 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -103,6 +103,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_DRAG;
+import static android.view.WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
@@ -127,6 +128,7 @@
 import android.view.WindowManagerImpl;
 import android.view.WindowManagerPolicy;
 import android.view.KeyCharacterMap.FallbackAction;
+import android.view.WindowManagerPolicy.WindowManagerFuncs;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
@@ -205,6 +207,7 @@
     static final int BOOT_PROGRESS_LAYER = 22;
     // the (mouse) pointer layer
     static final int POINTER_LAYER = 23;
+    static final int HIDDEN_NAV_CONSUMER_LAYER = 24;
 
     static final int APPLICATION_MEDIA_SUBLAYER = -2;
     static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
@@ -220,10 +223,16 @@
     private static final int SW_LID = 0x00;
     private static final int BTN_MOUSE = 0x110;
     
+    /**
+     * Lock protecting internal state.  Must not call out into window
+     * manager with lock held.  (This lock will be acquired in places
+     * where the window manager is calling in with its own lock held.)
+     */
     final Object mLock = new Object();
-    
+
     Context mContext;
     IWindowManager mWindowManager;
+    WindowManagerFuncs mWindowManagerFuncs;
     LocalPowerManager mPowerManager;
     IStatusBarService mStatusBarService;
     Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
@@ -344,7 +353,11 @@
     int mDockLeft, mDockTop, mDockRight, mDockBottom;
     // During layout, the layer at which the doc window is placed.
     int mDockLayer;
-    
+    int mLastSystemUiVisibility;
+    int mForceClearingStatusBarVisibility = 0;
+
+    FakeWindow mHideNavFakeWindow = null;
+
     static final Rect mTmpParentFrame = new Rect();
     static final Rect mTmpDisplayFrame = new Rect();
     static final Rect mTmpContentFrame = new Rect();
@@ -647,9 +660,11 @@
 
     /** {@inheritDoc} */
     public void init(Context context, IWindowManager windowManager,
+            WindowManagerFuncs windowManagerFuncs,
             LocalPowerManager powerManager) {
         mContext = context;
         mWindowManager = windowManager;
+        mWindowManagerFuncs = windowManagerFuncs;
         mPowerManager = powerManager;
         mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager);
         mHandler = new Handler();
@@ -1068,6 +1083,8 @@
             return NAVIGATION_BAR_LAYER;
         case TYPE_BOOT_PROGRESS:
             return BOOT_PROGRESS_LAYER;
+        case TYPE_HIDDEN_NAV_CONSUMER:
+            return HIDDEN_NAV_CONSUMER_LAYER;
         }
         Log.e(TAG, "Unknown window type: " + type);
         return APPLICATION_LAYER;
@@ -1646,6 +1663,46 @@
         }
     }
 
+    final InputHandler mHideNavInputHandler = new BaseInputHandler() {
+        @Override
+        public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
+            boolean handled = false;
+            try {
+                if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+                    if (event.getAction() == MotionEvent.ACTION_DOWN) {
+                        // When the user taps down, we re-show the nav bar.
+                        boolean changed = false;
+                        synchronized (mLock) {
+                            // Any user activity always causes us to show the navigation controls,
+                            // if they had been hidden.
+                            int newVal = mForceClearingStatusBarVisibility
+                                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+                            if (mForceClearingStatusBarVisibility != newVal) {
+                                mForceClearingStatusBarVisibility = newVal;
+                                changed = true;
+                            }
+                        }
+                        if (changed) {
+                            mWindowManagerFuncs.reevaluateStatusBarVisibility();
+                        }
+                    }
+                }
+            } finally {
+                finishedCallback.finished(handled);
+            }
+        }
+    };
+
+    @Override
+    public int adjustSystemUiVisibilityLw(int visibility) {
+        // Reset any bits in mForceClearingStatusBarVisibility that
+        // are now clear.
+        mForceClearingStatusBarVisibility &= visibility;
+        // Clear any bits in the new visibility that are currently being
+        // force cleared, before reporting it.
+        return visibility & ~mForceClearingStatusBarVisibility;
+    }
+
     public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect contentInset) {
         final int fl = attrs.flags;
         
@@ -1684,8 +1741,9 @@
 
         // decide where the status bar goes ahead of time
         if (mStatusBar != null) {
-            Rect navr = null;
             if (mNavigationBar != null) {
+                final boolean navVisible = mNavigationBar.isVisibleLw() &&
+                        (mLastSystemUiVisibility&View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
                 // Force the navigation bar to its appropriate place and
                 // size.  We need to do this directly, instead of relying on
                 // it to bubble up from the nav bar, because this needs to
@@ -1694,19 +1752,45 @@
                     // Portrait screen; nav bar goes on bottom.
                     mTmpNavigationFrame.set(0, displayHeight-mNavigationBarHeight,
                             displayWidth, displayHeight);
-                    if (mNavigationBar.isVisibleLw()) {
+                    if (navVisible) {
                         mDockBottom = mTmpNavigationFrame.top;
                         mRestrictedScreenHeight = mDockBottom - mDockTop;
+                    } else {
+                        // We currently want to hide the navigation UI.  Do this by just
+                        // moving it off the screen, so it can still receive input events
+                        // to know when to be re-shown.
+                        mTmpNavigationFrame.offset(0, mNavigationBarHeight);
                     }
                 } else {
                     // Landscape screen; nav bar goes to the right.
                     mTmpNavigationFrame.set(displayWidth-mNavigationBarWidth, 0,
                             displayWidth, displayHeight);
-                    if (mNavigationBar.isVisibleLw()) {
+                    if (navVisible) {
                         mDockRight = mTmpNavigationFrame.left;
                         mRestrictedScreenWidth = mDockRight - mDockLeft;
+                    } else {
+                        // We currently want to hide the navigation UI.  Do this by just
+                        // moving it off the screen, so it can still receive input events
+                        // to know when to be re-shown.
+                        mTmpNavigationFrame.offset(mNavigationBarWidth, 0);
                     }
                 }
+                // When the navigation bar isn't visible, we put up a fake
+                // input window to catch all touch events.  This way we can
+                // detect when the user presses anywhere to bring back the nav
+                // bar and ensure the application doesn't see the event.
+                if (navVisible) {
+                    if (mHideNavFakeWindow != null) {
+                        mHideNavFakeWindow.dismiss();
+                        mHideNavFakeWindow = null;
+                    }
+                } else if (mHideNavFakeWindow == null) {
+                    mHideNavFakeWindow = mWindowManagerFuncs.addFakeWindow(
+                            mHandler.getLooper(), mHideNavInputHandler,
+                            "hidden nav", WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER,
+                            0, false, false, true);
+                }
+                // And compute the final frame.
                 mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
                         mTmpNavigationFrame, mTmpNavigationFrame);
                 if (DEBUG_LAYOUT) Log.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
@@ -2214,7 +2298,11 @@
             }
         }
 
-        updateSystemUiVisibility();
+        if ((updateSystemUiVisibilityLw()&View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0) {
+            // If the navigation bar has been hidden or shown, we need to do another
+            // layout pass to update that window.
+            changes |= FINISH_LAYOUT_REDO_LAYOUT;
+        }
 
         // update since mAllowLockscreenWhenOn might have changed
         updateLockScreenTimeout();
@@ -2255,9 +2343,14 @@
         return true;
     }
 
-    public void focusChanged(WindowState lastFocus, WindowState newFocus) {
+    public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
         mFocusedWindow = newFocus;
-        updateSystemUiVisibility();
+        if ((updateSystemUiVisibilityLw()&View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0) {
+            // If the navigation bar has been hidden or shown, we need to do another
+            // layout pass to update that window.
+            return FINISH_LAYOUT_REDO_LAYOUT;
+        }
+        return 0;
     }
 
     /** {@inheritDoc} */
@@ -3200,6 +3293,17 @@
 
     /** {@inheritDoc} */
     public void userActivity() {
+        // ***************************************
+        // NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
+        // ***************************************
+        // THIS IS CALLED FROM DEEP IN THE POWER MANAGER
+        // WITH ITS LOCKS HELD.
+        //
+        // This code must be VERY careful about the locks
+        // it acquires.
+        // In fact, the current code acquires way too many,
+        // and probably has lurking deadlocks.
+
         synchronized (mScreenLockTimeout) {
             if (mLockScreenTimerActive) {
                 // reset the timer
@@ -3208,14 +3312,11 @@
             }
         }
 
-        if (mStatusBarService != null) {
-            try {
-                mStatusBarService.userActivity();
-            } catch (RemoteException ex) {}
-        }
-
-        synchronized (mLock) {
-            updateScreenSaverTimeoutLocked();
+        // Turn this off for now, screen savers not currently enabled.
+        if (false) {
+            synchronized (mLock) {
+                updateScreenSaverTimeoutLocked();
+            }
         }
     }
 
@@ -3257,6 +3358,9 @@
     private void updateScreenSaverTimeoutLocked() {
         if (mScreenSaverActivator == null) return;
 
+        // GAH...  acquiring a lock within a lock?  Please let's fix this.
+        // (Also note this is called from userActivity, with the power manager
+        // lock  held.  Not good.)
         synchronized (mScreenSaverActivator) {
             mHandler.removeCallbacks(mScreenSaverActivator);
             if (mScreenSaverEnabled && mScreenOnEarly && mScreenSaverTimeout > 0) {
@@ -3493,32 +3597,36 @@
         return mScreenOnEarly;
     }
 
-    private void updateSystemUiVisibility() {
+    private int updateSystemUiVisibilityLw() {
         // If there is no window focused, there will be nobody to handle the events
         // anyway, so just hang on in whatever state we're in until things settle down.
-        if (mFocusedWindow != null) {
-            final int visibility = mFocusedWindow.getSystemUiVisibility();
-            mHandler.post(new Runnable() {
-                    public void run() {
-                        if (mStatusBarService == null) {
-                            mStatusBarService = IStatusBarService.Stub.asInterface(
-                                    ServiceManager.getService("statusbar"));
-                        }
-                        if (mStatusBarService != null) {
-                            // need to assume status bar privileges to invoke lights on
-                            long origId = Binder.clearCallingIdentity();
-                            try {
-                                mStatusBarService.setSystemUiVisibility(visibility);
-                            } catch (RemoteException e) {
-                                // not much to be done
-                                mStatusBarService = null;
-                            } finally {
-                                Binder.restoreCallingIdentity(origId);
-                            }
+        if (mFocusedWindow == null) {
+            return 0;
+        }
+        final int visibility = mFocusedWindow.getSystemUiVisibility()
+                & ~mForceClearingStatusBarVisibility;
+        int diff = visibility ^ mLastSystemUiVisibility;
+        if (diff == 0) {
+            return 0;
+        }
+        mLastSystemUiVisibility = visibility;
+        mHandler.post(new Runnable() {
+                public void run() {
+                    if (mStatusBarService == null) {
+                        mStatusBarService = IStatusBarService.Stub.asInterface(
+                                ServiceManager.getService("statusbar"));
+                    }
+                    if (mStatusBarService != null) {
+                        try {
+                            mStatusBarService.setSystemUiVisibility(visibility);
+                        } catch (RemoteException e) {
+                            // not much to be done
+                            mStatusBarService = null;
                         }
                     }
-                });
-        }
+                }
+            });
+        return diff;
     }
 
     public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -3528,6 +3636,12 @@
         pw.print(prefix); pw.print("mLidOpen="); pw.print(mLidOpen);
                 pw.print(" mLidOpenRotation="); pw.print(mLidOpenRotation);
                 pw.print(" mHdmiPlugged="); pw.println(mHdmiPlugged);
+        if (mLastSystemUiVisibility != 0 || mForceClearingStatusBarVisibility != 0) {
+            pw.print(prefix); pw.print("mLastSystemUiVisibility=0x");
+                    pw.println(Integer.toHexString(mLastSystemUiVisibility));
+                    pw.print("  mForceClearingStatusBarVisibility=0x");
+                    pw.println(Integer.toHexString(mForceClearingStatusBarVisibility));
+        }
         pw.print(prefix); pw.print("mUiMode="); pw.print(mUiMode);
                 pw.print(" mDockMode="); pw.print(mDockMode);
                 pw.print(" mCarDockRotation="); pw.print(mCarDockRotation);
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index bab9f8a..a9ff6c5 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -117,11 +117,6 @@
     // ================================================================================
     // From IStatusBarService
     // ================================================================================
-    public void userActivity() {
-        if (mBar != null) try {
-            mBar.userActivity();
-        } catch (RemoteException ex) {}
-    }
     public void expand() {
         enforceExpandStatusBar();
 
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index f2e7485..25cc259 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -32,7 +32,6 @@
 import android.view.Surface;
 import android.view.View;
 import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
 
 import java.util.ArrayList;
 
diff --git a/services/java/com/android/server/wm/FakeWindowImpl.java b/services/java/com/android/server/wm/FakeWindowImpl.java
new file mode 100644
index 0000000..0e72f7d
--- /dev/null
+++ b/services/java/com/android/server/wm/FakeWindowImpl.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.os.Looper;
+import android.os.Process;
+import android.util.Slog;
+import android.view.InputChannel;
+import android.view.InputHandler;
+import android.view.InputQueue;
+import android.view.WindowManagerPolicy;
+
+public final class FakeWindowImpl implements WindowManagerPolicy.FakeWindow {
+    final WindowManagerService mService;
+    final InputChannel mServerChannel, mClientChannel;
+    final InputApplicationHandle mApplicationHandle;
+    final InputWindowHandle mWindowHandle;
+    final int mWindowLayer;
+
+    boolean mTouchFullscreen;
+
+    public FakeWindowImpl(WindowManagerService service, Looper looper, InputHandler inputHandler,
+            String name, int windowType, int layoutParamsFlags, boolean canReceiveKeys,
+            boolean hasFocus, boolean touchFullscreen) {
+        mService = service;
+
+        InputChannel[] channels = InputChannel.openInputChannelPair(name);
+        mServerChannel = channels[0];
+        mClientChannel = channels[1];
+        mService.mInputManager.registerInputChannel(mServerChannel, null);
+        InputQueue.registerInputChannel(mClientChannel, inputHandler, looper.getQueue());
+
+        mApplicationHandle = new InputApplicationHandle(null);
+        mApplicationHandle.name = name;
+        mApplicationHandle.dispatchingTimeoutNanos =
+                WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+
+        mWindowHandle = new InputWindowHandle(mApplicationHandle, null);
+        mWindowHandle.name = name;
+        mWindowHandle.inputChannel = mServerChannel;
+        mWindowLayer = getLayerLw(windowType);
+        mWindowHandle.layer = mWindowLayer;
+        mWindowHandle.layoutParamsFlags = layoutParamsFlags;
+        mWindowHandle.layoutParamsType = windowType;
+        mWindowHandle.dispatchingTimeoutNanos =
+                WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+        mWindowHandle.visible = true;
+        mWindowHandle.canReceiveKeys = canReceiveKeys;
+        mWindowHandle.hasFocus = hasFocus;
+        mWindowHandle.hasWallpaper = false;
+        mWindowHandle.paused = false;
+        mWindowHandle.ownerPid = Process.myPid();
+        mWindowHandle.ownerUid = Process.myUid();
+        mWindowHandle.inputFeatures = 0;
+        mWindowHandle.scaleFactor = 1.0f;
+
+        mTouchFullscreen = touchFullscreen;
+    }
+
+    void layout(int dw, int dh) {
+        if (mTouchFullscreen) {
+            mWindowHandle.touchableRegion.set(0, 0, dw, dh);
+        } else {
+            mWindowHandle.touchableRegion.setEmpty();
+        }
+        mWindowHandle.frameLeft = 0;
+        mWindowHandle.frameTop = 0;
+        mWindowHandle.frameRight = dw;
+        mWindowHandle.frameBottom = dh;
+    }
+
+    @Override
+    public void dismiss() {
+        synchronized (mService.mWindowMap) {
+            if (mService.removeFakeWindowLocked(this)) {
+                mService.mInputManager.unregisterInputChannel(mServerChannel);
+                InputQueue.unregisterInputChannel(mClientChannel);
+                mClientChannel.dispose();
+                mServerChannel.dispose();
+            }
+        }
+    }
+
+    private int getLayerLw(int windowType) {
+        return mService.mPolicy.windowTypeToLayerLw(windowType)
+                * WindowManagerService.TYPE_LAYER_MULTIPLIER
+                + WindowManagerService.TYPE_LAYER_OFFSET;
+    }
+}
diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java
index 573a7d42..9a559e0 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -169,6 +169,11 @@
             }
         }
 
+        final int NFW = mService.mFakeWindows.size();
+        for (int i = 0; i < NFW; i++) {
+            addInputWindowHandleLw(mService.mFakeWindows.get(i).mWindowHandle);
+        }
+
         final int N = windows.size();
         for (int i = N - 1; i >= 0; i--) {
             final WindowState child = windows.get(i);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 540c518..73a9601 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -118,6 +118,7 @@
 import android.view.WindowManagerImpl;
 import android.view.WindowManagerPolicy;
 import android.view.WindowManager.LayoutParams;
+import android.view.WindowManagerPolicy.FakeWindow;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Transformation;
@@ -142,7 +143,7 @@
 
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
-        implements Watchdog.Monitor {
+        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
     static final String TAG = "WindowManager";
     static final boolean DEBUG = false;
     static final boolean DEBUG_ADD_REMOVE = false;
@@ -352,6 +353,12 @@
     final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();
 
     /**
+     * Fake windows added to the window manager.  Note: ordered from top to
+     * bottom, opposite of mWindows.
+     */
+    final ArrayList<FakeWindowImpl> mFakeWindows = new ArrayList<FakeWindowImpl>();
+
+    /**
      * Windows that are being resized.  Used so we can tell the client about
      * the resize after closing the transaction in which we resized the
      * underlying surface.
@@ -442,7 +449,9 @@
     int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
     int mLayoutSeq = 0;
-    
+
+    int mLastStatusBarVisibility = 0;
+
     // State while inside of layoutAndPlaceSurfacesLocked().
     boolean mFocusMayChange;
     
@@ -702,7 +711,7 @@
             android.os.Process.setThreadPriority(
                     android.os.Process.THREAD_PRIORITY_FOREGROUND);
             android.os.Process.setCanSelfBackground(false);
-            mPolicy.init(mContext, mService, mPM);
+            mPolicy.init(mContext, mService, mService, mPM);
 
             synchronized (this) {
                 mRunning = true;
@@ -6368,8 +6377,6 @@
                                 // Ignore if process has died.
                             }
                         }
-
-                        mPolicy.focusChanged(lastFocus, newFocus);
                     }
                 } break;
 
@@ -7184,6 +7191,11 @@
         final int dw = mCurDisplayWidth;
         final int dh = mCurDisplayHeight;
 
+        final int NFW = mFakeWindows.size();
+        for (int i=0; i<NFW; i++) {
+            mFakeWindows.get(i).layout(dw, dh);
+        }
+
         final int N = mWindows.size();
         int i;
 
@@ -8835,6 +8847,7 @@
             final WindowState oldFocus = mCurrentFocus;
             mCurrentFocus = newFocus;
             mLosingFocus.remove(newFocus);
+            int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
 
             final WindowState imWindow = mInputMethodWindow;
             if (newFocus != imWindow && oldFocus != imWindow) {
@@ -8845,13 +8858,22 @@
                 }
                 if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                     performLayoutLockedInner(true /*initial*/, updateInputWindows);
+                    focusChanged &= ~WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
                 } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                     // Client will do the layout, but we need to assign layers
                     // for handleNewWindowLocked() below.
                     assignLayersLocked();
                 }
             }
-            
+
+            if ((focusChanged&WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+                // The change in focus caused us to need to do a layout.  Okay.
+                mLayoutNeeded = true;
+                if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
+                    performLayoutLockedInner(true /*initial*/, updateInputWindows);
+                }
+            }
+
             if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
                 // If we defer assigning layers, then the caller is responsible for
                 // doing this part.
@@ -9097,33 +9119,82 @@
 
     @Override
     public void statusBarVisibilityChanged(int visibility) {
-        mInputManager.setSystemUiVisibility(visibility);
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Caller does not hold permission "
+                    + android.Manifest.permission.STATUS_BAR);
+        }
 
         synchronized (mWindowMap) {
-            final int N = mWindows.size();
-            for (int i = 0; i < N; i++) {
-                WindowState ws = mWindows.get(i);
-                try {
-                    int curValue = ws.mSystemUiVisibility;
-                    int diff = curValue ^ visibility;
-                    // We are only interested in differences of one of the
-                    // clearable flags...
-                    diff &= View.SYSTEM_UI_CLEARABLE_FLAGS;
-                    // ...if it has actually been cleared.
-                    diff &= ~visibility;
-                    int newValue = (curValue&~diff) | (visibility&diff);
-                    if (newValue != curValue) {
-                        ws.mSeq++;
-                        ws.mSystemUiVisibility = newValue;
-                    }
-                    if (newValue != curValue || ws.mAttrs.hasSystemUiListeners) {
-                        ws.mClient.dispatchSystemUiVisibilityChanged(ws.mSeq,
-                                visibility, newValue, diff);
-                    }
-                } catch (RemoteException e) {
-                    // so sorry
+            mLastStatusBarVisibility = visibility;
+            visibility = mPolicy.adjustSystemUiVisibilityLw(visibility);
+            updateStatusBarVisibilityLocked(visibility);
+        }
+    }
+
+    void updateStatusBarVisibilityLocked(int visibility) {
+        mInputManager.setSystemUiVisibility(visibility);
+        final int N = mWindows.size();
+        for (int i = 0; i < N; i++) {
+            WindowState ws = mWindows.get(i);
+            try {
+                int curValue = ws.mSystemUiVisibility;
+                int diff = curValue ^ visibility;
+                // We are only interested in differences of one of the
+                // clearable flags...
+                diff &= View.SYSTEM_UI_CLEARABLE_FLAGS;
+                // ...if it has actually been cleared.
+                diff &= ~visibility;
+                int newValue = (curValue&~diff) | (visibility&diff);
+                if (newValue != curValue) {
+                    ws.mSeq++;
+                    ws.mSystemUiVisibility = newValue;
+                }
+                if (newValue != curValue || ws.mAttrs.hasSystemUiListeners) {
+                    ws.mClient.dispatchSystemUiVisibilityChanged(ws.mSeq,
+                            visibility, newValue, diff);
+                }
+            } catch (RemoteException e) {
+                // so sorry
+            }
+        }
+    }
+ 
+    @Override
+    public void reevaluateStatusBarVisibility() {
+        synchronized (mWindowMap) {
+            int visibility = mPolicy.adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
+            updateStatusBarVisibilityLocked(visibility);
+            performLayoutAndPlaceSurfacesLocked();
+        }
+    }
+
+    @Override
+    public FakeWindow addFakeWindow(Looper looper, InputHandler inputHandler,
+            String name, int windowType, int layoutParamsFlags, boolean canReceiveKeys,
+            boolean hasFocus, boolean touchFullscreen) {
+        synchronized (mWindowMap) {
+            FakeWindowImpl fw = new FakeWindowImpl(this, looper, inputHandler, name, windowType,
+                    layoutParamsFlags, canReceiveKeys, hasFocus, touchFullscreen);
+            int i=0;
+            while (i<mFakeWindows.size()) {
+                if (mFakeWindows.get(i).mWindowLayer <= fw.mWindowLayer) {
+                    break;
                 }
             }
+            mFakeWindows.add(i, fw);
+            mInputMonitor.updateInputWindowsLw(true);
+            return fw;
+        }
+    }
+
+    boolean removeFakeWindowLocked(FakeWindow window) {
+        synchronized (mWindowMap) {
+            if (mFakeWindows.remove(window)) {
+                mInputMonitor.updateInputWindowsLw(true);
+                return true;
+            }
+            return false;
         }
     }
 
@@ -9387,6 +9458,10 @@
         pw.print("  mInTouchMode="); pw.print(mInTouchMode);
                 pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
         if (dumpAll) {
+            if (mLastStatusBarVisibility != 0) {
+                pw.print("  mLastStatusBarVisibility=0x");
+                        pw.println(Integer.toHexString(mLastStatusBarVisibility));
+            }
             if (mInputMethodWindow != null) {
                 pw.print("  mInputMethodWindow="); pw.println(mInputMethodWindow);
             }