Add support for the PointerLocation overlay.

This change involves adding a new method to IWindowManager,
monitorInput() that returns an InputChannel to receive a copy of all
input that is dispatched to applications.  The caller must have
the READ_INPUT_STATE permission to make this request (similar to
other window manager methods such as getKeycodeState).

Change-Id: Icd14d810174a5b2928671ef16de73af88302aea0
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 69b3540..a9d7342 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -180,6 +180,11 @@
         return mThread;
     }
     
+    /** @hide */
+    public MessageQueue getQueue() {
+        return mQueue;
+    }
+    
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + this);
         pw.println(prefix + "mRun=" + mRun);
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index d6b9212..e86e3bf 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -28,6 +28,7 @@
 import android.view.KeyEvent;
 import android.view.InputEvent;
 import android.view.MotionEvent;
+import android.view.InputChannel;
 
 /**
  * System private interface to the window manager.
@@ -119,6 +120,7 @@
     int getKeycodeStateForDevice(int devid, int sw);
     int getTrackballKeycodeState(int sw);
     int getDPadKeycodeState(int sw);
+    InputChannel monitorInput(String inputChannelName);
     
     // Report whether the hardware supports the given keys; returns true if successful
     boolean hasKeys(in int[] keycodes, inout boolean[] keyExists);
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 33757f0..659f9cd 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -779,11 +779,6 @@
      */
     public void enableScreenAfterBoot();
     
-    /**
-     * Called every time the window manager is dispatching a pointer event.
-     */
-    public void dispatchedPointerEventLw(MotionEvent ev, int targetX, int targetY);
-    
     public void setCurrentOrientationLw(int newOrientation);
     
     /**
diff --git a/include/utils/String8.h b/include/utils/String8.h
index 4e41410..ef0b51a 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -171,7 +171,8 @@
             status_t            append(const char* other);
             status_t            append(const char* other, size_t numChars);
 
-            status_t            appendFormat(const char* fmt, ...);
+            status_t            appendFormat(const char* fmt, ...)
+                    __attribute__((format (printf, 2, 3)));
 
             // Note that this function takes O(N) time to calculate the value.
             // No cache value is stored.
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 83d9c47..e2e6f1a 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -37,6 +37,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.LocalPowerManager;
+import android.os.Looper;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -48,15 +49,20 @@
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.telephony.ITelephony;
+import com.android.internal.view.BaseInputHandler;
 import com.android.internal.widget.PointerLocationView;
 
 import android.util.Config;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.Slog;
 import android.view.Display;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.IWindowManager;
+import android.view.InputChannel;
+import android.view.InputQueue;
+import android.view.InputHandler;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.WindowOrientationListener;
@@ -220,6 +226,17 @@
     
     int mPointerLocationMode = 0;
     PointerLocationView mPointerLocationView = null;
+    InputChannel mPointerLocationInputChannel;
+    
+    private final InputHandler mPointerLocationInputHandler = new BaseInputHandler() {
+        @Override
+        public void handleMotion(MotionEvent event, Runnable finishedCallback) {
+            finishedCallback.run();
+            synchronized (mLock) {
+                mPointerLocationView.addTouchEvent(event);
+            }
+        }
+    };
     
     // The current size of the screen.
     int mW, mH;
@@ -613,8 +630,26 @@
             WindowManagerImpl wm = (WindowManagerImpl)
                     mContext.getSystemService(Context.WINDOW_SERVICE);
             wm.addView(addView, lp);
+            
+            if (mPointerLocationInputChannel == null) {
+                try {
+                    mPointerLocationInputChannel =
+                        mWindowManager.monitorInput("PointerLocationView");
+                    InputQueue.registerInputChannel(mPointerLocationInputChannel,
+                            mPointerLocationInputHandler, mHandler.getLooper().getQueue());
+                } catch (RemoteException ex) {
+                    Slog.e(TAG, "Could not set up input monitoring channel for PointerLocation.",
+                            ex);
+                }
+            }
         }
         if (removeView != null) {
+            if (mPointerLocationInputChannel != null) {
+                InputQueue.unregisterInputChannel(mPointerLocationInputChannel);
+                mPointerLocationInputChannel.dispose();
+                mPointerLocationInputChannel = null;
+            }
+            
             WindowManagerImpl wm = (WindowManagerImpl)
                     mContext.getSystemService(Context.WINDOW_SERVICE);
             wm.removeView(removeView);
@@ -728,20 +763,6 @@
                 : Configuration.KEYBOARDHIDDEN_YES;
     }
     
-    public void dispatchedPointerEventLw(MotionEvent ev, int targetX, int targetY) {
-        if (mPointerLocationView == null) {
-            return;
-        }
-        synchronized (mLock) {
-            if (mPointerLocationView == null) {
-                return;
-            }
-            ev.offsetLocation(targetX, targetY);
-            mPointerLocationView.addTouchEvent(ev);
-            ev.offsetLocation(-targetX, -targetY);
-        }
-    }
-    
     /** {@inheritDoc} */
     public int windowTypeToLayerLw(int type) {
         if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 9195123..c2c799b 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -75,7 +75,8 @@
             int sw);
     private static native boolean nativeHasKeys(int deviceId, int sourceMask,
             int[] keyCodes, boolean[] keyExists);
