Fix input channel leak.
Bug: 5156144

Input channels could leak or simply live longer than they should
in some cases.

1. Monitor channels (used by the pointer location overlay) are never
   unregistered, so they would leak.

   Added code to handle failures in the receive callback by closing
   the input channel.

2. The DragState held onto its input window and application handles
   even after the input channel was disposed.

   Added code to null these handles out when they are no longer needed.

3. Input channels previously used as input event targets would stick
   around until the targets were cleared (usually on the next
   event).

   Added code to detect when the input dispatcher is in
   an idle state and to proactively clear the targets then
   to ensure that resources are released promptly.

4. Native input window handles held onto the input channel even
   after the input window was removed from the input dispatcher.
   Consequently, the input channel would not be disposed until
   the input window handle itself was freed.  Since the input
   window handle is held from managed code, this meant that the
   window's input channel could stick around until the next GC.

   Refactored the input window handle to separate the properties
   (info) and identify (handle) state into different objects.
   Then modified the dispatcher to release the properties (info)
   when no longer needed, including the input channel.

7. The pointer location overlay does not actually use its
   standard input channel, only the monitor input channel.

   Added INPUT_FEATURE_NO_INPUT_CHANNEL to allow windows to
   request that they not be provided with an input channel
   at all.

Improved some of the error handling logic to emit the status
code as part of the exception message.

Change-Id: I01988d4391a70c6678c8b0e936ca051af680b1a5
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a0cc287..5c045bb 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -454,7 +454,10 @@
                 // manager, to make sure we do the relayout before receiving
                 // any other events from the system.
                 requestLayout();
-                mInputChannel = new InputChannel();
+                if ((mWindowAttributes.inputFeatures
+                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
+                    mInputChannel = new InputChannel();
+                }
                 try {
                     res = sWindowSession.add(mWindow, mWindowAttributes,
                             getHostVisibility(), mAttachInfo.mContentInsets,
@@ -524,12 +527,14 @@
                     mInputQueueCallback =
                         ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
                 }
-                if (mInputQueueCallback != null) {
-                    mInputQueue = new InputQueue(mInputChannel);
-                    mInputQueueCallback.onInputQueueCreated(mInputQueue);
-                } else {
-                    InputQueue.registerInputChannel(mInputChannel, mInputHandler,
-                            Looper.myQueue());
+                if (mInputChannel != null) {
+                    if (mInputQueueCallback != null) {
+                        mInputQueue = new InputQueue(mInputChannel);
+                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
+                    } else {
+                        InputQueue.registerInputChannel(mInputChannel, mInputHandler,
+                                Looper.myQueue());
+                    }
                 }
 
                 view.assignParent(this);
@@ -2152,13 +2157,12 @@
 
         mSurface.release();
 
-        if (mInputChannel != null) {
-            if (mInputQueueCallback != null) {
-                mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
-                mInputQueueCallback = null;
-            } else {
-                InputQueue.unregisterInputChannel(mInputChannel);
-            }
+        if (mInputQueueCallback != null && mInputQueue != null) {
+            mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
+            mInputQueueCallback = null;
+            mInputQueue = null;
+        } else if (mInputChannel != null) {
+            InputQueue.unregisterInputChannel(mInputChannel);
         }
         try {
             sWindowSession.remove(mWindow);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index fdd9b2c..52d25d9 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1029,9 +1029,18 @@
         public static final int INPUT_FEATURE_DISABLE_POINTER_GESTURES = 0x00000001;
 
         /**
+         * Does not construct an input channel for this window.  The channel will therefore
+         * be incapable of receiving input.
+         *
+         * @hide
+         */
+        public static final int INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002;
+
+        /**
          * Control special features of the input subsystem.
          *
          * @see #INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES
+         * @see #INPUT_FEATURE_NO_INPUT_CHANNEL
          * @hide
          */
         public int inputFeatures;
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 0c2801c..5fcf8fa 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -111,7 +111,9 @@
         NativeInputChannel* nativeInputChannel) {
     jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
             gInputChannelClassInfo.ctor);
-    android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
+    if (inputChannelObj) {
+        android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
+    }
     return inputChannelObj;
 }
 
@@ -126,18 +128,29 @@
     status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
 
     if (result) {
-        LOGE("Could not open input channel pair.  status=%d", result);
-        jniThrowRuntimeException(env, "Could not open input channel pair.");
+        String8 message;
+        message.appendFormat("Could not open input channel pair.  status=%d", result);
+        jniThrowRuntimeException(env, message.string());
         return NULL;
     }
 
-    // TODO more robust error checking
+    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
+    if (env->ExceptionCheck()) {
+        return NULL;
+    }
+
     jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
             new NativeInputChannel(serverChannel));
+    if (env->ExceptionCheck()) {
+        return NULL;
+    }
+
     jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
             new NativeInputChannel(clientChannel));
+    if (env->ExceptionCheck()) {
+        return NULL;
+    }
 
-    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
     env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
     env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
     return channelPair;
@@ -161,7 +174,7 @@
 
 static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
         jobject otherObj) {
-    if (android_view_InputChannel_getInputChannel(env, otherObj) != NULL) {
+    if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
                 "Other object already has a native input channel.");
         return;
@@ -175,7 +188,7 @@
 
 static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj,
         jobject parcelObj) {
-    if (android_view_InputChannel_getInputChannel(env, obj) != NULL) {
+    if (android_view_InputChannel_getNativeInputChannel(env, obj) != NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
                 "This object already has a native input channel.");
         return;
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index 80c4871..300c04a 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -455,8 +455,9 @@
             env, inputChannelObj, inputHandlerObj, messageQueueObj);
 
     if (status) {
-        jniThrowRuntimeException(env, "Failed to register input channel.  "
-                "Check logs for details.");
+        String8 message;
+        message.appendFormat("Failed to register input channel.  status=%d", status);
+        jniThrowRuntimeException(env, message.string());
     }
 }
 
@@ -465,8 +466,9 @@
     status_t status = gNativeInputQueue.unregisterInputChannel(env, inputChannelObj);
 
     if (status) {
-        jniThrowRuntimeException(env, "Failed to unregister input channel.  "
-                "Check logs for details.");
+        String8 message;
+        message.appendFormat("Failed to unregister input channel.  status=%d", status);
+        jniThrowRuntimeException(env, message.string());
     }
 }
 
@@ -479,8 +481,9 @@
     // was no longer registered (DEAD_OBJECT) since it is a common race that can occur
     // during application shutdown.  The input dispatcher recovers gracefully anyways.
     if (status != OK && status != DEAD_OBJECT) {
-        jniThrowRuntimeException(env, "Failed to finish input event.  "
-                "Check logs for details.");
+        String8 message;
+        message.appendFormat("Failed to finish input event.  status=%d", status);
+        jniThrowRuntimeException(env, message.string());
     }
 }
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index dfd1b00..c988846 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -900,6 +900,7 @@
             lp.setTitle("PointerLocation");
             WindowManager wm = (WindowManager)
                     mContext.getSystemService(Context.WINDOW_SERVICE);
