resolved conflicts for merge of 9e660c82 to master

Change-Id: Ic4bd85cbaa5b9a10dcb474a0dad46490bf967e43
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
new file mode 100644
index 0000000..72c4166
--- /dev/null
+++ b/services/java/com/android/server/InputManager.java
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2010 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;
+
+import com.android.internal.util.XmlUtils;
+import com.android.server.KeyInputQueue.VirtualKey;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Environment;
+import android.os.LocalPowerManager;
+import android.os.PowerManager;
+import android.util.Log;
+import android.util.Slog;
+import android.util.Xml;
+import android.view.InputChannel;
+import android.view.InputTarget;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.RawInputEvent;
+import android.view.Surface;
+import android.view.WindowManagerPolicy;
+
+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;
+
+/*
+ * Wraps the C++ InputManager and provides its callbacks.
+ * 
+ * XXX Tempted to promote this to a first-class service, ie. InputManagerService, to
+ *     improve separation of concerns with respect to the window manager.
+ */
+public class InputManager {
+    static final String TAG = "InputManager";
+    
+    private final Callbacks mCallbacks;
+    private final Context mContext;
+    private final WindowManagerService mWindowManagerService;
+    private final WindowManagerPolicy mWindowManagerPolicy;
+    private final PowerManager mPowerManager;
+    private final PowerManagerService mPowerManagerService;
+    
+    private int mTouchScreenConfig;
+    private int mKeyboardConfig;
+    private int mNavigationConfig;
+    
+    private static native void nativeInit(Callbacks callbacks);
+    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);
+    
+    private static native int nativeGetScanCodeState(int deviceId, int deviceClasses,
+            int scanCode);
+    private static native int nativeGetKeyCodeState(int deviceId, int deviceClasses,
+            int keyCode);
+    private static native int nativeGetSwitchState(int deviceId, int deviceClasses,
+            int sw);
+    private static native boolean nativeHasKeys(int[] keyCodes, boolean[] keyExists);
+    private static native void nativeRegisterInputChannel(InputChannel inputChannel);
+    private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
+    
+    // Device class as defined by EventHub.
+    private static final int CLASS_KEYBOARD = 0x00000001;
+    private static final int CLASS_ALPHAKEY = 0x00000002;
+    private static final int CLASS_TOUCHSCREEN = 0x00000004;
+    private static final int CLASS_TRACKBALL = 0x00000008;
+    private static final int CLASS_TOUCHSCREEN_MT = 0x00000010;
+    private static final int CLASS_DPAD = 0x00000020;
+    
+    public InputManager(Context context,
+            WindowManagerService windowManagerService,
+            WindowManagerPolicy windowManagerPolicy,
+            PowerManager powerManager,
+            PowerManagerService powerManagerService) {
+        this.mContext = context;
+        this.mWindowManagerService = windowManagerService;
+        this.mWindowManagerPolicy = windowManagerPolicy;
+        this.mPowerManager = powerManager;
+        this.mPowerManagerService = powerManagerService;
+        
+        this.mCallbacks = new Callbacks();
+        
+        mTouchScreenConfig = Configuration.TOUCHSCREEN_NOTOUCH;
+        mKeyboardConfig = Configuration.KEYBOARD_NOKEYS;
+        mNavigationConfig = Configuration.NAVIGATION_NONAV;
+        
+        init();
+    }
+    
+    private void init() {
+        Slog.i(TAG, "Initializing input manager");
+        nativeInit(mCallbacks);
+    }
+    
+    public void start() {
+        Slog.i(TAG, "Starting input manager");
+        nativeStart();
+    }
+    
+    public void setDisplaySize(int displayId, int width, int height) {
+        if (width <= 0 || height <= 0) {
+            throw new IllegalArgumentException("Invalid display id or dimensions.");
+        }
+        
+        Slog.i(TAG, "Setting display #" + displayId + " size to " + width + "x" + height);
+        nativeSetDisplaySize(displayId, width, height);
+    }
+    
+    public void setDisplayOrientation(int displayId, int rotation) {
+        if (rotation < Surface.ROTATION_0 || rotation > Surface.ROTATION_270) {
+            throw new IllegalArgumentException("Invalid rotation.");
+        }
+        
+        Slog.i(TAG, "Setting display #" + displayId + " orientation to " + rotation);
+        nativeSetDisplayOrientation(displayId, rotation);
+    }
+    
+    public void getInputConfiguration(Configuration config) {
+        if (config == null) {
+            throw new IllegalArgumentException("config must not be null.");
+        }
+        
+        config.touchscreen = mTouchScreenConfig;
+        config.keyboard = mKeyboardConfig;
+        config.navigation = mNavigationConfig;
+    }
+    
+    public int getScancodeState(int code) {
+        return nativeGetScanCodeState(0, -1, code);
+    }
+    
+    public int getScancodeState(int deviceId, int code) {
+        return nativeGetScanCodeState(deviceId, -1, code);
+    }
+    
+    public int getTrackballScancodeState(int code) {
+        return nativeGetScanCodeState(-1, CLASS_TRACKBALL, code);
+    }
+    
+    public int getDPadScancodeState(int code) {
+        return nativeGetScanCodeState(-1, CLASS_DPAD, code);
+    }
+    
+    public int getKeycodeState(int code) {
+        return nativeGetKeyCodeState(0, -1, code);
+    }
+    
+    public int getKeycodeState(int deviceId, int code) {
+        return nativeGetKeyCodeState(deviceId, -1, code);
+    }
+    
+    public int getTrackballKeycodeState(int code) {
+        return nativeGetKeyCodeState(-1, CLASS_TRACKBALL, code);
+    }
+    
+    public int getDPadKeycodeState(int code) {
+        return nativeGetKeyCodeState(-1, CLASS_DPAD, code);
+    }
+
+    public int getSwitchState(int sw) {
+        return nativeGetSwitchState(-1, -1, sw);
+    }
+    
+    public int getSwitchState(int deviceId, int sw) {
+        return nativeGetSwitchState(deviceId, -1, sw);
+    }
+
+    public boolean hasKeys(int[] keyCodes, boolean[] keyExists) {
+        if (keyCodes == null) {
+            throw new IllegalArgumentException("keyCodes must not be null.");
+        }
+        if (keyExists == null) {
+            throw new IllegalArgumentException("keyExists must not be null.");
+        }
+        
+        return nativeHasKeys(keyCodes, keyExists);
+    }
+    
+    public void registerInputChannel(InputChannel inputChannel) {
+        if (inputChannel == null) {
+            throw new IllegalArgumentException("inputChannel must not be null.");
+        }
+        
+        nativeRegisterInputChannel(inputChannel);
+    }
+    
+    public void unregisterInputChannel(InputChannel inputChannel) {
+        if (inputChannel == null) {
+            throw new IllegalArgumentException("inputChannel must not be null.");
+        }
+        
+        nativeUnregisterInputChannel(inputChannel);
+    }
+    
+    // TBD where this really belongs, duplicate copy in WindowManagerService
+    static final int INJECT_FAILED = 0;
+    static final int INJECT_SUCCEEDED = 1;
+    static final int INJECT_NO_PERMISSION = -1;
+    
+    /**
+     * Injects a key event into the event system on behalf of an application.
+     * @param event The event to inject.
+     * @param nature The nature of the event.
+     * @param sync If true, waits for the event to be completed before returning.
+     * @param pid The pid of the injecting application.
+     * @param uid The uid of the injecting application.
+     * @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION
+     */
+    public int injectKeyEvent(KeyEvent event, int nature, boolean sync, int pid, int uid) {
+        // TODO
+        return INJECT_FAILED;
+    }
+    
+    /**
+     * Injects a motion event into the event system on behalf of an application.
+     * @param event The event to inject.
+     * @param nature The nature of the event.
+     * @param sync If true, waits for the event to be completed before returning.
+     * @param pid The pid of the injecting application.
+     * @param uid The uid of the injecting application.
+     * @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION
+     */
+    public int injectMotionEvent(MotionEvent event, int nature, boolean sync, int pid, int uid) {
+        // TODO
+        return INJECT_FAILED;
+    }
+    
+    public void dump(PrintWriter pw) {
+        // TODO
+    }
+    
+    private static final class VirtualKeyDefinition {
+        public int scanCode;
+        
+        // configured position data, specified in display coords
+        public int centerX;
+        public int centerY;
+        public int width;
+        public int height;
+    }
+    
+    /*
+     * Callbacks from native.
+     */
+    private class Callbacks {
+        static final String TAG = "InputManager-Callbacks";
+        
+        private static final boolean DEBUG_VIRTUAL_KEYS = false;
+        private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
+        
+        private final InputTargetList mReusableInputTargetList = new InputTargetList();
+        
+        @SuppressWarnings("unused")
+        public boolean isScreenOn() {
+            return mPowerManagerService.isScreenOn();
+        }
+        
+        @SuppressWarnings("unused")
+        public boolean isScreenBright() {
+            return mPowerManagerService.isScreenBright();
+        }
+        
+        @SuppressWarnings("unused")
+        public void virtualKeyFeedback(long whenNanos, int deviceId, int action, int flags,
+                int keyCode, int scanCode, int metaState, long downTimeNanos) {
+            KeyEvent keyEvent = new KeyEvent(downTimeNanos / 1000000,
+                    whenNanos / 1000000, action, keyCode, 0, metaState, scanCode, deviceId,
+                    flags);
+            
+            mWindowManagerService.virtualKeyFeedback(keyEvent);
+        }
+        
+        @SuppressWarnings("unused")
+        public void notifyConfigurationChanged(long whenNanos,
+                int touchScreenConfig, int keyboardConfig, int navigationConfig) {
+            mTouchScreenConfig = touchScreenConfig;
+            mKeyboardConfig = keyboardConfig;
+            mNavigationConfig = navigationConfig;
+            
+            mWindowManagerService.sendNewConfiguration();
+        }
+        
+        @SuppressWarnings("unused")
+        public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
+            mWindowManagerPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
+        }
+        
+        @SuppressWarnings("unused")
+        public int hackInterceptKey(int deviceId, int type, int scanCode,
+                int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) {
+            RawInputEvent event = new RawInputEvent();
+            event.deviceId = deviceId;
+            event.type = type;
+            event.scancode = scanCode;
+            event.keycode = keyCode;
+            event.flags = policyFlags;
+            event.value = value;
+            event.when = whenNanos / 1000000;
+            
+            return mWindowManagerPolicy.interceptKeyTq(event, isScreenOn);
+        }
+        
+        @SuppressWarnings("unused")
+        public void goToSleep(long whenNanos) {
+            long when = whenNanos / 1000000;
+            mPowerManager.goToSleep(when);
+        }
+        
+        @SuppressWarnings("unused")
+        public void pokeUserActivityForKey(long whenNanos) {
+            long when = whenNanos / 1000000;
+            mPowerManagerService.userActivity(when, false,
+                    LocalPowerManager.BUTTON_EVENT, false);
+        }
+        
+        @SuppressWarnings("unused")
+        public void notifyAppSwitchComing() {
+            mWindowManagerService.mKeyWaiter.appSwitchComing();
+        }
+        
+        @SuppressWarnings("unused")
+        public boolean filterTouchEvents() {
+            return mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_filterTouchEvents);
+        }
+        
+        @SuppressWarnings("unused")
+        public boolean filterJumpyTouchEvents() {
+            return mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_filterJumpyTouchEvents);
+        }
+        
+        @SuppressWarnings("unused")
+        public VirtualKeyDefinition[] getVirtualKeyDefinitions(String deviceName) {
+            ArrayList<VirtualKeyDefinition> keys = new ArrayList<VirtualKeyDefinition>();
+            
+            try {
+                FileInputStream fis = new FileInputStream(
+                        "/sys/board_properties/virtualkeys." + deviceName);
+                InputStreamReader isr = new InputStreamReader(fis);
+                BufferedReader br = new BufferedReader(isr, 2048);
+                String str = br.readLine();
+                if (str != null) {
+                    String[] it = str.split(":");
+                    if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
+                    final int N = it.length-6;
+                    for (int i=0; i<=N; i+=6) {
+                        if (!"0x01".equals(it[i])) {
+                            Slog.w(TAG, "Unknown virtual key type at elem #" + i
+                                    + ": " + it[i]);
+                            continue;
+                        }
+                        try {
+                            VirtualKeyDefinition key = new VirtualKeyDefinition();
+                            key.scanCode = Integer.parseInt(it[i+1]);
+                            key.centerX = Integer.parseInt(it[i+2]);
+                            key.centerY = Integer.parseInt(it[i+3]);
+                            key.width = Integer.parseInt(it[i+4]);
+                            key.height = Integer.parseInt(it[i+5]);
+                            if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
+                                    + key.scanCode + ": center=" + key.centerX + ","
+                                    + key.centerY + " size=" + key.width + "x"
+                                    + key.height);
+                            keys.add(key);
+                        } catch (NumberFormatException e) {
+                            Slog.w(TAG, "Bad number at region " + i + " in: "
+                                    + str, e);
+                        }
+                    }
+                }
+                br.close();
+            } catch (FileNotFoundException e) {
+                Slog.i(TAG, "No virtual keys found");
+            } catch (IOException e) {
+                Slog.w(TAG, "Error reading virtual keys", e);
+            }
+            
+            return keys.toArray(new VirtualKeyDefinition[keys.size()]);
+        }
+        
+        @SuppressWarnings("unused")
+        public String[] getExcludedDeviceNames() {
+            ArrayList<String> names = new ArrayList<String>();
+            
+            // Read partner-provided list of excluded input devices
+            XmlPullParser parser = null;
+            // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
+            File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
+            FileReader confreader = null;
+            try {
+                confreader = new FileReader(confFile);
+                parser = Xml.newPullParser();
+                parser.setInput(confreader);
+                XmlUtils.beginDocument(parser, "devices");
+
+                while (true) {
+                    XmlUtils.nextElement(parser);
+                    if (!"device".equals(parser.getName())) {
+                        break;
+                    }
+                    String name = parser.getAttributeValue(null, "name");
+                    if (name != null) {
+                        names.add(name);
+                    }
+                }
+            } catch (FileNotFoundException e) {
+                // It's ok if the file does not exist.
+            } catch (Exception e) {
+                Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
+            } finally {
+                try { if (confreader != null) confreader.close(); } catch (IOException e) { }
+            }
+            
+            return names.toArray(new String[names.size()]);
+        }
+        
+        @SuppressWarnings("unused")
+        public InputTarget[] getKeyEventTargets(KeyEvent event, int nature, int policyFlags) {
+            mReusableInputTargetList.clear();
+            
+            mWindowManagerService.getKeyEventTargets(mReusableInputTargetList,
+                    event, nature, policyFlags);
+            
+            return mReusableInputTargetList.toNullTerminatedArray();
+        }
+        
+        @SuppressWarnings("unused")
+        public InputTarget[] getMotionEventTargets(MotionEvent event, int nature, int policyFlags) {
+            mReusableInputTargetList.clear();
+            
+            mWindowManagerService.getMotionEventTargets(mReusableInputTargetList,
+                    event, nature, policyFlags);
+            
+            return mReusableInputTargetList.toNullTerminatedArray();
+        }
+    }
+}
diff --git a/services/java/com/android/server/InputTargetList.java b/services/java/com/android/server/InputTargetList.java
new file mode 100644
index 0000000..1575612
--- /dev/null
+++ b/services/java/com/android/server/InputTargetList.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 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;
+
+import android.view.InputChannel;
+import android.view.InputTarget;
+
+/**
+ * A specialized list of input targets backed by an array.
+ * 
+ * This class is part of an InputManager optimization to avoid allocating and copying
+ * input target arrays unnecessarily on return from JNI callbacks.  Internally, it keeps
+ * an array full of demand-allocated InputTarget objects that it recycles each time the
+ * list is cleared.  The used portion of the array is padded with a null.
+ * 
+ * @hide
+ */
+public class InputTargetList {
+    private InputTarget[] mArray;
+    private int mCount;
+    
+    /**
+     * Creates an empty input target list.
+     */
+    public InputTargetList() {
+        mArray = new InputTarget[8];
+    }
+    
+    /**
+     * Clears the input target list.
+     */
+    public void clear() {
+        if (mCount == 0) {
+            return;
+        }
+        
+        int count = mCount;
+        mCount = 0;
+        mArray[count] = mArray[0];
+        while (count > 0) {
+            count -= 1;
+            mArray[count].recycle();
+        }
+        // mArray[0] could be set to null here but we do it in toNullTerminatedArray()
+    }
+    
+    /**
+     * Adds a new input target to the input target list.
+     * @param inputChannel The input channel of the target window.
+     * @param flags Input target flags.
+     * @param timeoutNanos The input dispatch timeout (before ANR) in nanoseconds or -1 if none.
+     * @param xOffset An offset to add to motion X coordinates during delivery.
+     * @param yOffset An offset to add to motion Y coordinates during delivery.
+     */
+    public void add(InputChannel inputChannel, int flags, long timeoutNanos,
+            float xOffset, float yOffset) {
+        if (inputChannel == null) {
+            throw new IllegalArgumentException("inputChannel must not be null");
+        }
+        
+        if (mCount + 1 == mArray.length) {
+            InputTarget[] oldArray = mArray;
+            mArray = new InputTarget[oldArray.length * 2];
+            System.arraycopy(oldArray, 0, mArray, 0, mCount);
+        }
+        
+        // Grab InputTarget from tail (after used section) if available.
+        InputTarget inputTarget = mArray[mCount + 1];
+        if (inputTarget == null) {
+            inputTarget = new InputTarget();
+        }
+        inputTarget.mInputChannel = inputChannel;
+        inputTarget.mFlags = flags;
+        inputTarget.mTimeoutNanos = timeoutNanos;
+        inputTarget.mXOffset = xOffset;
+        inputTarget.mYOffset = yOffset;
+        
+        mArray[mCount] = inputTarget;
+        mCount += 1;
+        // mArray[mCount] could be set to null here but we do it in toNullTerminatedArray()
+    }
+    
+    /**
+     * Gets the input targets as a null-terminated array.
+     * @return The input target array.
+     */
+    public InputTarget[] toNullTerminatedArray() {
+        mArray[mCount] = null;
+        return mArray;
+    }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
index f30346b..f62c7ee 100644
--- a/services/java/com/android/server/KeyInputQueue.java
+++ b/services/java/com/android/server/KeyInputQueue.java
@@ -298,7 +298,9 @@
         
         mHapticFeedbackCallback = hapticFeedbackCallback;
         
-        readExcludedDevices();
+        if (! WindowManagerService.ENABLE_NATIVE_INPUT_DISPATCH) {
+            readExcludedDevices();
+        }
         
         PowerManager pm = (PowerManager)context.getSystemService(
                                                         Context.POWER_SERVICE);
@@ -311,7 +313,9 @@
         mFirst.next = mLast;
         mLast.prev = mFirst;
 
-        mThread.start();
+        if (! WindowManagerService.ENABLE_NATIVE_INPUT_DISPATCH) {
+            mThread.start();
+        }
     }
 
     public void setDisplay(Display display) {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 97d0f9b..503dbb9 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -101,6 +101,9 @@
 import android.view.IWindow;
 import android.view.IWindowManager;
 import android.view.IWindowSession;
+import android.view.InputChannel;
+import android.view.InputQueue;
+import android.view.InputTarget;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.RawInputEvent;
@@ -156,6 +159,8 @@
     static final boolean SHOW_TRANSACTIONS = false;
     static final boolean HIDE_STACK_CRAWLS = true;
     static final boolean MEASURE_LATENCY = false;
+    static final boolean ENABLE_NATIVE_INPUT_DISPATCH =
+        WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH;
     static private LatencyTimer lt;
 
     static final boolean PROFILE_ORIENTATION = false;
@@ -496,10 +501,12 @@
 
     final KeyWaiter mKeyWaiter = new KeyWaiter();
     final KeyQ mQueue;
+    final InputManager mInputManager;
     final InputDispatcherThread mInputThread;
 
     // Who is holding the screen on.
     Session mHoldingScreenOn;
+    PowerManager.WakeLock mHoldingScreenWakeLock;
 
     boolean mTurnOnScreen;
 
@@ -649,8 +656,16 @@
         }
         mMinWaitTimeBetweenTouchEvents = 1000 / max_events_per_sec;
 