-    private static native void nativeRegisterInputChannel(InputChannel inputChannel);
+    private static native void nativeRegisterInputChannel(InputChannel inputChannel,
+            boolean monitor);
     private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
     private static native int nativeInjectInputEvent(InputEvent event,
             int injectorPid, int injectorUid, int syncMode, int timeoutMillis);
@@ -225,14 +226,38 @@
         return nativeHasKeys(deviceId, sourceMask, keyCodes, keyExists);
     }
     
+    /**
+     * Creates an input channel that will receive all input from the input dispatcher.
+     * @param inputChannelName The input channel name.
+     * @return The input channel.
+     */
+    public InputChannel monitorInput(String inputChannelName) {
+        if (inputChannelName == null) {
+            throw new IllegalArgumentException("inputChannelName must not be null.");
+        }
+        
+        InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
+        nativeRegisterInputChannel(inputChannels[0], true);
+        inputChannels[0].dispose(); // don't need to retain the Java object reference
+        return inputChannels[1];
+    }
+
+    /**
+     * Registers an input channel so that it can be used as an input event target.
+     * @param inputChannel The input channel to register.
+     */
     public void registerInputChannel(InputChannel inputChannel) {
         if (inputChannel == null) {
             throw new IllegalArgumentException("inputChannel must not be null.");
         }
         
-        nativeRegisterInputChannel(inputChannel);
+        nativeRegisterInputChannel(inputChannel, false);
     }
     
+    /**
+     * Unregisters an input channel.
+     * @param inputChannel The input channel to unregister.
+     */
     public void unregisterInputChannel(InputChannel inputChannel) {
         if (inputChannel == null) {
             throw new IllegalArgumentException("inputChannel must not be null.");
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 0def5f2..11fcca8 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -4382,11 +4382,19 @@
         }
         return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_DPAD, sw);
     }
-
+    
     public boolean hasKeys(int[] keycodes, boolean[] keyExists) {
         return mInputManager.hasKeys(-1, InputDevice.SOURCE_ANY, keycodes, keyExists);
     }
 
+    public InputChannel monitorInput(String inputChannelName) {
+        if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+                "monitorInput()")) {
+            throw new SecurityException("Requires READ_INPUT_STATE permission");
+        }
+        return mInputManager.monitorInput(inputChannelName);
+    }
+
     public void enableScreenAfterBoot() {
         synchronized(mWindowMap) {
             if (mSystemBooted) {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 0982b32..ebe71ab 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -213,7 +213,7 @@
     void setDisplayOrientation(int32_t displayId, int32_t orientation);
 
     status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
-            jweak inputChannelObjWeak);
+            jweak inputChannelObjWeak, bool monitor);
     status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
 
     void setInputWindows(JNIEnv* env, jobjectArray windowObjArray);
@@ -334,6 +334,7 @@
     bool mWindowsReady;
     Vector<InputWindow> mWindows;
     Vector<InputWindow*> mWallpaperWindows;
+    Vector<sp<InputChannel> > mMonitoringChannels;
 
     // Focus tracking for keys, trackball, etc.
     InputWindow* mFocusedWindow;
@@ -382,6 +383,10 @@
     static void addTarget(const InputWindow* window, int32_t targetFlags,
             nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets);
 
+    void registerMonitoringChannel(const sp<InputChannel>& inputChannel);
+    void unregisterMonitoringChannel(const sp<InputChannel>& inputChannel);
+    void addMonitoringTargetsLd(Vector<InputTarget>& outTargets);
+
     static inline JNIEnv* jniEnv() {
         return AndroidRuntime::getJNIEnv();
     }
@@ -492,7 +497,7 @@
 }
 
 status_t NativeInputManager::registerInputChannel(JNIEnv* env,
-        const sp<InputChannel>& inputChannel, jobject inputChannelObj) {
+        const sp<InputChannel>& inputChannel, jobject inputChannelObj, bool monitor) {
     jweak inputChannelObjWeak = env->NewWeakGlobalRef(inputChannelObj);
     if (! inputChannelObjWeak) {
         LOGE("Could not create weak reference for input channel.");
@@ -519,9 +524,14 @@
 
     status = mInputManager->registerInputChannel(inputChannel);
     if (! status) {
+        // Success.
+        if (monitor) {
+            registerMonitoringChannel(inputChannel);
+        }
         return OK;
     }
 
+    // Failed!
     {
         AutoMutex _l(mInputChannelRegistryLock);
         mInputChannelObjWeakByReceiveFd.removeItem(inputChannel->getReceivePipeFd());
@@ -552,6 +562,8 @@
 
     env->DeleteWeakGlobalRef(inputChannelObjWeak);
 
+    unregisterMonitoringChannel(inputChannel);
+
     return mInputManager->unregisterInputChannel(inputChannel);
 }
 
@@ -829,6 +841,8 @@
 
         env->DeleteLocalRef(inputChannelObjLocal);
     }
+
+    unregisterMonitoringChannel(inputChannel);
 }
 
 bool NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel,
