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 },