Fade out the mouse pointer after inactivity or other events.

Fades out the mouse pointer:
- after 15 seconds of inactivity normally
- after 3 seconds of inactivity in lights out mode
- after a non-modifier key down
- after a touch down

Extended the native Looper to support enqueuing time delayed
messages.  This is used by the PointerController to control
pointer fade timing.

Change-Id: I87792fea7dbe2d9376c78cf354fe3189a484d9da
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index a865d9f..c3c143c 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -399,6 +399,17 @@
     }
 }
 
+void InputReader::fadePointer() {
+    { // acquire device registry reader lock
+        RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+        for (size_t i = 0; i < mDevices.size(); i++) {
+            InputDevice* device = mDevices.valueAt(i);
+            device->fadePointer();
+        }
+    } // release device registry reader lock
+}
+
 void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) {
     { // acquire state lock
         AutoMutex _l(mStateLock);
@@ -695,6 +706,14 @@
     return result;
 }
 
+void InputDevice::fadePointer() {
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        mapper->fadePointer();
+    }
+}
+
 
 // --- InputMapper ---
 
@@ -739,6 +758,9 @@
     return 0;
 }
 
+void InputMapper::fadePointer() {
+}
+
 void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump,
         const RawAbsoluteAxisInfo& axis, const char* name) {
     if (axis.valid) {
@@ -967,6 +989,10 @@
         getContext()->updateGlobalMetaState();
     }
 
+    if (down && !isMetaKey(keyCode)) {
+        getContext()->fadePointer();
+    }
+
     if (policyFlags & POLICY_FLAG_FUNCTION) {
         newMetaState |= AMETA_FUNCTION_ON;
     }
@@ -1348,6 +1374,9 @@
         } else {
             hscroll = 0;
         }
+        if (hscroll != 0 || vscroll != 0) {
+            mPointerController->unfade();
+        }
     } // release lock
 
     int32_t metaState = mContext->getGlobalMetaState();
@@ -1376,6 +1405,13 @@
     }
 }
 
+void CursorInputMapper::fadePointer() {
+    { // acquire lock
+        AutoMutex _l(mLock);
+        mPointerController->fade();
+    } // release lock
+}
+
 
 // --- TouchInputMapper ---
 
@@ -2275,7 +2311,6 @@
     uint32_t policyFlags = 0;
 
     // Preprocess pointer data.
-
     if (mParameters.useBadTouchFilter) {
         if (applyBadTouchFilter()) {
             havePointerIds = false;
@@ -2303,8 +2338,12 @@
         savedTouch = & mCurrentTouch;
     }
 
-    // Process touches and virtual keys.
+    // Hide the pointer on an initial down.
+    if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) {
+        getContext()->fadePointer();
+    }
 
+    // Process touches and virtual keys.
     TouchResult touchResult = consumeOffScreenTouches(when, policyFlags);
     if (touchResult == DISPATCH_TOUCH) {
         detectGestures(when);
@@ -2312,7 +2351,6 @@
     }
 
     // Copy current touch to last touch in preparation for the next cycle.