@@ -1429,7 +1443,9 @@
 
             // If there is no currently touched window then fail.
             if (! mTouchedWindow) {
-                LOGW("Dropping event because there is no touched window to receive it.");
+#if DEBUG_INPUT_DISPATCHER_POLICY
+                LOGD("Dropping event because there is no touched window to receive it.");
+#endif
                 injectionResult = INPUT_EVENT_INJECTION_FAILED;
                 injectionPermission = INJECTION_PERMISSION_GRANTED;
                 break; // failed, exit wait loop
@@ -1587,6 +1603,8 @@
             outTargets.clear();
             return INPUT_EVENT_INJECTION_SUCCEEDED;
         }
+
+        addMonitoringTargetsLd(outTargets);
     }
 
     pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
@@ -1631,6 +1649,8 @@
         }
 
         windowType = focusedWindow->layoutParamsType;
+
+        addMonitoringTargetsLd(outTargets);
     } // release lock
 
     pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
@@ -1657,6 +1677,8 @@
         }
 
         windowType = touchedWindow->layoutParamsType;
+
+        addMonitoringTargetsLd(outTargets);
     } // release lock
 
     int32_t eventType;
@@ -1714,6 +1736,39 @@
     android_server_PowerManagerService_userActivity(eventTime, eventType);
 }
 
+void NativeInputManager::registerMonitoringChannel(const sp<InputChannel>& inputChannel) {
+    { // acquire lock
+         AutoMutex _l(mDispatchLock);
+         mMonitoringChannels.push(inputChannel);
+    } // release lock
+}
+
+void NativeInputManager::unregisterMonitoringChannel(const sp<InputChannel>& inputChannel) {
+    { // acquire lock
+         AutoMutex _l(mDispatchLock);
+
+         for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+             if (mMonitoringChannels[i] == inputChannel) {
+                 mMonitoringChannels.removeAt(i);
+                 break;
+             }
+         }
+    } // release lock
+}
+
+void NativeInputManager::addMonitoringTargetsLd(Vector<InputTarget>& outTargets) {
+    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+        outTargets.push();
+
+        InputTarget& target = outTargets.editTop();
+        target.inputChannel = mMonitoringChannels[i];
+        target.flags = 0;
+        target.timeout = -1;
+        target.xOffset = 0;
+        target.yOffset = 0;
+    }
+}
+
 static void dumpMotionRange(String8& dump,
         const char* name, const InputDeviceInfo::MotionRange* range) {
     if (range) {
@@ -1805,6 +1860,11 @@
                 mWindows[i].ownerPid, mWindows[i].ownerUid,
                 mWindows[i].dispatchingTimeout / 1000000.0);
     }
+
+    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+        dump.appendFormat("  monitoringChannel[%d]: '%s'\n",
+                i, mMonitoringChannels[i]->getName().string());
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -2012,7 +2072,7 @@
 }
 
 static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
-        jobject inputChannelObj) {
+        jobject inputChannelObj, jboolean monitor) {
     if (checkInputManagerUnitialized(env)) {
         return;
     }
@@ -2026,15 +2086,17 @@
 
 
     status_t status = gNativeInputManager->registerInputChannel(
-            env, inputChannel, inputChannelObj);
+            env, inputChannel, inputChannelObj, monitor);
     if (status) {
         jniThrowRuntimeException(env, "Failed to register input channel.  "
                 "Check logs for details.");
         return;
     }
 
-    android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
-            android_server_InputManager_handleInputChannelDisposed, NULL);
+    if (! monitor) {
+        android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
+                android_server_InputManager_handleInputChannelDisposed, NULL);
+    }
 }
 
 static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,
@@ -2149,7 +2211,7 @@
             (void*) android_server_InputManager_nativeGetSwitchState },
     { "nativeHasKeys", "(II[I[Z)Z",
             (void*) android_server_InputManager_nativeHasKeys },
-    { "nativeRegisterInputChannel", "(Landroid/view/InputChannel;)V",
+    { "nativeRegisterInputChannel", "(Landroid/view/InputChannel;Z)V",
             (void*) android_server_InputManager_nativeRegisterInputChannel },
     { "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
             (void*) android_server_InputManager_nativeUnregisterInputChannel },