+            lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
             wm.addView(addView, lp);
             
             if (mPointerLocationInputChannel == null) {
diff --git a/services/input/Android.mk b/services/input/Android.mk
index afbe546..86c6c8a 100644
--- a/services/input/Android.mk
+++ b/services/input/Android.mk
@@ -18,6 +18,7 @@
 
 LOCAL_SRC_FILES:= \
     EventHub.cpp \
+    InputApplication.cpp \
     InputDispatcher.cpp \
     InputListener.cpp \
     InputManager.cpp \
diff --git a/services/input/InputApplication.cpp b/services/input/InputApplication.cpp
new file mode 100644
index 0000000..a99e637
--- /dev/null
+++ b/services/input/InputApplication.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "InputApplication"
+
+#include "InputApplication.h"
+
+#include <cutils/log.h>
+
+namespace android {
+
+// --- InputApplicationHandle ---
+
+InputApplicationHandle::InputApplicationHandle() :
+    mInfo(NULL) {
+}
+
+InputApplicationHandle::~InputApplicationHandle() {
+    delete mInfo;
+}
+
+void InputApplicationHandle::releaseInfo() {
+    if (mInfo) {
+        delete mInfo;
+        mInfo = NULL;
+    }
+}
+
+} // namespace android
diff --git a/services/input/InputApplication.h b/services/input/InputApplication.h
index 8902f7a..67ae94b 100644
--- a/services/input/InputApplication.h
+++ b/services/input/InputApplication.h
@@ -27,14 +27,32 @@
 
 /*
  * Describes the properties of an application that can receive input.
+ */
+struct InputApplicationInfo {
+    String8 name;
+    nsecs_t dispatchingTimeout;
+};
+
+
+/*
+ * Handle for an application that can receive input.
  *
  * Used by the native input dispatcher as a handle for the window manager objects
  * that describe an application.
  */
 class InputApplicationHandle : public RefBase {
 public:
-    String8 name;
-    nsecs_t dispatchingTimeout;
+    inline const InputApplicationInfo* getInfo() const {
+        return mInfo;
+    }
+
+    inline String8 getName() const {
+        return mInfo ? mInfo->name : String8("<invalid>");
+    }
+
+    inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
+        return mInfo ? mInfo->dispatchingTimeout : defaultValue;
+    }
 
     /**
      * Requests that the state of this object be updated to reflect
@@ -45,11 +63,19 @@
      *
      * Returns true on success, or false if the handle is no longer valid.
      */
-    virtual bool update() = 0;
+    virtual bool updateInfo() = 0;
+
+    /**
+     * Releases the storage used by the associated information when it is
+     * no longer needed.
+     */
+    void releaseInfo();
 
 protected:
-    InputApplicationHandle() { }
-    virtual ~InputApplicationHandle() { }
+    InputApplicationHandle();
+    virtual ~InputApplicationHandle();
+
+    InputApplicationInfo* mInfo;
 };
 
 } // namespace android
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index cf167ae..1eb5f0e 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -306,7 +306,12 @@
                     }
                 }
             }
+
+            // Nothing to do if there is no pending event.
             if (! mPendingEvent) {
+                if (mActiveConnections.isEmpty()) {
+                    dispatchIdleLocked();
+                }
                 return;
             }
         } else {
@@ -462,6 +467,16 @@
     }
 }
 
+void InputDispatcher::dispatchIdleLocked() {
+#if DEBUG_FOCUS
+    LOGD("Dispatcher idle.  There are no pending events or active connections.");
+#endif
+
+    // Reset targets when idle, to release input channels and other resources
+    // they are holding onto.
+    resetTargetsLocked();
+}
+
 bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
     bool needWake = mInboundQueue.isEmpty();
     mInboundQueue.enqueueAtTail(entry);