-
     if (touchResult == DROP_STROKE) {
         mLastTouch.clear();
     } else {
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index cf41535..b344ffe 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -157,6 +157,8 @@
     virtual bool shouldDropVirtualKey(nsecs_t now,
             InputDevice* device, int32_t keyCode, int32_t scanCode) = 0;
 
+    virtual void fadePointer() = 0;
+
     virtual InputReaderPolicyInterface* getPolicy() = 0;
     virtual InputDispatcherInterface* getDispatcher() = 0;
     virtual EventHubInterface* getEventHub() = 0;
@@ -241,6 +243,8 @@
     virtual void updateGlobalMetaState();
     virtual int32_t getGlobalMetaState();
 
+    virtual void fadePointer();
+
     InputConfiguration mInputConfiguration;
     void updateInputConfiguration();
 
@@ -299,6 +303,8 @@
 
     int32_t getMetaState();
 
+    void fadePointer();
+
     inline const PropertyMap& getConfiguration() {
         return mConfiguration;
     }
@@ -351,6 +357,8 @@
 
     virtual int32_t getMetaState();
 
+    virtual void fadePointer();
+
 protected:
     InputDevice* mDevice;
     InputReaderContext* mContext;
@@ -459,6 +467,8 @@
 
     virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
 
+    virtual void fadePointer();
+
 private:
     // Amount that trackball needs to move in order to generate a key event.
     static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp
index 92af51e..954872b 100644
--- a/services/input/PointerController.cpp
+++ b/services/input/PointerController.cpp
@@ -35,8 +35,22 @@
 
 // --- PointerController ---
 
-PointerController::PointerController(int32_t pointerLayer) :
-    mPointerLayer(pointerLayer) {
+// Time to wait before starting the fade when the pointer is inactive.
+static const nsecs_t INACTIVITY_FADE_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds
+static const nsecs_t INACTIVITY_FADE_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds
+
+// Time to spend fading out the pointer completely.
+static const nsecs_t FADE_DURATION = 500 * 1000000LL; // 500 ms
+
+// Time to wait between frames.
+static const nsecs_t FADE_FRAME_INTERVAL = 1000000000LL / 60;
+
+// Amount to subtract from alpha per frame.
+static const float FADE_DECAY_PER_FRAME = float(FADE_FRAME_INTERVAL) / FADE_DURATION;
+
+
+PointerController::PointerController(const sp<Looper>& looper, int32_t pointerLayer) :
+        mLooper(looper), mPointerLayer(pointerLayer) {
     AutoMutex _l(mLock);
 
     mLocked.displayWidth = -1;
@@ -51,12 +65,19 @@
     mLocked.iconHotSpotX = 0;
     mLocked.iconHotSpotY = 0;
 
+    mLocked.fadeAlpha = 1;
+    mLocked.inactivityFadeDelay = INACTIVITY_FADE_DELAY_NORMAL;
+
     mLocked.wantVisible = false;
     mLocked.visible = false;
     mLocked.drawn = false;
+
+    mHandler = new WeakMessageHandler(this);
 }
 
 PointerController::~PointerController() {
+    mLooper->removeMessages(mHandler);
+
     if (mSurfaceControl != NULL) {
         mSurfaceControl->clear();
         mSurfaceControl.clear();
@@ -120,7 +141,7 @@
 
     if (mLocked.buttonState != buttonState) {
         mLocked.buttonState = buttonState;
-        mLocked.wantVisible = true;
+        unfadeBeforeUpdateLocked();
         updateLocked();
     }
 }
@@ -157,7 +178,7 @@
         } else {
             mLocked.pointerY = y;
         }
-        mLocked.wantVisible = true;
+        unfadeBeforeUpdateLocked();
         updateLocked();
     }
 }
@@ -169,6 +190,29 @@
     *outY = mLocked.pointerY;
 }
 
+void PointerController::fade() {
+    AutoMutex _l(mLock);
+
+    startFadeLocked();
+}
+
+void PointerController::unfade() {
+    AutoMutex _l(mLock);
+
+    if (unfadeBeforeUpdateLocked()) {
+        updateLocked();
+    }
+}
+
+void PointerController::setInactivityFadeDelay(InactivityFadeDelay inactivityFadeDelay) {
+    AutoMutex _l(mLock);
+
+    if (mLocked.inactivityFadeDelay != inactivityFadeDelay) {
+        mLocked.inactivityFadeDelay = inactivityFadeDelay;
+        startInactivityFadeDelayLocked();
+    }
+}
+
 void PointerController::updateLocked() {
     bool wantVisibleAndHavePointerIcon = mLocked.wantVisible && mLocked.iconBitmap;
 
@@ -201,6 +245,12 @@
             goto CloseTransaction;
         }
 
