Fix how we hide and show the nav bar.

The PhoneWindowManager is now responsible for hiding and showing
the nav bar.

For hiding, it just moves it off the screen (easy way to get a
nice slide animation on and off).  At the same time, we use a
new WM facility to put up a fake input window to capture all
touch events.

When a touch event is received, we force the system UI to clear
the navigation hiding bit so it will be shown again.

This removes a bunch of code from the system UI for hiding and
showing the nav bar.  Also removes the code calling from userActivity()
to the system UI, which was bad.  (Also no longer using userActivity()
fixes bugs around re-showing the nav bar due to key presses and
other wrong things.)

Change-Id: I8c3174873b5bcaa36a92322a51e8f7993e88e551
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);
             }