@@ -525,20 +540,21 @@
     size_t numWindows = mWindowHandles.size();
     for (size_t i = 0; i < numWindows; i++) {
         sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
-        int32_t flags = windowHandle->layoutParamsFlags;
+        const InputWindowInfo* windowInfo = windowHandle->getInfo();
+        int32_t flags = windowInfo->layoutParamsFlags;
 
-        if (windowHandle->visible) {
-            if (!(flags & InputWindowHandle::FLAG_NOT_TOUCHABLE)) {
-                bool isTouchModal = (flags & (InputWindowHandle::FLAG_NOT_FOCUSABLE
-                        | InputWindowHandle::FLAG_NOT_TOUCH_MODAL)) == 0;
-                if (isTouchModal || windowHandle->touchableRegionContainsPoint(x, y)) {
+        if (windowInfo->visible) {
+            if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
+                bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
+                        | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
+                if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
                     // Found window.
                     return windowHandle;
                 }
             }
         }
 
-        if (flags & InputWindowHandle::FLAG_SYSTEM_ERROR) {
+        if (flags & InputWindowInfo::FLAG_SYSTEM_ERROR) {
             // Error window is on top but not visible, so touch is dropped.
             return NULL;
         }
@@ -1051,9 +1067,15 @@
             LOGD("Waiting for application to become ready for input: %s",
                     getApplicationWindowLabelLocked(applicationHandle, windowHandle).string());
 #endif
-            nsecs_t timeout = windowHandle != NULL ? windowHandle->dispatchingTimeout :
-                applicationHandle != NULL ?
-                        applicationHandle->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+            nsecs_t timeout;
+            if (windowHandle != NULL) {
+                timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
+            } else if (applicationHandle != NULL) {
+                timeout = applicationHandle->getDispatchingTimeout(
+                        DEFAULT_INPUT_DISPATCHING_TIMEOUT);
+            } else {
+                timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+            }
 
             mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
             mInputTargetWaitStartTime = currentTime;
@@ -1168,7 +1190,7 @@
     }
 
     // If the currently focused window is paused then keep waiting.
-    if (mFocusedWindowHandle->paused) {
+    if (mFocusedWindowHandle->getInfo()->paused) {
 #if DEBUG_FOCUS
         LOGD("Waiting because focused window is paused.");
 #endif
@@ -1302,21 +1324,22 @@
         size_t numWindows = mWindowHandles.size();
         for (size_t i = 0; i < numWindows; i++) {
             sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
-            int32_t flags = windowHandle->layoutParamsFlags;
+            const InputWindowInfo* windowInfo = windowHandle->getInfo();
+            int32_t flags = windowInfo->layoutParamsFlags;
 
-            if (flags & InputWindowHandle::FLAG_SYSTEM_ERROR) {
+            if (flags & InputWindowInfo::FLAG_SYSTEM_ERROR) {
                 if (topErrorWindowHandle == NULL) {
                     topErrorWindowHandle = windowHandle;
                 }
             }
 
-            if (windowHandle->visible) {
-                if (! (flags & InputWindowHandle::FLAG_NOT_TOUCHABLE)) {
-                    isTouchModal = (flags & (InputWindowHandle::FLAG_NOT_FOCUSABLE
-                            | InputWindowHandle::FLAG_NOT_TOUCH_MODAL)) == 0;
-                    if (isTouchModal || windowHandle->touchableRegionContainsPoint(x, y)) {
+            if (windowInfo->visible) {
+                if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
+                    isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
+                            | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
+                    if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
                         if (! screenWasOff
-                                || (flags & InputWindowHandle::FLAG_TOUCHABLE_WHEN_WAKING)) {
+                                || (flags & InputWindowInfo::FLAG_TOUCHABLE_WHEN_WAKING)) {
                             newTouchedWindowHandle = windowHandle;
                         }
                         break; // found touched window, exit window loop
@@ -1324,7 +1347,7 @@
                 }
 
                 if (maskedAction == AMOTION_EVENT_ACTION_DOWN
-                        && (flags & InputWindowHandle::FLAG_WATCH_OUTSIDE_TOUCH)) {
+                        && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
                     int32_t outsideTargetFlags = InputTarget::FLAG_DISPATCH_AS_OUTSIDE;
                     if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
                         outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
@@ -1350,7 +1373,8 @@
         }
 
         // Figure out whether splitting will be allowed for this window.
-        if (newTouchedWindowHandle != NULL && newTouchedWindowHandle->supportsSplitTouch()) {
+        if (newTouchedWindowHandle != NULL
+                && newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
             // New window supports splitting.
             isSplit = true;
         } else if (isSplit) {
@@ -1396,7 +1420,7 @@
             // within the touched window.
             if (!isTouchModal) {
                 while (sample->next) {
-                    if (!newHoverWindowHandle->touchableRegionContainsPoint(
+                    if (!newHoverWindowHandle->getInfo()->touchableRegionContainsPoint(
                             sample->next->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X),
                             sample->next->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y))) {
                         *outSplitBatchAfterSample = sample;
@@ -1444,15 +1468,15 @@
                     && newTouchedWindowHandle != NULL) {
 #if DEBUG_FOCUS
                 LOGD("Touch is slipping out of window %s into window %s.",
-                        oldTouchedWindowHandle->name.string(),
-                        newTouchedWindowHandle->name.string());
+                        oldTouchedWindowHandle->getName().string(),
+                        newTouchedWindowHandle->getName().string());
 #endif
                 // Make a slippery exit from the old window.
                 mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
                         InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0));
 
                 // Make a slippery entrance into the new window.
-                if (newTouchedWindowHandle->supportsSplitTouch()) {
+                if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
                     isSplit = true;
                 }
 
@@ -1484,7 +1508,8 @@
         // Let the previous window know that the hover sequence is over.
         if (mLastHoverWindowHandle != NULL) {
 #if DEBUG_HOVER
-            LOGD("Sending hover exit event to window %s.", mLastHoverWindowHandle->name.string());
+            LOGD("Sending hover exit event to window %s.",
+                    mLastHoverWindowHandle->getName().string());
 #endif
             mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
                     InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
@@ -1493,7 +1518,8 @@
         // Let the new window know that the hover sequence is starting.
         if (newHoverWindowHandle != NULL) {
 #if DEBUG_HOVER
-            LOGD("Sending hover enter event to window %s.", newHoverWindowHandle->name.string());
+            LOGD("Sending hover enter event to window %s.",
+                    newHoverWindowHandle->getName().string());
 #endif
             mTempTouchState.addOrUpdateWindow(newHoverWindowHandle,
                     InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, BitSet32(0));
@@ -1533,12 +1559,12 @@
     if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
         sp<InputWindowHandle> foregroundWindowHandle =
                 mTempTouchState.getFirstForegroundWindowHandle();
-        const int32_t foregroundWindowUid = foregroundWindowHandle->ownerUid;
+        const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
         for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
             const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
             if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
                 sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
-                if (inputWindowHandle->ownerUid != foregroundWindowUid) {
+                if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
                     mTempTouchState.addOrUpdateWindow(inputWindowHandle,
                             InputTarget::FLAG_ZERO_COORDS, BitSet32(0));
                 }
@@ -1551,7 +1577,7 @@
         const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
         if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
             // If the touched window is paused then keep waiting.
-            if (touchedWindow.windowHandle->paused) {
+            if (touchedWindow.windowHandle->getInfo()->paused) {
 #if DEBUG_FOCUS
                 LOGD("Waiting because touched window is paused.");
 #endif
@@ -1581,10 +1607,11 @@
     if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
         sp<InputWindowHandle> foregroundWindowHandle =
                 mTempTouchState.getFirstForegroundWindowHandle();
-        if (foregroundWindowHandle->hasWallpaper) {
+        if (foregroundWindowHandle->getInfo()->hasWallpaper) {
             for (size_t i = 0; i < mWindowHandles.size(); i++) {
                 sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
-                if (windowHandle->layoutParamsType == InputWindowHandle::TYPE_WALLPAPER) {
+                if (windowHandle->getInfo()->layoutParamsType
+                        == InputWindowInfo::TYPE_WALLPAPER) {
                     mTempTouchState.addOrUpdateWindow(windowHandle,
                             InputTarget::FLAG_WINDOW_IS_OBSCURED
                                     | InputTarget::FLAG_DISPATCH_AS_IS,
@@ -1708,12 +1735,13 @@
         int32_t targetFlags, BitSet32 pointerIds) {
     mCurrentInputTargets.push();
 
+    const InputWindowInfo* windowInfo = windowHandle->getInfo();
     InputTarget& target = mCurrentInputTargets.editTop();
-    target.inputChannel = windowHandle->inputChannel;
+    target.inputChannel = windowInfo->inputChannel;
     target.flags = targetFlags;
-    target.xOffset = - windowHandle->frameLeft;
-    target.yOffset = - windowHandle->frameTop;
-    target.scaleFactor = windowHandle->scaleFactor;
+    target.xOffset = - windowInfo->frameLeft;
+    target.yOffset = - windowInfo->frameTop;
+    target.scaleFactor = windowInfo->scaleFactor;
     target.pointerIds = pointerIds;
 }
 
@@ -1734,14 +1762,15 @@
 bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
         const InjectionState* injectionState) {
     if (injectionState
-            && (windowHandle == NULL || windowHandle->ownerUid != injectionState->injectorUid)
+            && (windowHandle == NULL
+                    || windowHandle->getInfo()->ownerUid != injectionState->injectorUid)
             && !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) {
         if (windowHandle != NULL) {
             LOGW("Permission denied: injecting event from pid %d uid %d to window %s "
                     "owned by uid %d",
                     injectionState->injectorPid, injectionState->injectorUid,
-                    windowHandle->name.string(),
-                    windowHandle->ownerUid);
+                    windowHandle->getName().string(),
+                    windowHandle->getInfo()->ownerUid);
         } else {
             LOGW("Permission denied: injecting event from pid %d uid %d",
                     injectionState->injectorPid, injectionState->injectorUid);
@@ -1759,8 +1788,10 @@
         if (otherHandle == windowHandle) {
             break;
         }
-        if (otherHandle->visible && ! otherHandle->isTrustedOverlay()
-                && otherHandle->frameContainsPoint(x, y)) {
+
+        const InputWindowInfo* otherInfo = otherHandle->getInfo();
+        if (otherInfo->visible && ! otherInfo->isTrustedOverlay()
+                && otherInfo->frameContainsPoint(x, y)) {
             return true;
         }
     }
@@ -1769,7 +1800,7 @@
 
 bool InputDispatcher::isWindowFinishedWithPreviousInputLocked(
         const sp<InputWindowHandle>& windowHandle) {
-    ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->inputChannel);
+    ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
     if (connectionIndex >= 0) {
         sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
         return connection->outboundQueue.isEmpty();
@@ -1783,15 +1814,15 @@
         const sp<InputWindowHandle>& windowHandle) {
     if (applicationHandle != NULL) {
         if (windowHandle != NULL) {
-            String8 label(applicationHandle->name);
+            String8 label(applicationHandle->getName());
             label.append(" - ");
-            label.append(windowHandle->name);
+            label.append(windowHandle->getName());
             return label;
         } else {
-            return applicationHandle->name;
+            return applicationHandle->getName();
         }
     } else if (windowHandle != NULL) {
-        return windowHandle->name;
+        return windowHandle->getName();
     } else {
         return String8("<unknown application or window>");
     }
@@ -2127,7 +2158,7 @@
         if (status) {
             LOGE("channel '%s' ~ Could not publish key event, "
                     "status=%d", connection->getInputChannelName(), status);
-            abortBrokenDispatchCycleLocked(currentTime, connection);
+            abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
             return;
         }
         break;
@@ -2190,7 +2221,7 @@
         if (status) {
             LOGE("channel '%s' ~ Could not publish motion event, "
                     "status=%d", connection->getInputChannelName(), status);
-            abortBrokenDispatchCycleLocked(currentTime, connection);
+            abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
             return;
         }
 
@@ -2223,7 +2254,7 @@
                     LOGE("channel '%s' ~ Could not append motion sample "
                             "for a reason other than out of memory, status=%d",
                             connection->getInputChannelName(), status);
-                    abortBrokenDispatchCycleLocked(currentTime, connection);
+                    abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
                     return;
                 }
             }
@@ -2245,7 +2276,7 @@
     if (status) {
         LOGE("channel '%s' ~ Could not send dispatch signal, status=%d",
                 connection->getInputChannelName(), status);
-        abortBrokenDispatchCycleLocked(currentTime, connection);
+        abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
         return;
     }
 
@@ -2280,7 +2311,7 @@
     if (status) {
         LOGE("channel '%s' ~ Could not reset publisher, status=%d",
                 connection->getInputChannelName(), status);
-        abortBrokenDispatchCycleLocked(currentTime, connection);
+        abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
         return;
     }
 
@@ -2324,10 +2355,10 @@
 }
 
 void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
-        const sp<Connection>& connection) {
+        const sp<Connection>& connection, bool notify) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("channel '%s' ~ abortBrokenDispatchCycle",
-            connection->getInputChannelName());
+    LOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
+            connection->getInputChannelName(), toString(notify));
 #endif
 
     // Clear the outbound queue.
@@ -2338,8 +2369,10 @@
     if (connection->status == Connection::STATUS_NORMAL) {
         connection->status = Connection::STATUS_BROKEN;
 
-        // Notify other system components.
-        onDispatchCycleBrokenLocked(currentTime, connection);
+        if (notify) {
+            // Notify other system components.
+            onDispatchCycleBrokenLocked(currentTime, connection);
+        }
     }
 }
 
@@ -2368,36 +2401,41 @@
             return 0; // remove the callback
         }
 
-        nsecs_t currentTime = now();
-
+        bool notify;
         sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);
-        if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
-            LOGE("channel '%s' ~ Consumer closed input channel or an error occurred.  "
-                    "events=0x%x", connection->getInputChannelName(), events);
-            d->abortBrokenDispatchCycleLocked(currentTime, connection);
-            d->runCommandsLockedInterruptible();
-            return 0; // remove the callback
-        }
+        if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
+            if (!(events & ALOOPER_EVENT_INPUT)) {
+                LOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
+                        "events=0x%x", connection->getInputChannelName(), events);
+                return 1;
+            }
 
-        if (! (events & ALOOPER_EVENT_INPUT)) {
-            LOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
-                    "events=0x%x", connection->getInputChannelName(), events);
-            return 1;
-        }
+            bool handled = false;
+            status_t status = connection->inputPublisher.receiveFinishedSignal(&handled);
+            if (!status) {
+                nsecs_t currentTime = now();
+                d->finishDispatchCycleLocked(currentTime, connection, handled);
+                d->runCommandsLockedInterruptible();
+                return 1;
+            }
 
-        bool handled = false;
-        status_t status = connection->inputPublisher.receiveFinishedSignal(&handled);
-        if (status) {
             LOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
                     connection->getInputChannelName(), status);
-            d->abortBrokenDispatchCycleLocked(currentTime, connection);
-            d->runCommandsLockedInterruptible();
-            return 0; // remove the callback
+            notify = true;
+        } else {
+            // Monitor channels are never explicitly unregistered.
+            // We do it automatically when the remote endpoint is closed so don't warn
+            // about them.
+            notify = !connection->monitor;
+            if (notify) {
+                LOGW("channel '%s' ~ Consumer closed input channel or an error occurred.  "
+                        "events=0x%x", connection->getInputChannelName(), events);
+            }
         }
 
-        d->finishDispatchCycleLocked(currentTime, connection, handled);
-        d->runCommandsLockedInterruptible();
-        return 1;
+        // Unregister the channel.
+        d->unregisterInputChannelLocked(connection->inputChannel, notify);
+        return 0; // remove the callback
     } // release lock
 }
 
@@ -2450,9 +2488,10 @@
             InputTarget target;
             sp<InputWindowHandle> windowHandle = getWindowHandleLocked(connection->inputChannel);
             if (windowHandle != NULL) {
-                target.xOffset = -windowHandle->frameLeft;
-                target.yOffset = -windowHandle->frameTop;
-                target.scaleFactor = windowHandle->scaleFactor;
+                const InputWindowInfo* windowInfo = windowHandle->getInfo();
+                target.xOffset = -windowInfo->frameLeft;
+                target.yOffset = -windowInfo->frameTop;
+                target.scaleFactor = windowInfo->scaleFactor;
             } else {
                 target.xOffset = 0;
                 target.yOffset = 0;
@@ -2854,9 +2893,9 @@
 #if DEBUG_BATCHING
                             LOGD("Not streaming hover move because the last hovered window "
                                     "is '%s' but the currently hovered window is '%s'.",
-                                    mLastHoverWindowHandle->name.string(),
+                                    mLastHoverWindowHandle->getName().string(),
                                     hoverWindowHandle != NULL
-                                            ? hoverWindowHandle->name.string() : "<null>");
+                                            ? hoverWindowHandle->getName().string() : "<null>");
 #endif
                             goto NoBatchingOrStreaming;
                         }
@@ -3183,7 +3222,7 @@
     size_t numWindows = mWindowHandles.size();
     for (size_t i = 0; i < numWindows; i++) {
         const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
-        if (windowHandle->inputChannel == inputChannel) {
+        if (windowHandle->getInputChannel() == inputChannel) {
             return windowHandle;
         }
     }
@@ -3208,17 +3247,18 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
+        Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles;
         mWindowHandles = inputWindowHandles;
 
         sp<InputWindowHandle> newFocusedWindowHandle;
         bool foundHoveredWindow = false;
         for (size_t i = 0; i < mWindowHandles.size(); i++) {
             const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
-            if (!windowHandle->update() || windowHandle->inputChannel == NULL) {
+            if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == NULL) {
                 mWindowHandles.removeAt(i--);
                 continue;
             }
-            if (windowHandle->hasFocus) {
+            if (windowHandle->getInfo()->hasFocus) {
                 newFocusedWindowHandle = windowHandle;
             }
             if (windowHandle == mLastHoverWindowHandle) {
@@ -3234,19 +3274,20 @@
             if (mFocusedWindowHandle != NULL) {
 #if DEBUG_FOCUS
                 LOGD("Focus left window: %s",
-                        mFocusedWindowHandle->name.string());
+                        mFocusedWindowHandle->getName().string());
 #endif
-                if (mFocusedWindowHandle->inputChannel != NULL) {
+                sp<InputChannel> focusedInputChannel = mFocusedWindowHandle->getInputChannel();
+                if (focusedInputChannel != NULL) {
                     CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
                             "focus left window");
                     synthesizeCancelationEventsForInputChannelLocked(
-                            mFocusedWindowHandle->inputChannel, options);
+                            focusedInputChannel, options);
                 }
             }
             if (newFocusedWindowHandle != NULL) {
 #if DEBUG_FOCUS
                 LOGD("Focus entered window: %s",
-                        newFocusedWindowHandle->name.string());
+                        newFocusedWindowHandle->getName().string());
 #endif
             }
             mFocusedWindowHandle = newFocusedWindowHandle;
@@ -3256,17 +3297,34 @@
             TouchedWindow& touchedWindow = mTouchState.windows.editItemAt(i);
             if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
 #if DEBUG_FOCUS
-                LOGD("Touched window was removed: %s", touchedWindow.windowHandle->name.string());
+                LOGD("Touched window was removed: %s",
+                        touchedWindow.windowHandle->getName().string());
 #endif
-                if (touchedWindow.windowHandle->inputChannel != NULL) {
+                sp<InputChannel> touchedInputChannel =
+                        touchedWindow.windowHandle->getInputChannel();
+                if (touchedInputChannel != NULL) {
                     CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
                             "touched window was removed");
                     synthesizeCancelationEventsForInputChannelLocked(
-                            touchedWindow.windowHandle->inputChannel, options);
+                            touchedInputChannel, options);
                 }
                 mTouchState.windows.removeAt(i--);
             }
         }
+
+        // Release information for windows that are no longer present.
+        // This ensures that unused input channels are released promptly.
+        // Otherwise, they might stick around until the window handle is destroyed
+        // which might not happen until the next GC.
+        for (size_t i = 0; i < oldWindowHandles.size(); i++) {
+            const sp<InputWindowHandle>& oldWindowHandle = oldWindowHandles.itemAt(i);
+            if (!hasWindowHandleLocked(oldWindowHandle)) {
+#if DEBUG_FOCUS
+                LOGD("Window went away: %s", oldWindowHandle->getName().string());
+#endif
+                oldWindowHandle->releaseInfo();
+            }
+        }
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
@@ -3281,15 +3339,17 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        if (inputApplicationHandle != NULL && inputApplicationHandle->update()) {
+        if (inputApplicationHandle != NULL && inputApplicationHandle->updateInfo()) {
             if (mFocusedApplicationHandle != inputApplicationHandle) {
                 if (mFocusedApplicationHandle != NULL) {
                     resetTargetsLocked();
+                    mFocusedApplicationHandle->releaseInfo();
                 }
                 mFocusedApplicationHandle = inputApplicationHandle;
             }
         } else if (mFocusedApplicationHandle != NULL) {
             resetTargetsLocked();
+            mFocusedApplicationHandle->releaseInfo();
             mFocusedApplicationHandle.clear();
         }
 
@@ -3469,13 +3529,14 @@
 
     if (mFocusedApplicationHandle != NULL) {
         dump.appendFormat(INDENT "FocusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
-                mFocusedApplicationHandle->name.string(),
-                mFocusedApplicationHandle->dispatchingTimeout / 1000000.0);
+                mFocusedApplicationHandle->getName().string(),
+                mFocusedApplicationHandle->getDispatchingTimeout(
+                        DEFAULT_INPUT_DISPATCHING_TIMEOUT) / 1000000.0);
     } else {
         dump.append(INDENT "FocusedApplication: <null>\n");
     }
     dump.appendFormat(INDENT "FocusedWindow: name='%s'\n",
-            mFocusedWindowHandle != NULL ? mFocusedWindowHandle->name.string() : "<null>");
+            mFocusedWindowHandle != NULL ? mFocusedWindowHandle->getName().string() : "<null>");
 
     dump.appendFormat(INDENT "TouchDown: %s\n", toString(mTouchState.down));
     dump.appendFormat(INDENT "TouchSplit: %s\n", toString(mTouchState.split));
@@ -3486,7 +3547,8 @@
         for (size_t i = 0; i < mTouchState.windows.size(); i++) {
             const TouchedWindow& touchedWindow = mTouchState.windows[i];
             dump.appendFormat(INDENT2 "%d: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
-                    i, touchedWindow.windowHandle->name.string(), touchedWindow.pointerIds.value,
+                    i, touchedWindow.windowHandle->getName().string(),
+                    touchedWindow.pointerIds.value,
                     touchedWindow.targetFlags);
         }
     } else {
@@ -3497,26 +3559,28 @@
         dump.append(INDENT "Windows:\n");
         for (size_t i = 0; i < mWindowHandles.size(); i++) {
             const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
+            const InputWindowInfo* windowInfo = windowHandle->getInfo();
+
             dump.appendFormat(INDENT2 "%d: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
                     "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
                     "frame=[%d,%d][%d,%d], scale=%f, "
                     "touchableRegion=",
-                    i, windowHandle->name.string(),
-                    toString(windowHandle->paused),
-                    toString(windowHandle->hasFocus),
-                    toString(windowHandle->hasWallpaper),
-                    toString(windowHandle->visible),
-                    toString(windowHandle->canReceiveKeys),
-                    windowHandle->layoutParamsFlags, windowHandle->layoutParamsType,
-                    windowHandle->layer,
-                    windowHandle->frameLeft, windowHandle->frameTop,
-                    windowHandle->frameRight, windowHandle->frameBottom,
-                    windowHandle->scaleFactor);
-            dumpRegion(dump, windowHandle->touchableRegion);
-            dump.appendFormat(", inputFeatures=0x%08x", windowHandle->inputFeatures);
+                    i, windowInfo->name.string(),
+                    toString(windowInfo->paused),
+                    toString(windowInfo->hasFocus),
+                    toString(windowInfo->hasWallpaper),
+                    toString(windowInfo->visible),
+                    toString(windowInfo->canReceiveKeys),
+                    windowInfo->layoutParamsFlags, windowInfo->layoutParamsType,
+                    windowInfo->layer,
+                    windowInfo->frameLeft, windowInfo->frameTop,
+                    windowInfo->frameRight, windowInfo->frameBottom,
+                    windowInfo->scaleFactor);
+            dumpRegion(dump, windowInfo->touchableRegion);
+            dump.appendFormat(", inputFeatures=0x%08x", windowInfo->inputFeatures);
             dump.appendFormat(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
-                    windowHandle->ownerPid, windowHandle->ownerUid,
-                    windowHandle->dispatchingTimeout / 1000000.0);
+                    windowInfo->ownerPid, windowInfo->ownerUid,
+                    windowInfo->dispatchingTimeout / 1000000.0);
         }
     } else {
         dump.append(INDENT "Windows: <none>\n");
@@ -3572,7 +3636,7 @@
             return BAD_VALUE;
         }
 
-        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle);
+        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
         status_t status = connection->initialize();
         if (status) {
             LOGE("Failed to initialize input publisher for input channel '%s', status=%d",
@@ -3602,31 +3666,10 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
-        if (connectionIndex < 0) {
-            LOGW("Attempted to unregister already unregistered input channel '%s'",
-                    inputChannel->getName().string());
-            return BAD_VALUE;
+        status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/);
+        if (status) {
+            return status;
         }
-
-        sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
-        mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
-
-        connection->status = Connection::STATUS_ZOMBIE;
-
-        for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
-            if (mMonitoringChannels[i] == inputChannel) {
-                mMonitoringChannels.removeAt(i);
-                break;
-            }
-        }
-
-        mLooper->removeFd(inputChannel->getReceivePipeFd());
-
-        nsecs_t currentTime = now();
-        abortBrokenDispatchCycleLocked(currentTime, connection);
-
-        runCommandsLockedInterruptible();
     } // release lock
 
     // Wake the poll loop because removing the connection may have changed the current