+        status = mSurfaceControl->setAlpha(mLocked.fadeAlpha);
+        if (status) {
+            LOGE("Error %d setting pointer surface alpha.", status);
+            goto CloseTransaction;
+        }
+
         if (!mLocked.visible) {
             status = mSurfaceControl->setLayer(mPointerLayer);
             if (status) {
@@ -412,4 +462,64 @@
     return true;
 }
 
+void PointerController::handleMessage(const Message& message) {
+    switch (message.what) {
+    case MSG_FADE_STEP: {
+        AutoMutex _l(mLock);
+        fadeStepLocked();
+        break;
+    }
+    }
+}
+
+bool PointerController::unfadeBeforeUpdateLocked() {
+    sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked());
+
+    if (isFadingLocked()) {
+        mLocked.wantVisible = true;
+        mLocked.fadeAlpha = 1;
+        return true; // update required to effect the unfade
+    }
+    return false; // update not required
+}
+
+void PointerController::startFadeLocked() {
+    if (!isFadingLocked()) {
+        sendFadeStepMessageDelayedLocked(0);
+    }
+}
+
+void PointerController::startInactivityFadeDelayLocked() {
+    if (!isFadingLocked()) {
+        sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked());
+    }
+}
+
+void PointerController::fadeStepLocked() {
+    if (mLocked.wantVisible) {
+        mLocked.fadeAlpha -= FADE_DECAY_PER_FRAME;
+        if (mLocked.fadeAlpha < 0) {
+            mLocked.fadeAlpha = 0;
+            mLocked.wantVisible = false;
+        } else {
+            sendFadeStepMessageDelayedLocked(FADE_FRAME_INTERVAL);
+        }
+        updateLocked();
+    }
+}
+
+bool PointerController::isFadingLocked() {
+    return !mLocked.wantVisible || mLocked.fadeAlpha != 1;
+}
+
+nsecs_t PointerController::getInactivityFadeDelayTimeLocked() {
+    return mLocked.inactivityFadeDelay == INACTIVITY_FADE_DELAY_SHORT
+            ? INACTIVITY_FADE_DELAY_TIME_SHORT : INACTIVITY_FADE_DELAY_TIME_NORMAL;
+}
+
+void PointerController::sendFadeStepMessageDelayedLocked(nsecs_t delayTime) {
+    mLooper->removeMessages(mHandler, MSG_FADE_STEP);
+    mLooper->sendMessageDelayed(delayTime, mHandler, Message(MSG_FADE_STEP));
+}
+
 } // namespace android
diff --git a/services/input/PointerController.h b/services/input/PointerController.h
index a2a9955..e28dd7d 100644
--- a/services/input/PointerController.h
+++ b/services/input/PointerController.h
@@ -18,7 +18,9 @@
 #define _UI_POINTER_CONTROLLER_H
 
 #include <ui/DisplayInfo.h>
+#include <ui/Input.h>
 #include <utils/RefBase.h>
+#include <utils/Looper.h>
 #include <utils/String8.h>
 
 #include <surfaceflinger/Surface.h>
@@ -64,6 +66,12 @@
 
     /* Gets the absolute location of the pointer. */
     virtual void getPosition(float* outX, float* outY) const = 0;
+
+    /* Fades the pointer out now. */
+    virtual void fade() = 0;
+
+    /* Makes the pointer visible if it has faded out. */
+    virtual void unfade() = 0;
 };
 
 
@@ -72,12 +80,17 @@
  *
  * Handles pointer acceleration and animation.
  */