-        mQueue = new KeyQ();
+        mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
+                "KEEP_SCREEN_ON_FLAG");
+        mHoldingScreenWakeLock.setReferenceCounted(false);
 
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            mInputManager = new InputManager(context, this, mPolicy, pmc, mPowerManager);
+        } else {
+            mInputManager = null;
+        }
+        mQueue = new KeyQ();
         mInputThread = new InputDispatcherThread();
 
         PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
@@ -665,7 +680,11 @@
             }
         }
 
-        mInputThread.start();
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            mInputManager.start();
+        } else {
+            mInputThread.start();
+        }
 
         // Add ourself to the Watchdog monitors.
         Watchdog.getInstance().addMonitor(this);
@@ -1858,7 +1877,7 @@
     
     public int addWindow(Session session, IWindow client,
             WindowManager.LayoutParams attrs, int viewVisibility,
-            Rect outContentInsets) {
+            Rect outContentInsets, InputChannel outInputChannel) {
         int res = mPolicy.checkAddPermission(attrs);
         if (res != WindowManagerImpl.ADD_OKAY) {
             return res;
@@ -1877,7 +1896,12 @@
                 mDisplay = wm.getDefaultDisplay();
                 mInitialDisplayWidth = mDisplay.getWidth();
                 mInitialDisplayHeight = mDisplay.getHeight();
-                mQueue.setDisplay(mDisplay);
+                if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                    mInputManager.setDisplaySize(0,
+                            mInitialDisplayWidth, mInitialDisplayHeight);
+                } else {
+                    mQueue.setDisplay(mDisplay);
+                }
                 reportNewConfig = true;
             }
 