@@ -3635,6 +3678,42 @@
     return OK;
 }
 
+status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,
+        bool notify) {
+    ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
+    if (connectionIndex < 0) {
+        LOGW("Attempted to unregister already unregistered input channel '%s'",
+                inputChannel->getName().string());
+        return BAD_VALUE;
+    }
+
+    sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+    mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
+
+    if (connection->monitor) {
+        removeMonitorChannelLocked(inputChannel);
+    }
+
+    mLooper->removeFd(inputChannel->getReceivePipeFd());
+
+    nsecs_t currentTime = now();
+    abortBrokenDispatchCycleLocked(currentTime, connection, notify);
+
+    runCommandsLockedInterruptible();
+
+    connection->status = Connection::STATUS_ZOMBIE;
+    return OK;
+}
+
+void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) {
+    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+         if (mMonitoringChannels[i] == inputChannel) {
+             mMonitoringChannels.removeAt(i);
+             break;
+         }
+    }
+}
+
 ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
     ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
     if (connectionIndex >= 0) {
@@ -3736,7 +3815,7 @@
 
     resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
             commandEntry->inputWindowHandle != NULL
-                    ? commandEntry->inputWindowHandle->inputChannel : NULL);
+                    ? commandEntry->inputWindowHandle->getInputChannel() : NULL);
 }
 
 void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