-class PointerController : public PointerControllerInterface {
+class PointerController : public PointerControllerInterface, public MessageHandler {
 protected:
     virtual ~PointerController();
 
 public:
-    PointerController(int32_t pointerLayer);
+    enum InactivityFadeDelay {
+        INACTIVITY_FADE_DELAY_NORMAL = 0,
+        INACTIVITY_FADE_DELAY_SHORT = 1,
+    };
+
+    PointerController(const sp<Looper>& looper, int32_t pointerLayer);
 
     virtual bool getBounds(float* outMinX, float* outMinY,
             float* outMaxX, float* outMaxY) const;
@@ -86,14 +99,22 @@
     virtual uint32_t getButtonState() const;
     virtual void setPosition(float x, float y);
     virtual void getPosition(float* outX, float* outY) const;
+    virtual void fade();
+    virtual void unfade();
 
     void setDisplaySize(int32_t width, int32_t height);
     void setDisplayOrientation(int32_t orientation);
     void setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY);
+    void setInactivityFadeDelay(InactivityFadeDelay inactivityFadeDelay);
 
 private:
+    enum {
+        MSG_FADE_STEP = 0,
+    };
+
     mutable Mutex mLock;
 
+    sp<Looper> mLooper;
     int32_t mPointerLayer;
     sp<SurfaceComposerClient> mSurfaceComposerClient;
     sp<SurfaceControl> mSurfaceControl;
@@ -111,17 +132,31 @@
         float iconHotSpotX;
         float iconHotSpotY;
 
+        float fadeAlpha;
+        InactivityFadeDelay inactivityFadeDelay;
+
         bool wantVisible;
         bool visible;
         bool drawn;
     } mLocked;
 
+    sp<WeakMessageHandler> mHandler;
+
     bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
     void setPositionLocked(float x, float y);
     void updateLocked();
     bool createSurfaceIfNeededLocked();
     bool drawPointerIfNeededLocked();
     bool resizeSurfaceLocked(int32_t width, int32_t height);
+
+    void handleMessage(const Message& message);
+    bool unfadeBeforeUpdateLocked();
+    void startFadeLocked();
+    void startInactivityFadeDelayLocked();
+    void fadeStepLocked();
+    bool isFadingLocked();
+    nsecs_t getInactivityFadeDelayTimeLocked();
+    void sendFadeStepMessageDelayedLocked(nsecs_t delayTime);
 };
 
 } // namespace android
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index fac71bb..864241e 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -79,6 +79,12 @@
         *outX = 0;
         *outY = 0;
     }
+
+    virtual void fade() {
+    }
+
+    virtual void unfade() {
+    }
 };
 
 
@@ -743,6 +749,9 @@
             InputDevice* device, int32_t keyCode, int32_t scanCode) {
         return false;
     }
+
+    virtual void fadePointer() {
+    }
 };
 
 
@@ -875,6 +884,9 @@
     virtual int32_t getMetaState() {
         return mMetaState;
     }
+
+    virtual void fadePointer() {
+    }
 };
 
 
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index 80a2a96..326eca7 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -19,7 +19,6 @@
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -31,6 +30,8 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Environment;
+import android.os.Looper;
+import android.os.MessageQueue;
 import android.os.SystemProperties;
 import android.util.Slog;
 import android.util.Xml;
@@ -41,13 +42,10 @@
 import android.view.Surface;
 import android.view.WindowManager;
 
-import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
@@ -63,7 +61,7 @@
     private final Context mContext;
     private final WindowManagerService mWindowManagerService;
     
-    private static native void nativeInit(Callbacks callbacks);
+    private static native void nativeInit(Callbacks callbacks, MessageQueue messageQueue);
     private static native void nativeStart();
     private static native void nativeSetDisplaySize(int displayId, int width, int height);
     private static native void nativeSetDisplayOrientation(int displayId, int rotation);
@@ -83,6 +81,7 @@
             int injectorPid, int injectorUid, int syncMode, int timeoutMillis);
     private static native void nativeSetInputWindows(InputWindow[] windows);
     private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
+    private static native void nativeSetSystemUiVisibility(int visibility);
     private static native void nativeSetFocusedApplication(InputApplication application);
     private static native InputDevice nativeGetInputDevice(int deviceId);
     private static native void nativeGetInputConfiguration(Configuration configuration);