@@ -1970,6 +1994,17 @@
             if (res != WindowManagerImpl.ADD_OKAY) {
                 return res;
             }
+            
+            if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                if (outInputChannel != null) {
+                    String name = win.makeInputChannelName();
+                    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
+                    win.mInputChannel = inputChannels[0];
+                    inputChannels[1].transferToBinderOutParameter(outInputChannel);
+                    
+                    mInputManager.registerInputChannel(win.mInputChannel);
+                }
+            }
 
             // From now on, no exceptions or errors allowed!
 
@@ -4353,7 +4388,11 @@
                 "getSwitchState()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return KeyInputQueue.getSwitchState(sw);
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            return mInputManager.getSwitchState(sw);
+        } else {
+            return KeyInputQueue.getSwitchState(sw);
+        }
     }
 
     public int getSwitchStateForDevice(int devid, int sw) {
@@ -4361,7 +4400,11 @@
                 "getSwitchStateForDevice()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return KeyInputQueue.getSwitchState(devid, sw);
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            return mInputManager.getSwitchState(devid, sw);
+        } else {
+            return KeyInputQueue.getSwitchState(devid, sw);
+        }
     }
 
     public int getScancodeState(int sw) {
@@ -4369,7 +4412,11 @@
                 "getScancodeState()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mQueue.getScancodeState(sw);
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            return mInputManager.getScancodeState(sw);
+        } else {
+            return mQueue.getScancodeState(sw);
+        }
     }
 
     public int getScancodeStateForDevice(int devid, int sw) {
@@ -4377,7 +4424,11 @@
                 "getScancodeStateForDevice()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mQueue.getScancodeState(devid, sw);
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            return mInputManager.getScancodeState(devid, sw);
+        } else {
+            return mQueue.getScancodeState(devid, sw);
+        }
     }
 
     public int getTrackballScancodeState(int sw) {
@@ -4385,7 +4436,11 @@
                 "getTrackballScancodeState()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mQueue.getTrackballScancodeState(sw);
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            return mInputManager.getTrackballScancodeState(sw);
+        } else {
+            return mQueue.getTrackballScancodeState(sw);
+        }
     }
 
     public int getDPadScancodeState(int sw) {
@@ -4393,7 +4448,11 @@
                 "getDPadScancodeState()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mQueue.getDPadScancodeState(sw);
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            return mInputManager.getDPadScancodeState(sw);
+        } else {
+            return mQueue.getDPadScancodeState(sw);
+        }
     }
 
     public int getKeycodeState(int sw) {
@@ -4401,7 +4460,11 @@
                 "getKeycodeState()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mQueue.getKeycodeState(sw);
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            return mInputManager.getKeycodeState(sw);
+        } else {
+            return mQueue.getKeycodeState(sw);
+        }
     }
 
     public int getKeycodeStateForDevice(int devid, int sw) {
@@ -4409,7 +4472,11 @@
                 "getKeycodeStateForDevice()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mQueue.getKeycodeState(devid, sw);
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            return mInputManager.getKeycodeState(devid, sw);
+        } else {
+            return mQueue.getKeycodeState(devid, sw);
+        }
     }
 
     public int getTrackballKeycodeState(int sw) {
@@ -4417,7 +4484,11 @@
                 "getTrackballKeycodeState()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mQueue.getTrackballKeycodeState(sw);
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            return mInputManager.getTrackballKeycodeState(sw);
+        } else {
+            return mQueue.getTrackballKeycodeState(sw);
+        }
     }
 
     public int getDPadKeycodeState(int sw) {
@@ -4425,11 +4496,19 @@
                 "getDPadKeycodeState()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mQueue.getDPadKeycodeState(sw);
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            return mInputManager.getDPadKeycodeState(sw);
+        } else {
+            return mQueue.getDPadKeycodeState(sw);
+        }
     }
 
     public boolean hasKeys(int[] keycodes, boolean[] keyExists) {
-        return KeyInputQueue.hasKeys(keycodes, keyExists);
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            return mInputManager.hasKeys(keycodes, keyExists);
+        } else {
+            return KeyInputQueue.hasKeys(keycodes, keyExists);
+        }
     }
 
     public void enableScreenAfterBoot() {
@@ -4573,7 +4652,11 @@
             mLayoutNeeded = true;
             startFreezingDisplayLocked();
             Slog.i(TAG, "Setting rotation to " + rotation + ", animFlags=" + animFlags);
-            mQueue.setOrientation(rotation);
+            if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                mInputManager.setDisplayOrientation(0, rotation);
+            } else {
+                mQueue.setOrientation(rotation);
+            }
             if (mDisplayEnabled) {
                 Surface.setOrientation(0, rotation, animFlags);
             }