@@ -4495,8 +4574,9 @@
 // --- InputDispatcher::Connection ---
 
 InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel,
-        const sp<InputWindowHandle>& inputWindowHandle) :
+        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) :
         status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle),
+        monitor(monitor),
         inputPublisher(inputChannel),
         lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) {
 }
@@ -4627,8 +4707,9 @@
     for (size_t i = 0; i < windows.size(); i++) {
         const TouchedWindow& window = windows.itemAt(i);
         if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
-            if (haveSlipperyForegroundWindow || !(window.windowHandle->layoutParamsFlags
-                    & InputWindowHandle::FLAG_SLIPPERY)) {
+            if (haveSlipperyForegroundWindow
+                    || !(window.windowHandle->getInfo()->layoutParamsFlags
+                            & InputWindowInfo::FLAG_SLIPPERY)) {
                 return false;
             }
             haveSlipperyForegroundWindow = true;
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 3c83691..e78f7bd 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -814,6 +814,7 @@
         Status status;
         sp<InputChannel> inputChannel; // never null
         sp<InputWindowHandle> inputWindowHandle; // may be null
+        bool monitor;
         InputPublisher inputPublisher;
         InputState inputState;
         Queue<DispatchEntry> outboundQueue;
@@ -822,7 +823,7 @@
         nsecs_t lastDispatchTime; // the time when the last event was dispatched
 
         explicit Connection(const sp<InputChannel>& inputChannel,
-                const sp<InputWindowHandle>& inputWindowHandle);
+                const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
 
         inline const char* getInputChannelName() const { return inputChannel->getName().string(); }
 
@@ -868,6 +869,7 @@
     Vector<EventEntry*> mTempCancelationEvents;
 
     void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime);
+    void dispatchIdleLocked();
 
     // Batches a new sample onto a motion entry.
     // Assumes that the we have already checked that we can append samples.
@@ -1073,7 +1075,8 @@
     void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
             bool handled);
     void startNextDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