@@ -120,15 +119,12 @@
     public InputManager(Context context, WindowManagerService windowManagerService) {
         this.mContext = context;
         this.mWindowManagerService = windowManagerService;
-        
         this.mCallbacks = new Callbacks();
-        
-        init();
-    }
-    
-    private void init() {
+
+        Looper looper = windowManagerService.mH.getLooper();
+
         Slog.i(TAG, "Initializing input manager");
-        nativeInit(mCallbacks);
+        nativeInit(mCallbacks, looper.getQueue());
     }
     
     public void start() {
@@ -338,7 +334,11 @@
     public void setInputDispatchMode(boolean enabled, boolean frozen) {
         nativeSetInputDispatchMode(enabled, frozen);
     }
-    
+
+    public void setSystemUiVisibility(int visibility) {
+        nativeSetSystemUiVisibility(visibility);
+    }
+
     /**
      * Atomically transfers touch focus from one window to another as identified by
      * their input channels.  It is possible for multiple windows to have
@@ -361,7 +361,7 @@
         }
         return nativeTransferTouchFocus(fromChannel, toChannel);
     }
-    
+
     public void dump(PrintWriter pw) {
         String dumpStr = nativeDump();
         if (dumpStr != null) {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index e3218c8..15269dc 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -8412,6 +8412,7 @@
 
     @Override
     public void statusBarVisibilityChanged(int visibility) {
+        mInputManager.setSystemUiVisibility(visibility);
         synchronized (mWindowMap) {
             final int N = mWindows.size();
             for (int i = 0; i < N; i++) {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 0a50ff8..5ef234a 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -31,11 +31,13 @@
 #include <android_runtime/AndroidRuntime.h>
 
 #include <utils/Log.h>
+#include <utils/Looper.h>
 #include <utils/threads.h>
 
 #include <input/InputManager.h>
 #include <input/PointerController.h>
 
+#include <android_os_MessageQueue.h>
 #include <android_view_KeyEvent.h>
 #include <android_view_MotionEvent.h>
 #include <android_view_InputChannel.h>
@@ -136,7 +138,7 @@
     virtual ~NativeInputManager();
 
 public:
-    NativeInputManager(jobject callbacksObj);
+    NativeInputManager(jobject callbacksObj, const sp<Looper>& looper);
 
     inline sp<InputManager> getInputManager() const { return mInputManager; }
 
@@ -152,6 +154,7 @@
     void setInputWindows(JNIEnv* env, jobjectArray windowObjArray);
     void setFocusedApplication(JNIEnv* env, jobject applicationObj);
     void setInputDispatchMode(bool enabled, bool frozen);
+    void setSystemUiVisibility(int32_t visibility);
 
     /* --- InputReaderPolicyInterface implementation --- */
 
@@ -188,6 +191,7 @@
     sp<InputManager> mInputManager;
 
     jobject mCallbacksObj;
+    sp<Looper> mLooper;
 
     // Cached filtering policies.
     int32_t mFilterTouchEvents;
@@ -203,10 +207,15 @@
         int32_t displayWidth, displayHeight; // -1 when initialized
         int32_t displayOrientation;
 
+        // System UI visibility.
+        int32_t systemUiVisibility;
+
         // Pointer controller singleton, created and destroyed as needed.
         wp<PointerController> pointerController;
     } mLocked;
 
+    void updateInactivityFadeDelayLocked(const sp<PointerController>& controller);
+
     // Power manager interactions.
     bool isScreenOn();
     bool isScreenBright();
@@ -220,9 +229,10 @@
 
 
 
