summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2011-12-01 14:01:49 -0800
committerJeff Brown <jeffbrown@google.com>2011-12-01 21:04:47 -0800
commit32cbc3855c2a971aa5a801fd339fb6a37db91a1a (patch)
tree40d3fcf12181eb6d50fac3a3734ecf3c9f4953ec
parentdb918cf171afd3d4b3c22aab6dd3403d1dec94de (diff)
downloadframeworks_base-32cbc3855c2a971aa5a801fd339fb6a37db91a1a.zip
frameworks_base-32cbc3855c2a971aa5a801fd339fb6a37db91a1a.tar.gz
frameworks_base-32cbc3855c2a971aa5a801fd339fb6a37db91a1a.tar.bz2
Refactor InputQueue as InputEventReceiver.
This change simplifies the code associated with receiving input events from input channels and makes it more robust. It also does a better job of ensuring that input events are properly recycled (sometimes we dropped them on the floor). This change also adds a sequence number to all events, which is handy for determining whether we are looking at the same event or a new one, particularly when events are recycled. Change-Id: I4ebd88f73b5f77f3e150778cd550e7f91956aac2
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java37
-rwxr-xr-xcore/java/android/view/InputEvent.java57
-rw-r--r--core/java/android/view/InputEventConsistencyVerifier.java11
-rw-r--r--core/java/android/view/InputEventReceiver.java151
-rw-r--r--core/java/android/view/InputHandler.java35
-rw-r--r--core/java/android/view/InputQueue.java124
-rwxr-xr-xcore/java/android/view/KeyEvent.java8
-rw-r--r--core/java/android/view/MotionEvent.java20
-rw-r--r--core/java/android/view/ViewRootImpl.java54
-rw-r--r--core/java/android/view/WindowManagerPolicy.java3
-rw-r--r--core/jni/Android.mk2
-rw-r--r--core/jni/AndroidRuntime.cpp4
-rw-r--r--core/jni/android_view_InputEventReceiver.cpp307
-rw-r--r--core/jni/android_view_InputQueue.cpp522
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java53
-rw-r--r--services/java/com/android/server/wm/DragState.java10
-rw-r--r--services/java/com/android/server/wm/FakeWindowImpl.java12
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java23
18 files changed, 643 insertions, 790 deletions
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 133549b..7ce96c0 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -45,8 +45,7 @@ import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
-import android.view.InputHandler;
-import android.view.InputQueue;
+import android.view.InputEventReceiver;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
@@ -228,24 +227,29 @@ public abstract class WallpaperService extends Service {
}
};
-
- final InputHandler mInputHandler = new InputHandler() {
+
+ final class WallpaperInputEventReceiver extends InputEventReceiver {
+ public WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
@Override
- public void handleInputEvent(InputEvent event,
- InputQueue.FinishedCallback finishedCallback) {
+ public void onInputEvent(InputEvent event) {
boolean handled = false;
try {
if (event instanceof MotionEvent
&& (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- dispatchPointer((MotionEvent)event);
+ MotionEvent dup = MotionEvent.obtainNoHistory((MotionEvent)event);
+ dispatchPointer(dup);
handled = true;
}
} finally {
- finishedCallback.finished(handled);
+ finishInputEvent(event, handled);
}
}
- };
-
+ }
+ WallpaperInputEventReceiver mInputEventReceiver;
+
final BaseIWindow mWindow = new BaseIWindow() {
@Override
public void resized(int w, int h, Rect coveredInsets,
@@ -534,6 +538,8 @@ public abstract class WallpaperService extends Service {
}
Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
mCaller.sendMessage(msg);
+ } else {
+ event.recycle();
}
}
@@ -599,8 +605,8 @@ public abstract class WallpaperService extends Service {
}
mCreated = true;
- InputQueue.registerInputChannel(mInputChannel, mInputHandler,
- Looper.myQueue());
+ mInputEventReceiver = new WallpaperInputEventReceiver(
+ mInputChannel, Looper.myLooper());
}
mSurfaceHolder.mSurfaceLock.lock();
@@ -902,8 +908,9 @@ public abstract class WallpaperService extends Service {
if (DEBUG) Log.v(TAG, "Removing window and destroying surface "
+ mSurfaceHolder.getSurface() + " of: " + this);
- if (mInputChannel != null) {
- InputQueue.unregisterInputChannel(mInputChannel);
+ if (mInputEventReceiver != null) {
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
}
mSession.remove(mWindow);
@@ -970,6 +977,8 @@ public abstract class WallpaperService extends Service {
public void dispatchPointer(MotionEvent event) {
if (mEngine != null) {
mEngine.dispatchPointer(event);
+ } else {
+ event.recycle();
}
}
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 01ddcc9..c42bbdc 100755
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -19,6 +19,8 @@ package android.view;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
* Common base class for input events.
*/
@@ -27,8 +29,21 @@ public abstract class InputEvent implements Parcelable {
protected static final int PARCEL_TOKEN_MOTION_EVENT = 1;
/** @hide */
protected static final int PARCEL_TOKEN_KEY_EVENT = 2;
-
+
+ // Next sequence number.
+ private static final AtomicInteger mNextSeq = new AtomicInteger();
+
+ /** @hide */
+ protected int mSeq;
+
+ /** @hide */
+ protected boolean mRecycled;
+
+ private static final boolean TRACK_RECYCLED_LOCATION = false;
+ private RuntimeException mRecycledLocation;
+
/*package*/ InputEvent() {
+ mSeq = mNextSeq.getAndIncrement();
}
/**
@@ -82,7 +97,29 @@ public abstract class InputEvent implements Parcelable {
* objects are fine. See {@link KeyEvent#recycle()} for details.
* @hide
*/
- public abstract void recycle();
+ public void recycle() {
+ if (TRACK_RECYCLED_LOCATION) {
+ if (mRecycledLocation != null) {
+ throw new RuntimeException(toString() + " recycled twice!", mRecycledLocation);
+ }
+ mRecycledLocation = new RuntimeException("Last recycled here");
+ } else {
+ if (mRecycled) {
+ throw new RuntimeException(toString() + " recycled twice!");
+ }
+ mRecycled = true;
+ }
+ }
+
+ /**
+ * Reinitializes the event on reuse (after recycling).
+ * @hide
+ */
+ protected void prepareForReuse() {
+ mRecycled = false;
+ mRecycledLocation = null;
+ mSeq = mNextSeq.getAndIncrement();
+ }
/**
* Gets a private flag that indicates when the system has detected that this input event
@@ -113,6 +150,22 @@ public abstract class InputEvent implements Parcelable {
*/
public abstract long getEventTimeNano();
+ /**
+ * Gets the unique sequence number of this event.
+ * Every input event that is created or received by a process has a
+ * unique sequence number. Moreover, a new sequence number is obtained
+ * each time an event object is recycled.
+ *
+ * Sequence numbers are only guaranteed to be locally unique within a process.
+ * Sequence numbers are not preserved when events are parceled.
+ *
+ * @return The unique sequence number of this event.
+ * @hide
+ */
+ public int getSequenceNumber() {
+ return mSeq;
+ }
+
public int describeContents() {
return 0;
}
diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java
index 9b081b2..fafe416 100644
--- a/core/java/android/view/InputEventConsistencyVerifier.java
+++ b/core/java/android/view/InputEventConsistencyVerifier.java
@@ -58,7 +58,7 @@ public final class InputEventConsistencyVerifier {
// so that the verifier can detect when it has been asked to verify the same event twice.
// It does not make sense to examine the contents of the last event since it may have
// been recycled.
- private InputEvent mLastEvent;
+ private int mLastEventSeq;
private String mLastEventType;
private int mLastNestingLevel;
@@ -140,7 +140,7 @@ public final class InputEventConsistencyVerifier {
* Resets the state of the input event consistency verifier.
*/
public void reset() {
- mLastEvent = null;
+ mLastEventSeq = -1;
mLastNestingLevel = 0;
mTrackballDown = false;
mTrackballUnhandled = false;
@@ -573,17 +573,18 @@ public final class InputEventConsistencyVerifier {
private boolean startEvent(InputEvent event, int nestingLevel, String eventType) {
// Ignore the event if we already checked it at a higher nesting level.
- if (event == mLastEvent && nestingLevel < mLastNestingLevel
+ final int seq = event.getSequenceNumber();
+ if (seq == mLastEventSeq && nestingLevel < mLastNestingLevel
&& eventType == mLastEventType) {
return false;
}
if (nestingLevel > 0) {
- mLastEvent = event;
+ mLastEventSeq = seq;
mLastEventType = eventType;
mLastNestingLevel = nestingLevel;
} else {
- mLastEvent = null;
+ mLastEventSeq = -1;
mLastEventType = null;
mLastNestingLevel = 0;
}
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
new file mode 100644
index 0000000..abb5281
--- /dev/null
+++ b/core/java/android/view/InputEventReceiver.java
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+package android.view;
+
+import dalvik.system.CloseGuard;
+
+import android.os.Looper;
+import android.os.MessageQueue;
+import android.util.Log;
+
+/**
+ * Provides a low-level mechanism for an application to receive input events.
+ * @hide
+ */
+public abstract class InputEventReceiver {
+ private static final String TAG = "InputEventReceiver";
+
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+
+ private int mReceiverPtr;
+
+ // We keep references to the input channel and message queue objects here so that
+ // they are not GC'd while the native peer of the receiver is using them.
+ private InputChannel mInputChannel;
+ private MessageQueue mMessageQueue;
+
+ // The sequence number of the event that is in progress.
+ private int mEventSequenceNumberInProgress = -1;
+
+ private static native int nativeInit(InputEventReceiver receiver,
+ InputChannel inputChannel, MessageQueue messageQueue);
+ private static native void nativeDispose(int receiverPtr);
+ private static native void nativeFinishInputEvent(int receiverPtr, boolean handled);
+
+ /**
+ * Creates an input event receiver bound to the specified input channel.
+ *
+ * @param inputChannel The input channel.
+ * @param looper The looper to use when invoking callbacks.
+ */
+ public InputEventReceiver(InputChannel inputChannel, Looper looper) {
+ if (inputChannel == null) {
+ throw new IllegalArgumentException("inputChannel must not be null");
+ }
+ if (looper == null) {
+ throw new IllegalArgumentException("looper must not be null");
+ }
+
+ mInputChannel = inputChannel;
+ mMessageQueue = looper.getQueue();
+ mReceiverPtr = nativeInit(this, inputChannel, mMessageQueue);
+
+ mCloseGuard.open("dispose");
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ dispose();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Disposes the receiver.
+ */
+ public void dispose() {
+ if (mCloseGuard != null) {
+ mCloseGuard.close();
+ }
+ if (mReceiverPtr != 0) {
+ nativeDispose(mReceiverPtr);
+ mReceiverPtr = 0;
+ }
+ mInputChannel = null;
+ mMessageQueue = null;
+ }
+
+ /**
+ * Called when an input event is received.
+ * The recipient should process the input event and then call {@link #finishInputEvent}
+ * to indicate whether the event was handled. No new input events will be received
+ * until {@link #finishInputEvent} is called.
+ *
+ * @param event The input event that was received.
+ */
+ public void onInputEvent(InputEvent event) {
+ finishInputEvent(event, false);
+ }
+
+ /**
+ * Finishes an input event and indicates whether it was handled.
+ *
+ * @param event The input event that was finished.
+ * @param handled True if the event was handled.
+ */
+ public void finishInputEvent(InputEvent event, boolean handled) {
+ if (event == null) {
+ throw new IllegalArgumentException("event must not be null");
+ }
+ if (mReceiverPtr == 0) {
+ Log.w(TAG, "Attempted to finish an input event but the input event "
+ + "receiver has already been disposed.");
+ } else {
+ if (event.getSequenceNumber() != mEventSequenceNumberInProgress) {
+ Log.w(TAG, "Attempted to finish an input event that is not in progress.");
+ } else {
+ mEventSequenceNumberInProgress = -1;
+ nativeFinishInputEvent(mReceiverPtr, handled);
+ }
+ }
+ recycleInputEvent(event);
+ }
+
+ // Called from native code.
+ @SuppressWarnings("unused")
+ private void dispatchInputEvent(InputEvent event) {
+ mEventSequenceNumberInProgress = event.getSequenceNumber();
+ onInputEvent(event);
+ }
+
+ private static void recycleInputEvent(InputEvent event) {
+ if (event instanceof MotionEvent) {
+ // Event though key events are also recyclable, we only recycle motion events.
+ // Historically, key events were not recyclable and applications expect
+ // them to be immutable. We only ever recycle key events behind the
+ // scenes where an application never sees them (so, not here).
+ event.recycle();
+ }
+ }
+
+ public static interface Factory {
+ public InputEventReceiver createInputEventReceiver(
+ InputChannel inputChannel, Looper looper);
+ }
+}
diff --git a/core/java/android/view/InputHandler.java b/core/java/android/view/InputHandler.java
deleted file mode 100644
index 192a427..0000000
--- a/core/java/android/view/InputHandler.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-/**
- * Handles input messages that arrive on an input channel.
- * @hide
- */
-public class InputHandler {
- /**
- * Handle an input event.
- * It is the responsibility of the callee to ensure that the finished callback is
- * eventually invoked when the event processing is finished and the input system
- * can send the next event.
- * @param event The input event.
- * @param finishedCallback The callback to invoke when event processing is finished.
- */
- public void handleInputEvent(InputEvent event, InputQueue.FinishedCallback finishedCallback) {
- finishedCallback.finished(false);
- }
-}
diff --git a/core/java/android/view/InputQueue.java b/core/java/android/view/InputQueue.java
index 1206518..909a3b2 100644
--- a/core/java/android/view/InputQueue.java
+++ b/core/java/android/view/InputQueue.java
@@ -16,18 +16,11 @@
package android.view;
-import android.os.MessageQueue;
-import android.util.Slog;
-
/**
* An input queue provides a mechanism for an application to receive incoming
* input events. Currently only usable from native code.
*/
public final class InputQueue {
- private static final String TAG = "InputQueue";
-
- private static final boolean DEBUG = false;
-
/**
* Interface to receive notification of when an InputQueue is associated
* and dissociated with a thread.
@@ -48,13 +41,6 @@ public final class InputQueue {
final InputChannel mChannel;
- private static final Object sLock = new Object();
-
- private static native void nativeRegisterInputChannel(InputChannel inputChannel,
- InputHandler inputHandler, MessageQueue messageQueue);
- private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
- private static native void nativeFinished(long finishedToken, boolean handled);
-
/** @hide */
public InputQueue(InputChannel channel) {
mChannel = channel;
@@ -64,114 +50,4 @@ public final class InputQueue {
public InputChannel getInputChannel() {
return mChannel;
}
-
- /**
- * Registers an input channel and handler.
- * @param inputChannel The input channel to register.
- * @param inputHandler The input handler to input events send to the target.
- * @param messageQueue The message queue on whose thread the handler should be invoked.
- * @hide
- */
- public static void registerInputChannel(InputChannel inputChannel, InputHandler inputHandler,
- MessageQueue messageQueue) {
- if (inputChannel == null) {
- throw new IllegalArgumentException("inputChannel must not be null");
- }
- if (inputHandler == null) {
- throw new IllegalArgumentException("inputHandler must not be null");
- }
- if (messageQueue == null) {
- throw new IllegalArgumentException("messageQueue must not be null");
- }
-
- synchronized (sLock) {
- if (DEBUG) {
- Slog.d(TAG, "Registering input channel '" + inputChannel + "'");
- }
-
- nativeRegisterInputChannel(inputChannel, inputHandler, messageQueue);
- }
- }
-
- /**
- * Unregisters an input channel.
- * Does nothing if the channel is not currently registered.
- * @param inputChannel The input channel to unregister.
- * @hide
- */
- public static void unregisterInputChannel(InputChannel inputChannel) {
- if (inputChannel == null) {
- throw new IllegalArgumentException("inputChannel must not be null");
- }
-
- synchronized (sLock) {
- if (DEBUG) {
- Slog.d(TAG, "Unregistering input channel '" + inputChannel + "'");
- }
-
- nativeUnregisterInputChannel(inputChannel);
- }
- }
-
- @SuppressWarnings("unused")
- private static void dispatchInputEvent(InputHandler inputHandler,
- InputEvent event, long finishedToken) {
- FinishedCallback finishedCallback = FinishedCallback.obtain(finishedToken);
- inputHandler.handleInputEvent(event, finishedCallback);
- }
-
- /**
- * A callback that must be invoked to when finished processing an event.
- * @hide
- */
- public static final class FinishedCallback {
- private static final boolean DEBUG_RECYCLING = false;
-
- private static final int RECYCLE_MAX_COUNT = 4;
-
- private static FinishedCallback sRecycleHead;
- private static int sRecycleCount;
-
- private FinishedCallback mRecycleNext;
- private long mFinishedToken;
-
- private FinishedCallback() {
- }
-
- public static FinishedCallback obtain(long finishedToken) {
- synchronized (sLock) {
- FinishedCallback callback = sRecycleHead;
- if (callback != null) {
- sRecycleHead = callback.mRecycleNext;
- sRecycleCount -= 1;
- callback.mRecycleNext = null;
- } else {
- callback = new FinishedCallback();
- }
- callback.mFinishedToken = finishedToken;
- return callback;
- }
- }
-
- public void finished(boolean handled) {
- synchronized (sLock) {
- if (mFinishedToken == -1) {
- throw new IllegalStateException("Event finished callback already invoked.");
- }
-
- nativeFinished(mFinishedToken, handled);
- mFinishedToken = -1;
-
- if (sRecycleCount < RECYCLE_MAX_COUNT) {
- mRecycleNext = sRecycleHead;
- sRecycleHead = this;
- sRecycleCount += 1;
-
- if (DEBUG_RECYCLING) {
- Slog.d(TAG, "Recycled finished callbacks: " + sRecycleCount);
- }
- }
- }
- }
- }
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index f53e42c..9a46aab 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1225,7 +1225,6 @@ public class KeyEvent extends InputEvent implements Parcelable {
private static KeyEvent gRecyclerTop;
private KeyEvent mNext;
- private boolean mRecycled;
private int mDeviceId;
private int mSource;
@@ -1535,8 +1534,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
gRecyclerTop = ev.mNext;
gRecyclerUsed -= 1;
}
- ev.mRecycled = false;
ev.mNext = null;
+ ev.prepareForReuse();
return ev;
}
@@ -1598,10 +1597,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
* @hide
*/
public final void recycle() {
- if (mRecycled) {
- throw new RuntimeException(toString() + " recycled twice!");
- }
- mRecycled = true;
+ super.recycle();
mCharacters = null;
synchronized (gRecyclerLock) {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 8e0ab1a..e49193e 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -167,7 +167,6 @@ import android.util.SparseArray;
*/
public final class MotionEvent extends InputEvent implements Parcelable {
private static final long NS_PER_MS = 1000000;
- private static final boolean TRACK_RECYCLED_LOCATION = false;
/**
* An invalid pointer id.
@@ -1315,8 +1314,6 @@ public final class MotionEvent extends InputEvent implements Parcelable {
private int mNativePtr;
private MotionEvent mNext;
- private RuntimeException mRecycledLocation;
- private boolean mRecycled;
private static native int nativeInitialize(int nativePtr,
int deviceId, int source, int action, int flags, int edgeFlags,
@@ -1397,9 +1394,8 @@ public final class MotionEvent extends InputEvent implements Parcelable {
gRecyclerTop = ev.mNext;
gRecyclerUsed -= 1;
}
- ev.mRecycledLocation = null;
- ev.mRecycled = false;
ev.mNext = null;
+ ev.prepareForReuse();
return ev;
}
@@ -1647,19 +1643,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* this function you must not ever touch the event again.
*/
public final void recycle() {
- // Ensure recycle is only called once!
- if (TRACK_RECYCLED_LOCATION) {
- if (mRecycledLocation != null) {
- throw new RuntimeException(toString() + " recycled twice!", mRecycledLocation);
- }
- mRecycledLocation = new RuntimeException("Last recycled here");
- //Log.w("MotionEvent", "Recycling event " + this, mRecycledLocation);
- } else {
- if (mRecycled) {
- throw new RuntimeException(toString() + " recycled twice!");
- }
- mRecycled = true;
- }
+ super.recycle();
synchronized (gRecyclerLock) {
if (gRecyclerUsed < MAX_RECYCLED) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0e65334..f23c312 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -221,7 +221,6 @@ public final class ViewRootImpl extends Handler implements ViewParent,
private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10;
private QueuedInputEvent mQueuedInputEventPool;
private int mQueuedInputEventPoolSize;
- private int mQueuedInputEventNextSeq;
// Input event queue.
QueuedInputEvent mFirstPendingInputEvent;
@@ -559,8 +558,8 @@ public final class ViewRootImpl extends Handler implements ViewParent,
mInputQueue = new InputQueue(mInputChannel);
mInputQueueCallback.onInputQueueCreated(mInputQueue);
} else {
- InputQueue.registerInputChannel(mInputChannel, mInputHandler,
- Looper.myQueue());
+ mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
+ Looper.myLooper());
}
}
@@ -2283,8 +2282,9 @@ public final class ViewRootImpl extends Handler implements ViewParent,
mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
mInputQueueCallback = null;
mInputQueue = null;
- } else if (mInputChannel != null) {
- InputQueue.unregisterInputChannel(mInputChannel);
+ } else if (mInputEventReceiver != null) {
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
}
try {
sWindowSession.remove(mWindow);
@@ -3199,9 +3199,10 @@ public final class ViewRootImpl extends Handler implements ViewParent,
if (mLastWasImTarget) {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
+ final int seq = event.getSequenceNumber();
if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
- + q.mSeq + " event=" + event);
- imm.dispatchKeyEvent(mView.getContext(), q.mSeq, event, mInputMethodCallback);
+ + seq + " event=" + event);
+ imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
return;
}
}
@@ -3213,7 +3214,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
void handleImeFinishedEvent(int seq, boolean handled) {
final QueuedInputEvent q = mCurrentInputEvent;
- if (q != null && q.mSeq == seq) {
+ if (q != null && q.mEvent.getSequenceNumber() == seq) {
final KeyEvent event = (KeyEvent)q.mEvent;
if (DEBUG_IMF) {
Log.v(TAG, "IME finished event: seq=" + seq
@@ -3715,9 +3716,8 @@ public final class ViewRootImpl extends Handler implements ViewParent,
public QueuedInputEvent mNext;
public InputEvent mEvent;
- public InputQueue.FinishedCallback mFinishedCallback;
+ public InputEventReceiver mReceiver;
public int mFlags;
- public int mSeq;
// Used for latency calculations.
public long mReceiveTimeNanos;
@@ -3726,7 +3726,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
}
private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,
- InputQueue.FinishedCallback finishedCallback, int flags) {
+ InputEventReceiver receiver, int flags) {
QueuedInputEvent q = mQueuedInputEventPool;
if (q != null) {
mQueuedInputEventPoolSize -= 1;
@@ -3737,15 +3737,14 @@ public final class ViewRootImpl extends Handler implements ViewParent,
}
q.mEvent = event;
- q.mFinishedCallback = finishedCallback;
+ q.mReceiver = receiver;
q.mFlags = flags;
- q.mSeq = mQueuedInputEventNextSeq++;
return q;
}
private void recycleQueuedInputEvent(QueuedInputEvent q) {
q.mEvent = null;
- q.mFinishedCallback = null;
+ q.mReceiver = null;
if (mQueuedInputEventPoolSize < MAX_QUEUED_INPUT_EVENT_POOL_SIZE) {
mQueuedInputEventPoolSize += 1;
@@ -3755,8 +3754,8 @@ public final class ViewRootImpl extends Handler implements ViewParent,
}
void enqueueInputEvent(InputEvent event,
- InputQueue.FinishedCallback finishedCallback, int flags) {
- QueuedInputEvent q = obtainQueuedInputEvent(event, finishedCallback, flags);
+ InputEventReceiver receiver, int flags) {
+ QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
if (ViewDebug.DEBUG_LATENCY) {
q.mReceiveTimeNanos = System.nanoTime();
@@ -3847,11 +3846,9 @@ public final class ViewRootImpl extends Handler implements ViewParent,
Log.d(ViewDebug.DEBUG_LATENCY_TAG, msg.toString());
}
- if (q.mFinishedCallback != null) {
- q.mFinishedCallback.finished(handled);
- }
-
- if (q.mEvent instanceof MotionEvent) {
+ if (q.mReceiver != null) {
+ q.mReceiver.finishInputEvent(q.mEvent, handled);
+ } else if (q.mEvent instanceof MotionEvent) {
// Event though key events are also recyclable, we only recycle motion events.
// Historically, key events were not recyclable and applications expect
// them to be immutable. We only ever recycle key events behind the
@@ -3867,12 +3864,17 @@ public final class ViewRootImpl extends Handler implements ViewParent,
}
}
- private final InputHandler mInputHandler = new InputHandler() {
- public void handleInputEvent(InputEvent event,
- InputQueue.FinishedCallback finishedCallback) {
- enqueueInputEvent(event, finishedCallback, 0);
+ final class WindowInputEventReceiver extends InputEventReceiver {
+ public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
}
- };
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ enqueueInputEvent(event, this, 0);
+ }
+ }
+ WindowInputEventReceiver mInputEventReceiver;
public void dispatchKey(KeyEvent event) {
enqueueInputEvent(event, null, 0);
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 2e19bf6..7d729c6 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -340,7 +340,8 @@ public interface WindowManagerPolicy {
* Add a fake window to the window manager. This window sits
* at the top of the other windows and consumes events.
*/
- public FakeWindow addFakeWindow(Looper looper, InputHandler inputHandler,
+ public FakeWindow addFakeWindow(Looper looper,
+ InputEventReceiver.Factory inputEventReceiverFactory,
String name, int windowType, int layoutParamsFlags, boolean canReceiveKeys,
boolean hasFocus, boolean touchFullscreen);
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 71c5d26..f20fbbb 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -50,7 +50,7 @@ LOCAL_SRC_FILES:= \
android_view_Surface.cpp \
android_view_TextureView.cpp \
android_view_InputChannel.cpp \
- android_view_InputQueue.cpp \
+ android_view_InputEventReceiver.cpp \
android_view_KeyEvent.cpp \
android_view_KeyCharacterMap.cpp \
android_view_HardwareRenderer.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 6d1410c..c6447e1 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -168,7 +168,7 @@ extern int register_android_app_backup_FullBackup(JNIEnv *env);
extern int register_android_app_ActivityThread(JNIEnv *env);
extern int register_android_app_NativeActivity(JNIEnv *env);
extern int register_android_view_InputChannel(JNIEnv* env);
-extern int register_android_view_InputQueue(JNIEnv* env);
+extern int register_android_view_InputEventReceiver(JNIEnv* env);
extern int register_android_view_KeyEvent(JNIEnv* env);
extern int register_android_view_MotionEvent(JNIEnv* env);
extern int register_android_view_PointerIcon(JNIEnv* env);
@@ -1192,7 +1192,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_app_ActivityThread),
REG_JNI(register_android_app_NativeActivity),
REG_JNI(register_android_view_InputChannel),
- REG_JNI(register_android_view_InputQueue),
+ REG_JNI(register_android_view_InputEventReceiver),
REG_JNI(register_android_view_KeyEvent),
REG_JNI(register_android_view_MotionEvent),
REG_JNI(register_android_view_PointerIcon),
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
new file mode 100644
index 0000000..9ae63dd
--- /dev/null
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -0,0 +1,307 @@
+/*
+ * 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 "InputEventReceiver"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages about the dispatch cycle.
+#define DEBUG_DISPATCH_CYCLE 0
+
+
+#include "JNIHelp.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+#include <ui/InputTransport.h>
+#include "android_os_MessageQueue.h"
+#include "android_view_InputChannel.h"
+#include "android_view_KeyEvent.h"
+#include "android_view_MotionEvent.h"
+
+namespace android {
+
+static struct {
+ jclass clazz;
+
+ jmethodID dispatchInputEvent;
+} gInputEventReceiverClassInfo;
+
+
+class NativeInputEventReceiver : public RefBase {
+public:
+ NativeInputEventReceiver(JNIEnv* env,
+ jobject receiverObj, const sp<InputChannel>& inputChannel,
+ const sp<Looper>& looper);
+
+ status_t initialize();
+ status_t finishInputEvent(bool handled);
+ static int handleReceiveCallback(int receiveFd, int events, void* data);
+
+protected:
+ virtual ~NativeInputEventReceiver();
+
+private:
+ jobject mReceiverObjGlobal;
+ InputConsumer mInputConsumer;
+ sp<Looper> mLooper;
+ bool mEventInProgress;
+ PreallocatedInputEventFactory mInputEventFactory;
+
+ const char* getInputChannelName() {
+ return mInputConsumer.getChannel()->getName().string();
+ }
+};
+
+
+NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
+ jobject receiverObj, const sp<InputChannel>& inputChannel, const sp<Looper>& looper) :
+ mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
+ mInputConsumer(inputChannel), mLooper(looper), mEventInProgress(false) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName());
+#endif
+}
+
+NativeInputEventReceiver::~NativeInputEventReceiver() {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName());
+#endif
+
+ mLooper->removeFd(mInputConsumer.getChannel()->getReceivePipeFd());
+ if (mEventInProgress) {
+ mInputConsumer.sendFinishedSignal(false); // ignoring result
+ }
+
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->DeleteGlobalRef(mReceiverObjGlobal);
+}
+
+status_t NativeInputEventReceiver::initialize() {
+ status_t result = mInputConsumer.initialize();
+ if (result) {
+ LOGW("Failed to initialize input consumer for input channel '%s', status=%d",
+ getInputChannelName(), result);
+ return result;
+ }
+
+ int32_t receiveFd = mInputConsumer.getChannel()->getReceivePipeFd();
+ mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
+ return OK;
+}
+
+status_t NativeInputEventReceiver::finishInputEvent(bool handled) {
+ if (mEventInProgress) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Finished input event.", getInputChannelName());
+#endif
+ mEventInProgress = false;
+
+ status_t status = mInputConsumer.sendFinishedSignal(handled);
+ if (status) {
+ LOGW("Failed to send finished signal on channel '%s'. status=%d",
+ getInputChannelName(), status);
+ }
+ return status;
+ } else {
+ LOGW("Ignoring attempt to finish input event while no event is in progress.");
+ return OK;
+ }
+}
+
+int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, void* data) {
+ sp<NativeInputEventReceiver> r = static_cast<NativeInputEventReceiver*>(data);
+
+ if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
+ LOGE("channel '%s' ~ Publisher closed input channel or an error occurred. "
+ "events=0x%x", r->getInputChannelName(), events);
+ return 0; // remove the callback
+ }
+
+ if (!(events & ALOOPER_EVENT_INPUT)) {
+ LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
+ "events=0x%x", r->getInputChannelName(), events);
+ return 1;
+ }
+
+ status_t status = r->mInputConsumer.receiveDispatchSignal();
+ if (status) {
+ LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d",
+ r->getInputChannelName(), status);
+ return 0; // remove the callback
+ }
+
+ if (r->mEventInProgress) {
+ LOGW("channel '%s' ~ Publisher sent spurious dispatch signal.",
+ r->getInputChannelName());
+ return 1;
+ }
+
+ InputEvent* inputEvent;
+ status = r->mInputConsumer.consume(&r->mInputEventFactory, &inputEvent);
+ if (status) {
+ LOGW("channel '%s' ~ Failed to consume input event. status=%d",
+ r->getInputChannelName(), status);
+ r->mInputConsumer.sendFinishedSignal(false);
+ return 1;
+ }
+
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jobject inputEventObj;
+ switch (inputEvent->getType()) {
+ case AINPUT_EVENT_TYPE_KEY:
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Received key event.",
+ r->getInputChannelName());
+#endif
+ inputEventObj = android_view_KeyEvent_fromNative(env,
+ static_cast<KeyEvent*>(inputEvent));
+ break;
+
+ case AINPUT_EVENT_TYPE_MOTION:
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Received motion event.",
+ r->getInputChannelName());
+#endif
+ inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
+ static_cast<MotionEvent*>(inputEvent));
+ break;
+
+ default:
+ assert(false); // InputConsumer should prevent this from ever happening
+ inputEventObj = NULL;
+ }
+
+ if (!inputEventObj) {
+ LOGW("channel '%s' ~ Failed to obtain event object.",
+ r->getInputChannelName());
+ r->mInputConsumer.sendFinishedSignal(false);
+ return 1;
+ }
+
+ r->mEventInProgress = true;
+
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Invoking input handler.", r->getInputChannelName());
+#endif
+ env->CallVoidMethod(r->mReceiverObjGlobal,
+ gInputEventReceiverClassInfo.dispatchInputEvent, inputEventObj);
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Returned from input handler.", r->getInputChannelName());
+#endif
+
+ if (env->ExceptionCheck()) {
+ LOGE("channel '%s' ~ An exception occurred while dispatching an event.",
+ r->getInputChannelName());
+ LOGE_EX(env);
+ env->ExceptionClear();
+
+ if (r->mEventInProgress) {
+ r->mInputConsumer.sendFinishedSignal(false);
+ r->mEventInProgress = false;
+ }
+ }
+
+ env->DeleteLocalRef(inputEventObj);
+ return 1;
+}
+
+
+static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
+ jobject inputChannelObj, jobject messageQueueObj) {
+ sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
+ inputChannelObj);
+ if (inputChannel == NULL) {
+ jniThrowRuntimeException(env, "InputChannel is not initialized.");
+ return 0;
+ }
+
+ sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
+ if (looper == NULL) {
+ jniThrowRuntimeException(env, "MessageQueue is not initialized.");
+ return 0;
+ }
+
+ sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
+ receiverObj, inputChannel, looper);
+ status_t status = receiver->initialize();
+ if (status) {
+ String8 message;
+ message.appendFormat("Failed to initialize input event receiver. status=%d", status);
+ jniThrowRuntimeException(env, message.string());
+ return 0;
+ }
+
+ receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
+ return reinterpret_cast<jint>(receiver.get());
+}
+
+static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) {
+ sp<NativeInputEventReceiver> receiver =
+ reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
+ receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object
+}
+
+static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr, jboolean handled) {
+ sp<NativeInputEventReceiver> receiver =
+ reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
+ status_t status = receiver->finishInputEvent(handled);
+ if (status) {
+ String8 message;
+ message.appendFormat("Failed to finish input event. status=%d", status);
+ jniThrowRuntimeException(env, message.string());
+ }
+}
+
+
+static JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeInit",
+ "(Landroid/view/InputEventReceiver;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
+ (void*)nativeInit },
+ { "nativeDispose",
+ "(I)V",
+ (void*)nativeDispose },
+ { "nativeFinishInputEvent", "(IZ)V",
+ (void*)nativeFinishInputEvent }
+};
+
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className); \
+ var = jclass(env->NewGlobalRef(var));
+
+#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+ var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find method " methodName);
+
+int register_android_view_InputEventReceiver(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "android/view/InputEventReceiver",
+ gMethods, NELEM(gMethods));
+ LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ FIND_CLASS(gInputEventReceiverClassInfo.clazz, "android/view/InputEventReceiver");
+
+ GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchInputEvent,
+ gInputEventReceiverClassInfo.clazz,
+ "dispatchInputEvent", "(Landroid/view/InputEvent;)V");
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
deleted file mode 100644
index 549f575..0000000
--- a/core/jni/android_view_InputQueue.cpp
+++ /dev/null
@@ -1,522 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "InputQueue-JNI"
-
-//#define LOG_NDEBUG 0
-
-// Log debug messages about the dispatch cycle.
-#define DEBUG_DISPATCH_CYCLE 0
-
-// Log debug messages about registrations.
-#define DEBUG_REGISTRATION 0
-
-
-#include "JNIHelp.h"
-
-#include <android_runtime/AndroidRuntime.h>
-#include <utils/Log.h>
-#include <utils/Looper.h>
-#include <utils/KeyedVector.h>
-#include <utils/threads.h>
-#include <ui/InputTransport.h>
-#include "android_os_MessageQueue.h"
-#include "android_view_InputChannel.h"
-#include "android_view_KeyEvent.h"
-#include "android_view_MotionEvent.h"
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-static struct {
- jclass clazz;
-
- jmethodID dispatchInputEvent;
-} gInputQueueClassInfo;
-
-// ----------------------------------------------------------------------------
-
-class NativeInputQueue {
-public:
- NativeInputQueue();
- ~NativeInputQueue();
-
- status_t registerInputChannel(JNIEnv* env, jobject inputChannelObj,
- jobject inputHandlerObj, jobject messageQueueObj);
-
- status_t unregisterInputChannel(JNIEnv* env, jobject inputChannelObj);
-
- status_t finished(JNIEnv* env, jlong finishedToken, bool handled, bool ignoreSpuriousFinish);
-
-private:
- class Connection : public RefBase {
- protected:
- virtual ~Connection();
-
- public:
- enum Status {
- // Everything is peachy.
- STATUS_NORMAL,
- // The input channel has been unregistered.
- STATUS_ZOMBIE
- };
-
- Connection(uint16_t id,
- const sp<InputChannel>& inputChannel, const sp<Looper>& looper);
-
- inline const char* getInputChannelName() const { return inputChannel->getName().string(); }
-
- // A unique id for this connection.
- uint16_t id;
-
- Status status;
-
- sp<InputChannel> inputChannel;
- InputConsumer inputConsumer;
- sp<Looper> looper;
- jobject inputHandlerObjGlobal;
- PreallocatedInputEventFactory inputEventFactory;
-
- // The sequence number of the current event being dispatched.
- // This is used as part of the finished token as a way to determine whether the finished
- // token is still valid before sending a finished signal back to the publisher.
- uint16_t messageSeqNum;
-
- // True if a message has been received from the publisher but not yet finished.
- bool messageInProgress;
- };
-
- Mutex mLock;
- uint16_t mNextConnectionId;
- KeyedVector<int32_t, sp<Connection> > mConnectionsByReceiveFd;
-
- ssize_t getConnectionIndex(const sp<InputChannel>& inputChannel);
-
- static void handleInputChannelDisposed(JNIEnv* env,
- jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data);
-
- static int handleReceiveCallback(int receiveFd, int events, void* data);
-
- static jlong generateFinishedToken(int32_t receiveFd,
- uint16_t connectionId, uint16_t messageSeqNum);
-
- static void parseFinishedToken(jlong finishedToken,
- int32_t* outReceiveFd, uint16_t* outConnectionId, uint16_t* outMessageIndex);
-};
-
-// ----------------------------------------------------------------------------
-
-NativeInputQueue::NativeInputQueue() :
- mNextConnectionId(0) {
-}
-
-NativeInputQueue::~NativeInputQueue() {
-}
-
-status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChannelObj,
- jobject inputHandlerObj, jobject messageQueueObj) {
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
- if (inputChannel == NULL) {
- LOGW("Input channel is not initialized.");
- return BAD_VALUE;
- }
-
-#if DEBUG_REGISTRATION
- LOGD("channel '%s' - Registered", inputChannel->getName().string());
-#endif
-
- sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
-
- { // acquire lock
- AutoMutex _l(mLock);
-
- if (getConnectionIndex(inputChannel) >= 0) {
- LOGW("Attempted to register already registered input channel '%s'",
- inputChannel->getName().string());
- return BAD_VALUE;
- }
-
- uint16_t connectionId = mNextConnectionId++;
- sp<Connection> connection = new Connection(connectionId, inputChannel, looper);
- status_t result = connection->inputConsumer.initialize();
- if (result) {
- LOGW("Failed to initialize input consumer for input channel '%s', status=%d",
- inputChannel->getName().string(), result);
- return result;
- }
-
- connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj);
-
- int32_t receiveFd = inputChannel->getReceivePipeFd();
- mConnectionsByReceiveFd.add(receiveFd, connection);
-
- looper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
- } // release lock
-
- android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
- handleInputChannelDisposed, this);
- return OK;
-}
-
-status_t NativeInputQueue::unregisterInputChannel(JNIEnv* env, jobject inputChannelObj) {
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
- if (inputChannel == NULL) {
- LOGW("Input channel is not initialized.");
- return BAD_VALUE;
- }
-
-#if DEBUG_REGISTRATION
- LOGD("channel '%s' - Unregistered", inputChannel->getName().string());
-#endif
-
- { // acquire lock
- AutoMutex _l(mLock);
-
- ssize_t connectionIndex = getConnectionIndex(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);
-
- connection->status = Connection::STATUS_ZOMBIE;
-
- connection->looper->removeFd(inputChannel->getReceivePipeFd());
-
- env->DeleteGlobalRef(connection->inputHandlerObjGlobal);
- connection->inputHandlerObjGlobal = NULL;
-
- if (connection->messageInProgress) {
- LOGI("Sending finished signal for input channel '%s' since it is being unregistered "
- "while an input message is still in progress.",
- connection->getInputChannelName());
- connection->messageInProgress = false;
- connection->inputConsumer.sendFinishedSignal(false); // ignoring result
- }
- } // release lock
-
- android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
- return OK;
-}
-
-ssize_t NativeInputQueue::getConnectionIndex(const sp<InputChannel>& inputChannel) {
- ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
- if (connectionIndex >= 0) {
- sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
- if (connection->inputChannel.get() == inputChannel.get()) {
- return connectionIndex;
- }
- }
-
- return -1;
-}
-
-status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken,
- bool handled, bool ignoreSpuriousFinish) {
- int32_t receiveFd;
- uint16_t connectionId;
- uint16_t messageSeqNum;
- parseFinishedToken(finishedToken, &receiveFd, &connectionId, &messageSeqNum);
-
- { // acquire lock
- AutoMutex _l(mLock);
-
- ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
- if (connectionIndex < 0) {
- if (! ignoreSpuriousFinish) {
- LOGI("Ignoring finish signal on channel that is no longer registered.");
- }
- return DEAD_OBJECT;
- }
-
- sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
- if (connectionId != connection->id) {
- if (! ignoreSpuriousFinish) {
- LOGI("Ignoring finish signal on channel that is no longer registered.");
- }
- return DEAD_OBJECT;
- }
-
- if (messageSeqNum != connection->messageSeqNum || ! connection->messageInProgress) {
- if (! ignoreSpuriousFinish) {
- LOGW("Attempted to finish input twice on channel '%s'. "
- "finished messageSeqNum=%d, current messageSeqNum=%d, messageInProgress=%d",
- connection->getInputChannelName(),
- messageSeqNum, connection->messageSeqNum, connection->messageInProgress);
- }
- return INVALID_OPERATION;
- }
-
- connection->messageInProgress = false;
-
- status_t status = connection->inputConsumer.sendFinishedSignal(handled);
- if (status) {
- LOGW("Failed to send finished signal on channel '%s'. status=%d",
- connection->getInputChannelName(), status);
- return status;
- }
-
-#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ Finished event.",
- connection->getInputChannelName());
-#endif
- } // release lock
-
- return OK;
-}
-
-void NativeInputQueue::handleInputChannelDisposed(JNIEnv* env,
- jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data) {
- LOGW("Input channel object '%s' was disposed without first being unregistered with "
- "the input queue!", inputChannel->getName().string());
-
- NativeInputQueue* q = static_cast<NativeInputQueue*>(data);
- q->unregisterInputChannel(env, inputChannelObj);
-}
-
-int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data) {
- NativeInputQueue* q = static_cast<NativeInputQueue*>(data);
- JNIEnv* env = AndroidRuntime::getJNIEnv();
-
- sp<Connection> connection;
- InputEvent* inputEvent;
- jobject inputHandlerObjLocal;
- jlong finishedToken;
- { // acquire lock
- AutoMutex _l(q->mLock);
-
- ssize_t connectionIndex = q->mConnectionsByReceiveFd.indexOfKey(receiveFd);
- if (connectionIndex < 0) {
- LOGE("Received spurious receive callback for unknown input channel. "
- "fd=%d, events=0x%x", receiveFd, events);
- return 0; // remove the callback
- }
-
- connection = q->mConnectionsByReceiveFd.valueAt(connectionIndex);
- if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
- LOGE("channel '%s' ~ Publisher closed input channel or an error occurred. "
- "events=0x%x", connection->getInputChannelName(), events);
- return 0; // remove the callback
- }
-
- if (! (events & ALOOPER_EVENT_INPUT)) {
- LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
- "events=0x%x", connection->getInputChannelName(), events);
- return 1;
- }
-
- status_t status = connection->inputConsumer.receiveDispatchSignal();
- if (status) {
- LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d",
- connection->getInputChannelName(), status);
- return 0; // remove the callback
- }
-
- if (connection->messageInProgress) {
- LOGW("channel '%s' ~ Publisher sent spurious dispatch signal.",
- connection->getInputChannelName());
- return 1;
- }
-
- status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent);
- if (status) {
- LOGW("channel '%s' ~ Failed to consume input event. status=%d",
- connection->getInputChannelName(), status);
- connection->inputConsumer.sendFinishedSignal(false);
- return 1;
- }
-
- connection->messageInProgress = true;
- connection->messageSeqNum += 1;
-
- finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum);
-
- inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);
- } // release lock
-
- // Invoke the handler outside of the lock.
- //
- // Note: inputEvent is stored in a field of the connection object which could potentially
- // become disposed due to the input channel being unregistered concurrently.
- // For this reason, we explicitly keep the connection object alive by holding
- // a strong pointer to it within this scope. We also grabbed a local reference to
- // the input handler object itself for the same reason.
-
- int32_t inputEventType = inputEvent->getType();
-
- jobject inputEventObj;
- switch (inputEventType) {
- case AINPUT_EVENT_TYPE_KEY:
-#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ Received key event.", connection->getInputChannelName());
-#endif
- inputEventObj = android_view_KeyEvent_fromNative(env,
- static_cast<KeyEvent*>(inputEvent));
- break;
-
- case AINPUT_EVENT_TYPE_MOTION:
-#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ Received motion event.", connection->getInputChannelName());
-#endif
- inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
- static_cast<MotionEvent*>(inputEvent));
- break;
-
- default:
- assert(false); // InputConsumer should prevent this from ever happening
- inputEventObj = NULL;
- }
-
- if (! inputEventObj) {
- LOGW("channel '%s' ~ Failed to obtain DVM event object.",
- connection->getInputChannelName());
- env->DeleteLocalRef(inputHandlerObjLocal);
- q->finished(env, finishedToken, false, false);
- return 1;
- }
-
-#if DEBUG_DISPATCH_CYCLE
- LOGD("Invoking input handler.");
-#endif
- env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
- gInputQueueClassInfo.dispatchInputEvent, inputHandlerObjLocal, inputEventObj,
- jlong(finishedToken));
-#if DEBUG_DISPATCH_CYCLE
- LOGD("Returned from input handler.");
-#endif
-
- if (env->ExceptionCheck()) {
- LOGE("An exception occurred while invoking the input handler for an event.");
- LOGE_EX(env);
- env->ExceptionClear();
-
- q->finished(env, finishedToken, false, true /*ignoreSpuriousFinish*/);
- }
-
- env->DeleteLocalRef(inputEventObj);
- env->DeleteLocalRef(inputHandlerObjLocal);
- return 1;
-}
-
-jlong NativeInputQueue::generateFinishedToken(int32_t receiveFd, uint16_t connectionId,
- uint16_t messageSeqNum) {
- return (jlong(receiveFd) << 32) | (jlong(connectionId) << 16) | jlong(messageSeqNum);
-}
-
-void NativeInputQueue::parseFinishedToken(jlong finishedToken,
- int32_t* outReceiveFd, uint16_t* outConnectionId, uint16_t* outMessageIndex) {
- *outReceiveFd = int32_t(finishedToken >> 32);
- *outConnectionId = uint16_t(finishedToken >> 16);
- *outMessageIndex = uint16_t(finishedToken);
-}
-
-// ----------------------------------------------------------------------------
-
-NativeInputQueue::Connection::Connection(uint16_t id,
- const sp<InputChannel>& inputChannel, const sp<Looper>& looper) :
- id(id), status(STATUS_NORMAL), inputChannel(inputChannel), inputConsumer(inputChannel),
- looper(looper), inputHandlerObjGlobal(NULL),
- messageSeqNum(0), messageInProgress(false) {
-}
-
-NativeInputQueue::Connection::~Connection() {
-}
-
-// ----------------------------------------------------------------------------
-
-static NativeInputQueue gNativeInputQueue;
-
-static void android_view_InputQueue_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
- jobject inputChannelObj, jobject inputHandlerObj, jobject messageQueueObj) {
- status_t status = gNativeInputQueue.registerInputChannel(
- env, inputChannelObj, inputHandlerObj, messageQueueObj);
-
- if (status) {
- String8 message;
- message.appendFormat("Failed to register input channel. status=%d", status);
- jniThrowRuntimeException(env, message.string());
- }
-}
-
-static void android_view_InputQueue_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,
- jobject inputChannelObj) {
- status_t status = gNativeInputQueue.unregisterInputChannel(env, inputChannelObj);
-
- if (status) {
- String8 message;
- message.appendFormat("Failed to unregister input channel. status=%d", status);
- jniThrowRuntimeException(env, message.string());
- }
-}
-
-static void android_view_InputQueue_nativeFinished(JNIEnv* env, jclass clazz,
- jlong finishedToken, bool handled) {
- status_t status = gNativeInputQueue.finished(
- env, finishedToken, handled, false /*ignoreSpuriousFinish*/);
-
- // We ignore the case where an event could not be finished because the input channel
- // 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) {
- String8 message;
- message.appendFormat("Failed to finish input event. status=%d", status);
- jniThrowRuntimeException(env, message.string());
- }
-}
-
-// ----------------------------------------------------------------------------
-
-static JNINativeMethod gInputQueueMethods[] = {
- /* name, signature, funcPtr */
- { "nativeRegisterInputChannel",
- "(Landroid/view/InputChannel;Landroid/view/InputHandler;Landroid/os/MessageQueue;)V",
- (void*)android_view_InputQueue_nativeRegisterInputChannel },
- { "nativeUnregisterInputChannel",
- "(Landroid/view/InputChannel;)V",
- (void*)android_view_InputQueue_nativeUnregisterInputChannel },
- { "nativeFinished", "(JZ)V",
- (void*)android_view_InputQueue_nativeFinished }
-};
-
-#define FIND_CLASS(var, className) \
- var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
-
-#define GET_STATIC_METHOD_ID(var, clazz, methodName, methodDescriptor) \
- var = env->GetStaticMethodID(clazz, methodName, methodDescriptor); \
- LOG_FATAL_IF(! var, "Unable to find static method " methodName);
-
-int register_android_view_InputQueue(JNIEnv* env) {
- int res = jniRegisterNativeMethods(env, "android/view/InputQueue",
- gInputQueueMethods, NELEM(gInputQueueMethods));
- LOG_FATAL_IF(res < 0, "Unable to register native methods.");
-
- FIND_CLASS(gInputQueueClassInfo.clazz, "android/view/InputQueue");
-
- GET_STATIC_METHOD_ID(gInputQueueClassInfo.dispatchInputEvent, gInputQueueClassInfo.clazz,
- "dispatchInputEvent",
- "(Landroid/view/InputHandler;Landroid/view/InputEvent;J)V");
- return 0;
-}
-
-} // namespace android
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 08cef01..e6b86fc 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -45,6 +45,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.LocalPowerManager;
+import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
@@ -75,8 +76,7 @@ import android.view.IWindowManager;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
-import android.view.InputQueue;
-import android.view.InputHandler;
+import android.view.InputEventReceiver;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -345,10 +345,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
WindowState mFocusedWindow;
IApplicationToken mFocusedApp;
- private final InputHandler mPointerLocationInputHandler = new InputHandler() {
+ final class PointerLocationInputEventReceiver extends InputEventReceiver {
+ public PointerLocationInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
@Override
- public void handleInputEvent(InputEvent event,
- InputQueue.FinishedCallback finishedCallback) {
+ public void onInputEvent(InputEvent event) {
boolean handled = false;
try {
if (event instanceof MotionEvent
@@ -362,11 +365,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
} finally {
- finishedCallback.finished(handled);
+ finishInputEvent(event, handled);
}
}
- };
-
+ }
+ PointerLocationInputEventReceiver mPointerLocationInputEventReceiver;
+
// The current size of the screen; really; (ir)regardless of whether the status
// bar can be hidden or not
int mUnrestrictedScreenLeft, mUnrestrictedScreenTop;
@@ -1003,9 +1007,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mPointerLocationInputChannel == null) {
try {
mPointerLocationInputChannel =
- mWindowManager.monitorInput("PointerLocationView");
- InputQueue.registerInputChannel(mPointerLocationInputChannel,
- mPointerLocationInputHandler, mHandler.getLooper().getQueue());
+ mWindowManager.monitorInput("PointerLocationView");
+ mPointerLocationInputEventReceiver =
+ new PointerLocationInputEventReceiver(
+ mPointerLocationInputChannel, mHandler.getLooper());
} catch (RemoteException ex) {
Slog.e(TAG, "Could not set up input monitoring channel for PointerLocation.",
ex);
@@ -1013,8 +1018,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
if (removeView != null) {
+ if (mPointerLocationInputEventReceiver != null) {
+ mPointerLocationInputEventReceiver.dispose();
+ mPointerLocationInputEventReceiver = null;
+ }
if (mPointerLocationInputChannel != null) {
- InputQueue.unregisterInputChannel(mPointerLocationInputChannel);
mPointerLocationInputChannel.dispose();
mPointerLocationInputChannel = null;
}
@@ -1839,10 +1847,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
* to determine when the nav bar should be shown and prevent applications from
* receiving those touches.
*/
- final InputHandler mHideNavInputHandler = new InputHandler() {
+ final class HideNavInputEventReceiver extends InputEventReceiver {
+ public HideNavInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
@Override
- public void handleInputEvent(InputEvent event,
- InputQueue.FinishedCallback finishedCallback) {
+ public void onInputEvent(InputEvent event) {
boolean handled = false;
try {
if (event instanceof MotionEvent
@@ -1885,9 +1896,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
} finally {
- finishedCallback.finished(handled);
+ finishInputEvent(event, handled);
}
}
+ }
+ final InputEventReceiver.Factory mHideNavInputEventReceiverFactory =
+ new InputEventReceiver.Factory() {
+ @Override
+ public InputEventReceiver createInputEventReceiver(
+ InputChannel inputChannel, Looper looper) {
+ return new HideNavInputEventReceiver(inputChannel, looper);
+ }
};
@Override
@@ -1951,7 +1970,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
} else if (mHideNavFakeWindow == null) {
mHideNavFakeWindow = mWindowManagerFuncs.addFakeWindow(
- mHandler.getLooper(), mHideNavInputHandler,
+ mHandler.getLooper(), mHideNavInputEventReceiverFactory,
"hidden nav", WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER,
0, false, false, true);
}
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index 73cd64e..a19035a 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import com.android.server.wm.WindowManagerService.DragInputEventReceiver;
import com.android.server.wm.WindowManagerService.H;
import android.content.ClipData;
@@ -28,7 +29,6 @@ import android.os.RemoteException;
import android.util.Slog;
import android.view.DragEvent;
import android.view.InputChannel;
-import android.view.InputQueue;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
@@ -50,6 +50,7 @@ class DragState {
float mCurrentX, mCurrentY;
float mThumbOffsetX, mThumbOffsetY;
InputChannel mServerChannel, mClientChannel;
+ DragInputEventReceiver mInputEventReceiver;
InputApplicationHandle mDragApplicationHandle;
InputWindowHandle mDragWindowHandle;
WindowState mTargetWindow;
@@ -90,8 +91,8 @@ class DragState {
mServerChannel = channels[0];
mClientChannel = channels[1];
mService.mInputManager.registerInputChannel(mServerChannel, null);
- InputQueue.registerInputChannel(mClientChannel, mService.mDragInputHandler,
- mService.mH.getLooper().getQueue());
+ mInputEventReceiver = mService.new DragInputEventReceiver(mClientChannel,
+ mService.mH.getLooper());
mDragApplicationHandle = new InputApplicationHandle(null);
mDragApplicationHandle.name = "drag";
@@ -139,7 +140,8 @@ class DragState {
Slog.e(WindowManagerService.TAG, "Unregister of nonexistent drag input channel");
} else {
mService.mInputManager.unregisterInputChannel(mServerChannel);
- InputQueue.unregisterInputChannel(mClientChannel);
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
mClientChannel.dispose();
mServerChannel.dispose();
mClientChannel = null;
diff --git a/services/java/com/android/server/wm/FakeWindowImpl.java b/services/java/com/android/server/wm/FakeWindowImpl.java
index 0e72f7d..121ce18 100644
--- a/services/java/com/android/server/wm/FakeWindowImpl.java
+++ b/services/java/com/android/server/wm/FakeWindowImpl.java
@@ -20,7 +20,7 @@ import android.os.Looper;
import android.os.Process;
import android.util.Slog;
import android.view.InputChannel;
-import android.view.InputHandler;
+import android.view.InputEventReceiver;
import android.view.InputQueue;
import android.view.WindowManagerPolicy;
@@ -29,11 +29,13 @@ public final class FakeWindowImpl implements WindowManagerPolicy.FakeWindow {
final InputChannel mServerChannel, mClientChannel;
final InputApplicationHandle mApplicationHandle;
final InputWindowHandle mWindowHandle;
+ final InputEventReceiver mInputEventReceiver;
final int mWindowLayer;
boolean mTouchFullscreen;
- public FakeWindowImpl(WindowManagerService service, Looper looper, InputHandler inputHandler,
+ public FakeWindowImpl(WindowManagerService service,
+ Looper looper, InputEventReceiver.Factory inputEventReceiverFactory,
String name, int windowType, int layoutParamsFlags, boolean canReceiveKeys,
boolean hasFocus, boolean touchFullscreen) {
mService = service;
@@ -42,7 +44,9 @@ public final class FakeWindowImpl implements WindowManagerPolicy.FakeWindow {
mServerChannel = channels[0];
mClientChannel = channels[1];
mService.mInputManager.registerInputChannel(mServerChannel, null);
- InputQueue.registerInputChannel(mClientChannel, inputHandler, looper.getQueue());
+
+ mInputEventReceiver = inputEventReceiverFactory.createInputEventReceiver(
+ mClientChannel, looper);
mApplicationHandle = new InputApplicationHandle(null);
mApplicationHandle.name = name;
@@ -87,8 +91,8 @@ public final class FakeWindowImpl implements WindowManagerPolicy.FakeWindow {
public void dismiss() {
synchronized (mService.mWindowMap) {
if (mService.removeFakeWindowLocked(this)) {
+ mInputEventReceiver.dispose();
mService.mInputManager.unregisterInputChannel(mServerChannel);
- InputQueue.unregisterInputChannel(mClientChannel);
mClientChannel.dispose();
mServerChannel.dispose();
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index d7dcacb..75ace4f 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -106,8 +106,7 @@ import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
-import android.view.InputHandler;
-import android.view.InputQueue;
+import android.view.InputEventReceiver;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
@@ -570,10 +569,14 @@ public class WindowManagerService extends IWindowManager.Stub
boolean mTurnOnScreen;
DragState mDragState = null;
- final InputHandler mDragInputHandler = new InputHandler() {
+
+ final class DragInputEventReceiver extends InputEventReceiver {
+ public DragInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
@Override
- public void handleInputEvent(InputEvent event,
- InputQueue.FinishedCallback finishedCallback) {
+ public void onInputEvent(InputEvent event) {
boolean handled = false;
try {
if (event instanceof MotionEvent
@@ -625,10 +628,10 @@ public class WindowManagerService extends IWindowManager.Stub
} catch (Exception e) {
Slog.e(TAG, "Exception caught by drag handleMotion", e);
} finally {
- finishedCallback.finished(handled);
+ finishInputEvent(event, handled);
}
}
- };
+ }
/**
* Whether the UI is currently running in touch mode (not showing
@@ -9380,11 +9383,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public FakeWindow addFakeWindow(Looper looper, InputHandler inputHandler,
+ public FakeWindow addFakeWindow(Looper looper,
+ InputEventReceiver.Factory inputEventReceiverFactory,
String name, int windowType, int layoutParamsFlags, boolean canReceiveKeys,
boolean hasFocus, boolean touchFullscreen) {
synchronized (mWindowMap) {
- FakeWindowImpl fw = new FakeWindowImpl(this, looper, inputHandler, name, windowType,
+ FakeWindowImpl fw = new FakeWindowImpl(this, looper, inputEventReceiverFactory,
+ name, windowType,
layoutParamsFlags, canReceiveKeys, hasFocus, touchFullscreen);
int i=0;
while (i<mFakeWindows.size()) {