-    void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+    void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+            bool notify);
     void drainOutboundQueueLocked(Connection* connection);
     static int handleReceiveCallback(int receiveFd, int events, void* data);
 
@@ -1094,6 +1097,10 @@
     void dumpDispatchStateLocked(String8& dump);
     void logDispatchStateLocked();
 
+    // Registration.
+    void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel);
+    status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify);
+
     // Add or remove a connection to the mActiveConnections vector.
     void activateConnectionLocked(Connection* connection);
     void deactivateConnectionLocked(Connection* connection);
diff --git a/services/input/InputWindow.cpp b/services/input/InputWindow.cpp
index 0ce8867..fe61918 100644
--- a/services/input/InputWindow.cpp
+++ b/services/input/InputWindow.cpp
@@ -22,25 +22,43 @@
 
 namespace android {
 
-// --- InputWindowHandle ---
+// --- InputWindowInfo ---
 
-bool InputWindowHandle::touchableRegionContainsPoint(int32_t x, int32_t y) const {
+bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const {
     return touchableRegion.contains(x, y);
 }
 
-bool InputWindowHandle::frameContainsPoint(int32_t x, int32_t y) const {
+bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const {
     return x >= frameLeft && x <= frameRight
             && y >= frameTop && y <= frameBottom;
 }
 
-bool InputWindowHandle::isTrustedOverlay() const {
+bool InputWindowInfo::isTrustedOverlay() const {
     return layoutParamsType == TYPE_INPUT_METHOD
             || layoutParamsType == TYPE_INPUT_METHOD_DIALOG
             || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY;
 }
 
-bool InputWindowHandle::supportsSplitTouch() const {
+bool InputWindowInfo::supportsSplitTouch() const {
     return layoutParamsFlags & FLAG_SPLIT_TOUCH;
 }
 
+
+// --- InputWindowHandle ---
+
+InputWindowHandle::InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle) :
+    inputApplicationHandle(inputApplicationHandle), mInfo(NULL) {
+}
+
+InputWindowHandle::~InputWindowHandle() {
+    delete mInfo;
+}
+
+void InputWindowHandle::releaseInfo() {
+    if (mInfo) {
+        delete mInfo;
+        mInfo = NULL;
+    }
+}
+
 } // namespace android
diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h
index 272081c..8861bee 100644
--- a/services/input/InputWindow.h
+++ b/services/input/InputWindow.h
@@ -30,15 +30,9 @@
 namespace android {
 
 /*
- * A handle to a window that can receive input.
- *
- * Used by the native input dispatcher to indirectly refer to the window manager objects
- * that describe a window.
+ * Describes the properties of a window that can receive input.
  */
-class InputWindowHandle : public RefBase {
-public:
-    const sp<InputApplicationHandle> inputApplicationHandle;
-
+struct InputWindowInfo {
     // Window flags from WindowManager.LayoutParams
     enum {
         FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001,
@@ -116,7 +110,6 @@
         INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
     };
 
-    sp<InputWindowHandle> inputWindowHandle;
     sp<InputChannel> inputChannel;
     String8 name;
     int32_t layoutParamsFlags;
@@ -149,6 +142,34 @@
     bool isTrustedOverlay() const;
 
     bool supportsSplitTouch() const;
+};
+
+
+/*
+ * Handle for a window that can receive input.
+ *
+ * Used by the native input dispatcher to indirectly refer to the window manager objects
+ * that describe a window.
+ */
+class InputWindowHandle : public RefBase {
+public:
+    const sp<InputApplicationHandle> inputApplicationHandle;
+
+    inline const InputWindowInfo* getInfo() const {
+        return mInfo;
+    }
+
+    inline sp<InputChannel> getInputChannel() const {
+        return mInfo ? mInfo->inputChannel : NULL;
+    }
+
+    inline String8 getName() const {
+        return mInfo ? mInfo->name : String8("<invalid>");
+    }
+
+    inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
+        return mInfo ? mInfo->dispatchingTimeout : defaultValue;
+    }
 
     /**
      * Requests that the state of this object be updated to reflect
@@ -159,12 +180,19 @@
      *
      * Returns true on success, or false if the handle is no longer valid.
      */
-    virtual bool update() = 0;
+    virtual bool updateInfo() = 0;
+
+    /**
+     * Releases the storage used by the associated information when it is
+     * no longer needed.
+     */
+    void releaseInfo();
 
 protected:
-    InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle) :
-            inputApplicationHandle(inputApplicationHandle) { }
-    virtual ~InputWindowHandle() { }
+    InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle);
+    virtual ~InputWindowHandle();
+
+    InputWindowInfo* mInfo;
 };
 
 } // namespace android
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index b37d1c2..e622503 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -139,6 +139,9 @@
             mServerChannel.dispose();
             mClientChannel = null;
             mServerChannel = null;
+
+            mDragWindowHandle = null;
+            mDragApplicationHandle = null;
         }
     }
 