@@ -4904,7 +4987,11 @@
         if (mDisplay == null) {
             return false;
         }
-        mQueue.getInputConfiguration(config);
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            mInputManager.getInputConfiguration(config);
+        } else {
+            mQueue.getInputConfiguration(config);
+        }
 
         // Use the effective "visual" dimensions based on current rotation
         final boolean rotated = (mRotation == Surface.ROTATION_90
@@ -4987,6 +5074,291 @@
     // -------------------------------------------------------------
     // Input Events and Focus Management
     // -------------------------------------------------------------
+    
+    public void getKeyEventTargets(InputTargetList inputTargets,
+            KeyEvent event, int nature, int policyFlags) {
+        if (DEBUG_INPUT) Slog.v(TAG, "Dispatch key: " + event);
+
+        // TODO what do we do with mDisplayFrozen?
+        // TODO what do we do with focus.mToken.paused?
+        
+        WindowState focus = getFocusedWindow();
+        wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
+        
+        addInputTarget(inputTargets, focus, InputTarget.FLAG_SYNC);
+    }
+    
+    // Target of Motion events
+    WindowState mTouchFocus;
+
+    // Windows above the target who would like to receive an "outside"
+    // touch event for any down events outside of them.
+    // (This is a linked list by way of WindowState.mNextOutsideTouch.)
+    WindowState mOutsideTouchTargets;
+    
+    private void clearTouchFocus() {
+        mTouchFocus = null;
+        mOutsideTouchTargets = null;
+    }
+    
+    public void getMotionEventTargets(InputTargetList inputTargets,
+            MotionEvent event, int nature, int policyFlags) {
+        if (nature == InputQueue.INPUT_EVENT_NATURE_TRACKBALL) {
+            // More or less the same as for keys...
+            WindowState focus = getFocusedWindow();
+            wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
+            
+            addInputTarget(inputTargets, focus, InputTarget.FLAG_SYNC);
+            return;
+        }
+        
+        int action = event.getAction();
+
+        // TODO detect cheek presses somewhere... either here or in native code
+        
+        final boolean screenWasOff = (policyFlags & WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0;
+        
+        WindowState target = mTouchFocus;
+        
+        if (action == MotionEvent.ACTION_UP) {
+            // let go of our target
+            mPowerManager.logPointerUpEvent();
+            clearTouchFocus();
+        } else if (action == MotionEvent.ACTION_DOWN) {
+            // acquire a new target
+            mPowerManager.logPointerDownEvent();
+        
+            synchronized (mWindowMap) {
+                if (mTouchFocus != null) {
+                    // this is weird, we got a pen down, but we thought it was
+                    // already down!
+                    // XXX: We should probably send an ACTION_UP to the current
+                    // target.
+                    Slog.w(TAG, "Pointer down received while already down in: "
+                            + mTouchFocus);
+                    clearTouchFocus();
+                }
+
+                // ACTION_DOWN is special, because we need to lock next events to
+                // the window we'll land onto.
+                final int x = (int) event.getX();
+                final int y = (int) event.getY();
+
+                final ArrayList windows = mWindows;
+                final int N = windows.size();
+                WindowState topErrWindow = null;
+                final Rect tmpRect = mTempRect;
+                for (int i=N-1; i>=0; i--) {
+                    WindowState child = (WindowState)windows.get(i);
+                    //Slog.i(TAG, "Checking dispatch to: " + child);
+                    final int flags = child.mAttrs.flags;
+                    if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
+                        if (topErrWindow == null) {
+                            topErrWindow = child;
+                        }
+                    }
+                    if (!child.isVisibleLw()) {
+                        //Slog.i(TAG, "Not visible!");
+                        continue;
+                    }
+                    if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
+                        //Slog.i(TAG, "Not touchable!");
+                        if ((flags & WindowManager.LayoutParams
+                                .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
+                            child.mNextOutsideTouch = mOutsideTouchTargets;
+                            mOutsideTouchTargets = child;
+                        }
+                        continue;
+                    }
+                    tmpRect.set(child.mFrame);
+                    if (child.mTouchableInsets == ViewTreeObserver
+                                .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
+                        // The touch is inside of the window if it is
+                        // inside the frame, AND the content part of that
+                        // frame that was given by the application.
+                        tmpRect.left += child.mGivenContentInsets.left;
+                        tmpRect.top += child.mGivenContentInsets.top;
+                        tmpRect.right -= child.mGivenContentInsets.right;
+                        tmpRect.bottom -= child.mGivenContentInsets.bottom;
+                    } else if (child.mTouchableInsets == ViewTreeObserver
+                                .InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) {
+                        // The touch is inside of the window if it is
+                        // inside the frame, AND the visible part of that
+                        // frame that was given by the application.
+                        tmpRect.left += child.mGivenVisibleInsets.left;
+                        tmpRect.top += child.mGivenVisibleInsets.top;
+                        tmpRect.right -= child.mGivenVisibleInsets.right;
+                        tmpRect.bottom -= child.mGivenVisibleInsets.bottom;
+                    }
+                    final int touchFlags = flags &
+                        (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+                    if (tmpRect.contains(x, y) || touchFlags == 0) {
+                        //Slog.i(TAG, "Using this target!");
+                        if (!screenWasOff || (flags &
+                                WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) {
+                            mTouchFocus = child;
+                        } else {
+                            //Slog.i(TAG, "Waking, skip!");
+                            mTouchFocus = null;
+                        }
+                        break;
+                    }
+
+                    if ((flags & WindowManager.LayoutParams
+                            .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
+                        child.mNextOutsideTouch = mOutsideTouchTargets;
+                        mOutsideTouchTargets = child;
+                        //Slog.i(TAG, "Adding to outside target list: " + child);
+                    }
+                }
+
+                // if there's an error window but it's not accepting
+                // focus (typically because it is not yet visible) just
+                // wait for it -- any other focused window may in fact
+                // be in ANR state.
+                if (topErrWindow != null && mTouchFocus != topErrWindow) {
+                    mTouchFocus = null;
+                }
+            }
+            
+            target = mTouchFocus;
+        }
+        
+        if (target != null) {
+            wakeupIfNeeded(target, eventType(event));
+        }
+        
+        int targetFlags = 0;
+        if (target == null) {
+            // In this case we are either dropping the event, or have received
+            // a move or up without a down.  It is common to receive move
+            // events in such a way, since this means the user is moving the
+            // pointer without actually pressing down.  All other cases should
+            // be atypical, so let's log them.
+            if (action != MotionEvent.ACTION_MOVE) {
+                Slog.w(TAG, "No window to dispatch pointer action " + action);
+            }
+        } else {
+            if ((target.mAttrs.flags &
+                    WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
+                //target wants to ignore fat touch events
+                boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(event);
+                //explicit flag to return without processing event further
+                boolean returnFlag = false;
+                if((action == MotionEvent.ACTION_DOWN)) {
+                    mFatTouch = false;
+                    if(cheekPress) {
+                        mFatTouch = true;
+                        returnFlag = true;
+                    }
+                } else {
+                    if(action == MotionEvent.ACTION_UP) {
+                        if(mFatTouch) {
+                            //earlier even was invalid doesnt matter if current up is cheekpress or not
+                            mFatTouch = false;
+                            returnFlag = true;
+                        } else if(cheekPress) {
+                            //cancel the earlier event
+                            targetFlags |= InputTarget.FLAG_CANCEL;
+                            action = MotionEvent.ACTION_CANCEL;
+                        }
+                    } else if(action == MotionEvent.ACTION_MOVE) {
+                        if(mFatTouch) {
+                            //two cases here
+                            //an invalid down followed by 0 or moves(valid or invalid)
+                            //a valid down,  invalid move, more moves. want to ignore till up
+                            returnFlag = true;
+                        } else if(cheekPress) {
+                            //valid down followed by invalid moves
+                            //an invalid move have to cancel earlier action
+                            targetFlags |= InputTarget.FLAG_CANCEL;
+                            action = MotionEvent.ACTION_CANCEL;
+                            if (DEBUG_INPUT) Slog.v(TAG, "Sending cancel for invalid ACTION_MOVE");
+                            //note that the subsequent invalid moves will not get here
+                            mFatTouch = true;
+                        }
+                    }
+                } //else if action
+                if(returnFlag) {
+                    return;
+                }
+            } //end if target
+        }        
+        
+        synchronized (mWindowMap) {
+            if (target != null && ! target.isVisibleLw()) {
+                target = null;
+            }
+            
+            if (action == MotionEvent.ACTION_DOWN) {
+                while (mOutsideTouchTargets != null) {
+                    addInputTarget(inputTargets, mOutsideTouchTargets,
+                            InputTarget.FLAG_OUTSIDE | targetFlags);
+                    mOutsideTouchTargets = mOutsideTouchTargets.mNextOutsideTouch;
+                }
+            }
+            
+            // If we sent an initial down to the wallpaper, then continue
+            // sending events until the final up.
+            // Alternately if we are on top of the wallpaper, then the wallpaper also
+            // gets to see this movement.
+            if (mSendingPointersToWallpaper ||
+                    (target != null && action == MotionEvent.ACTION_DOWN
+                            && mWallpaperTarget == target
+                            && target.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD)) {
+                int curTokenIndex = mWallpaperTokens.size();
+                while (curTokenIndex > 0) {
+                    curTokenIndex--;
+                    WindowToken token = mWallpaperTokens.get(curTokenIndex);
+                    int curWallpaperIndex = token.windows.size();
+                    while (curWallpaperIndex > 0) {
+                        curWallpaperIndex--;
+                        WindowState wallpaper = token.windows.get(curWallpaperIndex);
+                        if ((wallpaper.mAttrs.flags &
+                                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
+                            continue;
+                        }
+                        
+                        switch (action) {
+                            case MotionEvent.ACTION_DOWN:
+                                mSendingPointersToWallpaper = true;
+                                break;
+                            case MotionEvent.ACTION_UP:
+                                mSendingPointersToWallpaper = false;
+                                break;
+                        }
+                        
+                        addInputTarget(inputTargets, wallpaper, targetFlags);
+                    }
+                }
+            }
+            
+            if (target != null) {
+                addInputTarget(inputTargets, target, InputTarget.FLAG_SYNC | targetFlags);
+            }
+        }
+    }
+    
+    private void addInputTarget(InputTargetList inputTargets, WindowState window, int flags) {
+        if (window.mInputChannel == null) {
+            return;
+        }
+        
+        long timeoutNanos = -1;
+        IApplicationToken appToken = window.getAppToken();
+
+        if (appToken != null) {
+            try {
+                timeoutNanos = appToken.getKeyDispatchingTimeout() * 1000000;
+            } catch (RemoteException ex) {
+                Slog.w(TAG, "Could not get key dispatching timeout.", ex);
+            }
+        }
+        
+        inputTargets.add(window.mInputChannel, flags, timeoutNanos,
+                - window.mFrame.left, - window.mFrame.top);
+    }
 
     private final void wakeupIfNeeded(WindowState targetWin, int eventType) {
         long curTime = SystemClock.uptimeMillis();
@@ -5502,10 +5874,18 @@
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
         final long ident = Binder.clearCallingIdentity();
-        final int result = dispatchKey(newEvent, pid, uid);
-        if (sync) {
-            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
+        
+        final int result;
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            result = mInputManager.injectKeyEvent(newEvent,
+                    InputQueue.INPUT_EVENT_NATURE_KEY, sync, pid, uid);
+        } else {
+            result = dispatchKey(newEvent, pid, uid);
+            if (sync) {
+                mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
+            }
         }
+        
         Binder.restoreCallingIdentity(ident);
         switch (result) {
             case INJECT_NO_PERMISSION:
@@ -5530,10 +5910,18 @@
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
         final long ident = Binder.clearCallingIdentity();
-        final int result = dispatchPointer(null, ev, pid, uid);
-        if (sync) {
-            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
+        
+        final int result;
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            result = mInputManager.injectMotionEvent(ev,
+                    InputQueue.INPUT_EVENT_NATURE_TOUCH, sync, pid, uid);
+        } else {
+            result = dispatchPointer(null, ev, pid, uid);
+            if (sync) {
+                mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
+            }
         }
+        
         Binder.restoreCallingIdentity(ident);
         switch (result) {
             case INJECT_NO_PERMISSION:
@@ -5558,10 +5946,18 @@
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
         final long ident = Binder.clearCallingIdentity();
-        final int result = dispatchTrackball(null, ev, pid, uid);
-        if (sync) {
-            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
+        
+        final int result;
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            result = mInputManager.injectMotionEvent(ev,
+                    InputQueue.INPUT_EVENT_NATURE_TRACKBALL, sync, pid, uid);
+        } else {
+            result = dispatchTrackball(null, ev, pid, uid);
+            if (sync) {
+                mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
+            }
         }
+        
         Binder.restoreCallingIdentity(ident);
         switch (result) {
             case INJECT_NO_PERMISSION:
@@ -6329,14 +6725,8 @@
 
     private class KeyQ extends KeyInputQueue
             implements KeyInputQueue.FilterCallback {
-        PowerManager.WakeLock mHoldingScreen;
-
         KeyQ() {
             super(mContext, WindowManagerService.this);
-            PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
-            mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
-                    "KEEP_SCREEN_ON_FLAG");
-            mHoldingScreen.setReferenceCounted(false);
         }
 
         @Override
@@ -6448,21 +6838,6 @@
                     return FILTER_KEEP;
             }
         }
-
-        /**
-         * Must be called with the main window manager lock held.
-         */
-        void setHoldScreenLocked(boolean holding) {
-            boolean state = mHoldingScreen.isHeld();
-            if (holding != state) {
-                if (holding) {
-                    mHoldingScreen.acquire();
-                } else {
-                    mPolicy.screenOnStoppedLw();
-                    mHoldingScreen.release();
-                }
-            }
-        }
     }
 
     public boolean detectSafeMode() {
@@ -6791,8 +7166,14 @@
         }
 
         public int add(IWindow window, WindowManager.LayoutParams attrs,
+                int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) {
+            return addWindow(this, window, attrs, viewVisibility, outContentInsets,
+                    outInputChannel);
+        }
+        
+        public int addWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
                 int viewVisibility, Rect outContentInsets) {
-            return addWindow(this, window, attrs, viewVisibility, outContentInsets);
+            return addWindow(this, window, attrs, viewVisibility, outContentInsets, null);
         }
 
         public void remove(IWindow window) {
@@ -7161,6 +7542,9 @@
         int mSurfaceLayer;
         float mSurfaceAlpha;
         
+        // Input channel
+        InputChannel mInputChannel;
+        
         WindowState(Session s, IWindow c, WindowToken token,
                WindowState attachedWindow, WindowManager.LayoutParams a,
                int viewVisibility) {
@@ -8183,6 +8567,15 @@
                 // Ignore if it has already been removed (usually because
                 // we are doing this as part of processing a death note.)
             }
+            
+            if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                if (mInputChannel != null) {
+                    mInputManager.unregisterInputChannel(mInputChannel);
+                    
+                    mInputChannel.dispose();
+                    mInputChannel = null;
+                }
+            }
         }
 
         private class DeathRecipient implements IBinder.DeathRecipient {
@@ -8425,6 +8818,11 @@
                         pw.print(" mWallpaperYStep="); pw.println(mWallpaperYStep);
             }
         }
+        
+        String makeInputChannelName() {
+            return Integer.toHexString(System.identityHashCode(this))
+                + " " + mAttrs.getTitle();
+        }
 
         @Override
         public String toString() {
@@ -9276,7 +9674,8 @@
             IInputContext inputContext) {
         if (client == null) throw new IllegalArgumentException("null client");
         if (inputContext == null) throw new IllegalArgumentException("null inputContext");
-        return new Session(client, inputContext);
+        Session session = new Session(client, inputContext);
+        return session;
     }
 
     public boolean inputMethodClientHasFocus(IInputMethodClient client) {
@@ -10764,7 +11163,7 @@
         } else if (animating) {
             requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis());
         }
-        mQueue.setHoldScreenLocked(holdScreen != null);
+        setHoldScreenLocked(holdScreen != null);
         if (screenBrightness < 0 || screenBrightness > 1.0f) {
             mPowerManager.setScreenBrightnessOverride(-1);
         } else {
@@ -10794,6 +11193,21 @@
         // be enabled, because the window obscured flags have changed.
         enableScreenIfNeededLocked();
     }
+    
+    /**
+     * Must be called with the main window manager lock held.
+     */
+    void setHoldScreenLocked(boolean holding) {
+        boolean state = mHoldingScreenWakeLock.isHeld();
+        if (holding != state) {
+            if (holding) {
+                mHoldingScreenWakeLock.acquire();
+            } else {
+                mPolicy.screenOnStoppedLw();
+                mHoldingScreenWakeLock.release();
+            }
+        }
+    }
 
     void requestAnimationLocked(long delay) {
         if (!mAnimationPending) {
@@ -11124,8 +11538,13 @@
             return;
         }
 
-        pw.println("Input State:");
-        mQueue.dump(pw, "  ");
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            pw.println("Input Dispatcher State:");
+            mInputManager.dump(pw);
+        } else {
+            pw.println("Input State:");
+            mQueue.dump(pw, "  ");
+        }
         pw.println(" ");
         
         synchronized(mWindowMap) {