-NativeInputManager::NativeInputManager(jobject callbacksObj) :
-    mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mVirtualKeyQuietTime(-1),
-    mMaxEventsPerSecond(-1) {
+NativeInputManager::NativeInputManager(jobject callbacksObj, const sp<Looper>& looper) :
+        mLooper(looper),
+        mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mVirtualKeyQuietTime(-1),
+        mMaxEventsPerSecond(-1) {
     JNIEnv* env = jniEnv();
 
     mCallbacksObj = env->NewGlobalRef(callbacksObj);
@@ -232,6 +242,8 @@
         mLocked.displayWidth = -1;
         mLocked.displayHeight = -1;
         mLocked.displayOrientation = ROTATION_0;
+
+        mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
     }
 
     sp<EventHub> eventHub = new EventHub();
@@ -408,7 +420,7 @@
             layer = -1;
         }
 
-        controller = new PointerController(layer);
+        controller = new PointerController(mLooper, layer);
         mLocked.pointerController = controller;
 
         controller->setDisplaySize(mLocked.displayWidth, mLocked.displayHeight);
@@ -428,6 +440,8 @@
             }
             env->DeleteLocalRef(iconObj);
         }
+
+        updateInactivityFadeDelayLocked(controller);
     }
     return controller;
 }
@@ -571,6 +585,26 @@
     mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen);
 }
 
+void NativeInputManager::setSystemUiVisibility(int32_t visibility) {
+    AutoMutex _l(mLock);
+
+    if (mLocked.systemUiVisibility != visibility) {
+        mLocked.systemUiVisibility = visibility;
+
+        sp<PointerController> controller = mLocked.pointerController.promote();
+        if (controller != NULL) {
+            updateInactivityFadeDelayLocked(controller);
+        }
+    }
+}
+
+void NativeInputManager::updateInactivityFadeDelayLocked(const sp<PointerController>& controller) {
+    bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
+    controller->setInactivityFadeDelay(lightsOut
+            ? PointerController::INACTIVITY_FADE_DELAY_SHORT
+            : PointerController::INACTIVITY_FADE_DELAY_NORMAL);
+}
+
 bool NativeInputManager::isScreenOn() {
     return android_server_PowerManagerService_isScreenOn();
 }
@@ -751,9 +785,10 @@
 }
 
 static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,
-        jobject callbacks) {
+        jobject callbacks, jobject messageQueueObj) {
     if (gNativeInputManager == NULL) {
-        gNativeInputManager = new NativeInputManager(callbacks);
+        sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
+        gNativeInputManager = new NativeInputManager(callbacks, looper);
     } else {
         LOGE("Input manager already initialized.");
         jniThrowRuntimeException(env, "Input manager already initialized.");
@@ -972,6 +1007,15 @@
     gNativeInputManager->setInputDispatchMode(enabled, frozen);
 }
 
+static void android_server_InputManager_nativeSetSystemUiVisibility(JNIEnv* env,
+        jclass clazz, jint visibility) {
+    if (checkInputManagerUnitialized(env)) {
+        return;
+    }
+
+    gNativeInputManager->setSystemUiVisibility(visibility);
+}
+
 static jobject android_server_InputManager_nativeGetInputDevice(JNIEnv* env,
         jclass clazz, jint deviceId) {
     if (checkInputManagerUnitialized(env)) {
@@ -1079,7 +1123,7 @@
 
 static JNINativeMethod gInputManagerMethods[] = {
     /* name, signature, funcPtr */
-    { "nativeInit", "(Lcom/android/server/wm/InputManager$Callbacks;)V",
+    { "nativeInit", "(Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V",
             (void*) android_server_InputManager_nativeInit },
     { "nativeStart", "()V",
             (void*) android_server_InputManager_nativeStart },
@@ -1108,6 +1152,8 @@
             (void*) android_server_InputManager_nativeSetFocusedApplication },
     { "nativeSetInputDispatchMode", "(ZZ)V",
             (void*) android_server_InputManager_nativeSetInputDispatchMode },
+    { "nativeSetSystemUiVisibility", "(I)V",
+            (void*) android_server_InputManager_nativeSetSystemUiVisibility },
     { "nativeGetInputDevice", "(I)Landroid/view/InputDevice;",
             (void*) android_server_InputManager_nativeGetInputDevice },
     { "nativeGetInputDeviceIds", "()[I",