diff --git a/services/java/com/android/server/wm/InputApplicationHandle.java b/services/java/com/android/server/wm/InputApplicationHandle.java
index d78b1d9..1812f11 100644
--- a/services/java/com/android/server/wm/InputApplicationHandle.java
+++ b/services/java/com/android/server/wm/InputApplicationHandle.java
@@ -46,7 +46,10 @@
 
     @Override
     protected void finalize() throws Throwable {
-        nativeDispose();
-        super.finalize();
+        try {
+            nativeDispose();
+        } finally {
+            super.finalize();
+        }
     }
 }
diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java
index 12ef238..573a7d42 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -17,10 +17,10 @@
 package com.android.server.wm;
 
 import android.graphics.Rect;
-import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Slog;
+import android.view.InputChannel;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 
@@ -160,13 +160,21 @@
             if (WindowManagerService.DEBUG_DRAG) {
                 Log.d(WindowManagerService.TAG, "Inserting drag window");
             }
-            addInputWindowHandleLw(mService.mDragState.mDragWindowHandle);
+            final InputWindowHandle dragWindowHandle = mService.mDragState.mDragWindowHandle;
+            if (dragWindowHandle != null) {
+                addInputWindowHandleLw(dragWindowHandle);
+            } else {
+                Slog.w(WindowManagerService.TAG, "Drag is in progress but there is no "
+                        + "drag window handle.");
+            }
         }
 
         final int N = windows.size();
         for (int i = N - 1; i >= 0; i--) {
             final WindowState child = windows.get(i);
-            if (child.mInputChannel == null || child.mRemoved) {
+            final InputChannel inputChannel = child.mInputChannel;
+            final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
+            if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
                 // Skip this window because it cannot possibly receive input.
                 continue;
             }
@@ -186,8 +194,6 @@
             }
 
             // Add a window to our list of input windows.
-            final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
-            inputWindowHandle.inputChannel = child.mInputChannel;
             inputWindowHandle.name = child.toString();
             inputWindowHandle.layoutParamsFlags = flags;
             inputWindowHandle.layoutParamsType = type;
diff --git a/services/java/com/android/server/wm/InputWindowHandle.java b/services/java/com/android/server/wm/InputWindowHandle.java
index abf68d9..264877c 100644
--- a/services/java/com/android/server/wm/InputWindowHandle.java
+++ b/services/java/com/android/server/wm/InputWindowHandle.java
@@ -98,7 +98,10 @@
 
     @Override
     protected void finalize() throws Throwable {
-        nativeDispose();
-        super.finalize();
+        try {
+            nativeDispose();
+        } finally {
+            super.finalize();
+        }
     }
 }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 192d32b..8bc4f81 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -2051,10 +2051,11 @@
                 return res;
             }
             
-            if (outInputChannel != null) {
+            if (outInputChannel != null && (attrs.inputFeatures
+                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                 String name = win.makeInputChannelName();
                 InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
-                win.mInputChannel = inputChannels[0];
+                win.setInputChannel(inputChannels[0]);
                 inputChannels[1].transferTo(outInputChannel);
                 
                 mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index cdd0047..a384b03 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -1236,7 +1236,8 @@
      * Input Manager uses when discarding windows from input consideration.
      */
     boolean isPotentialDragTarget() {
-        return isVisibleNow() && (mInputChannel != null) && !mRemoved;
+        return isVisibleNow() && !mRemoved
+                && mInputChannel != null && mInputWindowHandle != null;
     }
 
     /**
@@ -1372,7 +1373,16 @@
             // we are doing this as part of processing a death note.)
         }
     }
-    
+
+    void setInputChannel(InputChannel inputChannel) {
+        if (mInputChannel != null) {
+            throw new IllegalStateException("Window already has an input channel.");
+        }
+
+        mInputChannel = inputChannel;
+        mInputWindowHandle.inputChannel = inputChannel;
+    }
+
     void disposeInputChannel() {
         if (mInputChannel != null) {
             mService.mInputManager.unregisterInputChannel(mInputChannel);
@@ -1380,6 +1390,8 @@
             mInputChannel.dispose();
             mInputChannel = null;
         }
+
+        mInputWindowHandle.inputChannel = null;
     }
 
     private class DeathRecipient implements IBinder.DeathRecipient {
diff --git a/services/jni/com_android_server_InputApplicationHandle.cpp b/services/jni/com_android_server_InputApplicationHandle.cpp
index 7de67d9..c76ab53 100644
--- a/services/jni/com_android_server_InputApplicationHandle.cpp
+++ b/services/jni/com_android_server_InputApplicationHandle.cpp
@@ -49,25 +49,30 @@
     return env->NewLocalRef(mObjWeak);
 }
 
-bool NativeInputApplicationHandle::update() {
+bool NativeInputApplicationHandle::updateInfo() {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     jobject obj = env->NewLocalRef(mObjWeak);
     if (!obj) {
+        releaseInfo();
         return false;
     }
 
+    if (!mInfo) {
+        mInfo = new InputApplicationInfo();
+    }
+
     jstring nameObj = jstring(env->GetObjectField(obj,
             gInputApplicationHandleClassInfo.name));
     if (nameObj) {
         const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
-        name.setTo(nameStr);
+        mInfo->name.setTo(nameStr);
         env->ReleaseStringUTFChars(nameObj, nameStr);
         env->DeleteLocalRef(nameObj);
     } else {
-        name.setTo("<null>");
+        mInfo->name.setTo("<null>");
     }
 
-    dispatchingTimeout = env->GetLongField(obj,
+    mInfo->dispatchingTimeout = env->GetLongField(obj,
             gInputApplicationHandleClassInfo.dispatchingTimeoutNanos);
 
     env->DeleteLocalRef(obj);
diff --git a/services/jni/com_android_server_InputApplicationHandle.h b/services/jni/com_android_server_InputApplicationHandle.h
index 04cd9d6..89d48c6 100644
--- a/services/jni/com_android_server_InputApplicationHandle.h
+++ b/services/jni/com_android_server_InputApplicationHandle.h
@@ -31,7 +31,7 @@
 
     jobject getInputApplicationHandleObjLocalRef(JNIEnv* env);
 
-    virtual bool update();
+    virtual bool updateInfo();
 
 private:
     jweak mObjWeak;
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 0a723e8..f976301 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -618,8 +618,9 @@
     size_t numWindows = windowHandles.size();
     for (size_t i = 0; i < numWindows; i++) {
         const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i);
-        if (windowHandle->hasFocus && (windowHandle->inputFeatures
-                & InputWindowHandle::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES)) {
+        const InputWindowInfo* windowInfo = windowHandle->getInfo();
+        if (windowInfo && windowInfo->hasFocus && (windowInfo->inputFeatures
+                & InputWindowInfo::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES)) {
             newPointerGesturesEnabled = false;
         }
     }
@@ -1086,8 +1087,9 @@
     status_t status = gNativeInputManager->registerInputChannel(
             env, inputChannel, inputWindowHandle, monitor);
     if (status) {
-        jniThrowRuntimeException(env, "Failed to register input channel.  "
-                "Check logs for details.");
+        String8 message;
+        message.appendFormat("Failed to register input channel.  status=%d", status);
+        jniThrowRuntimeException(env, message.string());
         return;
     }
 
@@ -1113,9 +1115,10 @@
     android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
 
     status_t status = gNativeInputManager->unregisterInputChannel(env, inputChannel);
-    if (status) {
-        jniThrowRuntimeException(env, "Failed to unregister input channel.  "
-                "Check logs for details.");
+    if (status && status != BAD_VALUE) { // ignore already unregistered channel
+        String8 message;
+        message.appendFormat("Failed to unregister input channel.  status=%d", status);
+        jniThrowRuntimeException(env, message.string());
     }
 }
 
diff --git a/services/jni/com_android_server_InputWindowHandle.cpp b/services/jni/com_android_server_InputWindowHandle.cpp
index 09be881..0607eee 100644
--- a/services/jni/com_android_server_InputWindowHandle.cpp
+++ b/services/jni/com_android_server_InputWindowHandle.cpp
@@ -74,77 +74,82 @@
     return env->NewLocalRef(mObjWeak);
 }
 
-bool NativeInputWindowHandle::update() {
+bool NativeInputWindowHandle::updateInfo() {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     jobject obj = env->NewLocalRef(mObjWeak);
     if (!obj) {
+        releaseInfo();
         return false;
     }
 
+    if (!mInfo) {
+        mInfo = new InputWindowInfo();
+    }
+
     jobject inputChannelObj = env->GetObjectField(obj,
             gInputWindowHandleClassInfo.inputChannel);
     if (inputChannelObj) {
-        inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj);
+        mInfo->inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj);
         env->DeleteLocalRef(inputChannelObj);
     } else {
-        inputChannel = NULL;
+        mInfo->inputChannel.clear();
     }
 
     jstring nameObj = jstring(env->GetObjectField(obj,
             gInputWindowHandleClassInfo.name));
     if (nameObj) {
         const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
-        name.setTo(nameStr);
+        mInfo->name.setTo(nameStr);
         env->ReleaseStringUTFChars(nameObj, nameStr);
         env->DeleteLocalRef(nameObj);
     } else {
-        name.setTo("<null>");
+        mInfo->name.setTo("<null>");
     }
 
-    layoutParamsFlags = env->GetIntField(obj,
+    mInfo->layoutParamsFlags = env->GetIntField(obj,
             gInputWindowHandleClassInfo.layoutParamsFlags);
-    layoutParamsType = env->GetIntField(obj,
+    mInfo->layoutParamsType = env->GetIntField(obj,
             gInputWindowHandleClassInfo.layoutParamsType);
-    dispatchingTimeout = env->GetLongField(obj,
+    mInfo->dispatchingTimeout = env->GetLongField(obj,
             gInputWindowHandleClassInfo.dispatchingTimeoutNanos);
-    frameLeft = env->GetIntField(obj,
+    mInfo->frameLeft = env->GetIntField(obj,
             gInputWindowHandleClassInfo.frameLeft);
-    frameTop = env->GetIntField(obj,
+    mInfo->frameTop = env->GetIntField(obj,
             gInputWindowHandleClassInfo.frameTop);
-    frameRight = env->GetIntField(obj,
+    mInfo->frameRight = env->GetIntField(obj,
             gInputWindowHandleClassInfo.frameRight);
-    frameBottom = env->GetIntField(obj,
+    mInfo->frameBottom = env->GetIntField(obj,
             gInputWindowHandleClassInfo.frameBottom);
-    scaleFactor = env->GetFloatField(obj,
+    mInfo->scaleFactor = env->GetFloatField(obj,
             gInputWindowHandleClassInfo.scaleFactor);
 
     jobject regionObj = env->GetObjectField(obj,
             gInputWindowHandleClassInfo.touchableRegion);
     if (regionObj) {
         SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj);
-        touchableRegion.set(*region);
+        mInfo->touchableRegion.set(*region);
         env->DeleteLocalRef(regionObj);
     } else {
-        touchableRegion.setEmpty();
+        mInfo->touchableRegion.setEmpty();
     }
 
-    visible = env->GetBooleanField(obj,
+    mInfo->visible = env->GetBooleanField(obj,
             gInputWindowHandleClassInfo.visible);
-    canReceiveKeys = env->GetBooleanField(obj,
+    mInfo->canReceiveKeys = env->GetBooleanField(obj,
             gInputWindowHandleClassInfo.canReceiveKeys);
-    hasFocus = env->GetBooleanField(obj,
+    mInfo->hasFocus = env->GetBooleanField(obj,
             gInputWindowHandleClassInfo.hasFocus);
-    hasWallpaper = env->GetBooleanField(obj,
+    mInfo->hasWallpaper = env->GetBooleanField(obj,
             gInputWindowHandleClassInfo.hasWallpaper);
-    paused = env->GetBooleanField(obj,
+    mInfo->paused = env->GetBooleanField(obj,
             gInputWindowHandleClassInfo.paused);
-    layer = env->GetIntField(obj,
+    mInfo->layer = env->GetIntField(obj,
             gInputWindowHandleClassInfo.layer);
-    ownerPid = env->GetIntField(obj,
+    mInfo->ownerPid = env->GetIntField(obj,
             gInputWindowHandleClassInfo.ownerPid);
-    ownerUid = env->GetIntField(obj,
+    mInfo->ownerUid = env->GetIntField(obj,
             gInputWindowHandleClassInfo.ownerUid);
-    inputFeatures = env->GetIntField(obj,
+    mInfo->inputFeatures = env->GetIntField(obj,
             gInputWindowHandleClassInfo.inputFeatures);
 
     env->DeleteLocalRef(obj);
diff --git a/services/jni/com_android_server_InputWindowHandle.h b/services/jni/com_android_server_InputWindowHandle.h
index 913c3b1..2cfa17d3 100644
--- a/services/jni/com_android_server_InputWindowHandle.h
+++ b/services/jni/com_android_server_InputWindowHandle.h
@@ -32,7 +32,7 @@
 
     jobject getInputWindowHandleObjLocalRef(JNIEnv* env);
 
-    virtual bool update();
+    virtual bool updateInfo();
 
 private:
     jweak mObjWeak;